diff --git a/.devcontainer/server/container-compose-overrides.yml b/.devcontainer/server/container-compose-overrides.yml index db20390255..c5db2a6b32 100644 --- a/.devcontainer/server/container-compose-overrides.yml +++ b/.devcontainer/server/container-compose-overrides.yml @@ -15,7 +15,7 @@ services: volumes: - ${UPLOAD_LOCATION:-upload-devcontainer-volume}${UPLOAD_LOCATION:+/photos}:/data - /etc/localtime:/etc/localtime:ro - - pnpm_store_server:/buildcache/pnpm-store + - build_cache:/buildcache - ../packages/plugin-core:/build/plugins/immich-plugin-core immich-web: env_file: !reset [] diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index acbb7c785b..0000000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1 +0,0 @@ -custom: ['https://buy.immich.app', 'https://immich.store'] diff --git a/.github/workflows/build-mobile.yml b/.github/workflows/build-mobile.yml index 1f3b99ef38..2f1446c6e5 100644 --- a/.github/workflows/build-mobile.yml +++ b/.github/workflows/build-mobile.yml @@ -51,7 +51,7 @@ jobs: should_run: ${{ steps.check.outputs.should_run }} steps: - id: token - uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0 + uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1 with: client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} @@ -79,7 +79,7 @@ jobs: steps: - id: token - uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0 + uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1 with: client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} @@ -201,7 +201,7 @@ jobs: steps: - id: token - uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0 + uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1 with: client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} diff --git a/.github/workflows/cache-cleanup.yml b/.github/workflows/cache-cleanup.yml index 4ecd758f6d..aa801d851a 100644 --- a/.github/workflows/cache-cleanup.yml +++ b/.github/workflows/cache-cleanup.yml @@ -19,7 +19,7 @@ jobs: actions: write steps: - id: token - uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0 + uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1 with: client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} diff --git a/.github/workflows/check-openapi.yml b/.github/workflows/check-openapi.yml index 0ce156a0bc..f2b3e3c248 100644 --- a/.github/workflows/check-openapi.yml +++ b/.github/workflows/check-openapi.yml @@ -24,7 +24,7 @@ jobs: persist-credentials: false - name: Check for breaking API changes - uses: oasdiff/oasdiff-action/breaking@6147a58e5d1249a12f42fc864ab791d571a30015 # v0.0.47 + uses: oasdiff/oasdiff-action/breaking@50e6a3413e5aa9c3ae4d8393c34745be44288b46 # v0.0.48 with: base: https://raw.githubusercontent.com/${{ github.repository }}/main/open-api/immich-openapi-specs.json revision: open-api/immich-openapi-specs.json diff --git a/.github/workflows/cli.yml b/.github/workflows/cli.yml index 42adf5c72a..3a17dbb579 100644 --- a/.github/workflows/cli.yml +++ b/.github/workflows/cli.yml @@ -31,7 +31,7 @@ jobs: working-directory: ./packages/cli steps: - id: token - uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0 + uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1 with: client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} @@ -49,7 +49,9 @@ jobs: - name: Publish if: ${{ github.event_name == 'release' }} - run: mise run ci-publish + env: + NPM_TAG: ${{ github.event.release.prerelease && 'rc' || 'latest' }} + run: mise run ci-publish -- --tag "$NPM_TAG" docker: name: Docker @@ -61,7 +63,7 @@ jobs: steps: - id: token - uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0 + uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1 with: client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} @@ -73,13 +75,13 @@ jobs: token: ${{ steps.token.outputs.token }} - name: Set up QEMU - uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0 + uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 + uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 - name: Login to GitHub Container Registry - uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 + uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 if: ${{ !github.event.pull_request.head.repo.fork }} with: registry: ghcr.io @@ -94,7 +96,7 @@ jobs: - name: Generate docker image tags id: metadata - uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0 + uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6.1.0 with: flavor: | latest=false @@ -102,10 +104,10 @@ jobs: name=ghcr.io/${{ github.repository_owner }}/immich-cli tags: | type=raw,value=${{ steps.package-version.outputs.version }},enable=${{ github.event_name == 'release' }} - type=raw,value=latest,enable=${{ github.event_name == 'release' }} + type=raw,value=latest,enable=${{ github.event_name == 'release' && !github.event.release.prerelease }} - name: Build and push image - uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0 + uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0 with: file: packages/cli/Dockerfile platforms: linux/amd64,linux/arm64 diff --git a/.github/workflows/close-duplicates.yml b/.github/workflows/close-duplicates.yml index b0b5258048..590256058d 100644 --- a/.github/workflows/close-duplicates.yml +++ b/.github/workflows/close-duplicates.yml @@ -35,7 +35,7 @@ jobs: needs: [get_body, should_run] if: ${{ needs.should_run.outputs.should_run == 'true' }} container: - image: ghcr.io/immich-app/mdq:main@sha256:0a8b8867773a0f8368061f47578603f438349f8f1f28b0e16105f481e5c794e0 + image: ghcr.io/immich-app/mdq:main@sha256:e73f60195b39748c4876f23e3e6cd22a68a9754acec8aef1fd6979fd52cd2c9f outputs: checked: ${{ steps.get_checkbox.outputs.checked }} steps: diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index cebd9b1747..d10fd72335 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -44,7 +44,7 @@ jobs: steps: - id: token - uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0 + uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1 with: client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} @@ -57,7 +57,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@9e0d7b8d25671d64c341c19c0152d693099fb5ba # v4.35.5 + uses: github/codeql-action/init@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -70,7 +70,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@9e0d7b8d25671d64c341c19c0152d693099fb5ba # v4.35.5 + uses: github/codeql-action/autobuild@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0 # ℹ️ 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 @@ -83,6 +83,6 @@ jobs: # ./location_of_script_within_repo/buildscript.sh - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@9e0d7b8d25671d64c341c19c0152d693099fb5ba # v4.35.5 + uses: github/codeql-action/analyze@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0 with: category: '/language:${{matrix.language}}' diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 8e16894b49..133950fb0a 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -23,7 +23,7 @@ jobs: should_run: ${{ steps.check.outputs.should_run }} steps: - id: token - uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0 + uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1 with: client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} @@ -60,7 +60,7 @@ jobs: suffix: ['', '-cuda', '-rocm', '-openvino', '-armnn', '-rknn'] steps: - name: Login to GitHub Container Registry - uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 + uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 with: registry: ghcr.io username: ${{ github.repository_owner }} @@ -90,7 +90,7 @@ jobs: suffix: [''] steps: - name: Login to GitHub Container Registry - uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 + uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 with: registry: ghcr.io username: ${{ github.repository_owner }} @@ -132,7 +132,7 @@ jobs: suffixes: '-rocm' platforms: linux/amd64 runner-mapping: '{"linux/amd64": "pokedex-large"}' - uses: immich-app/devtools/.github/workflows/multi-runner-build.yml@5813c7c4f7016c748ae7ac5d5f684846649d4d20 # multi-runner-build-workflow-v2.4.0 + uses: immich-app/devtools/.github/workflows/multi-runner-build.yml@db54dcf16fbb12c43479a23749ceea0ad1b4a704 # multi-runner-build-workflow-v3.0.0 permissions: contents: read actions: read @@ -147,7 +147,7 @@ jobs: platforms: ${{ matrix.platforms }} runner-mapping: ${{ matrix.runner-mapping }} suffixes: ${{ matrix.suffixes }} - dockerhub-push: ${{ github.event_name == 'release' }} + dockerhub-push: ${{ github.event_name == 'release' && !github.event.release.prerelease }} build-args: | DEVICE=${{ matrix.device }} @@ -155,7 +155,7 @@ jobs: name: Build and Push Server needs: pre-job if: ${{ fromJSON(needs.pre-job.outputs.should_run).server == true }} - uses: immich-app/devtools/.github/workflows/multi-runner-build.yml@5813c7c4f7016c748ae7ac5d5f684846649d4d20 # multi-runner-build-workflow-v2.4.0 + uses: immich-app/devtools/.github/workflows/multi-runner-build.yml@db54dcf16fbb12c43479a23749ceea0ad1b4a704 # multi-runner-build-workflow-v3.0.0 permissions: contents: read actions: read @@ -167,7 +167,7 @@ jobs: image: immich-server context: . dockerfile: server/Dockerfile - dockerhub-push: ${{ github.event_name == 'release' }} + dockerhub-push: ${{ github.event_name == 'release' && !github.event.release.prerelease }} build-args: | DEVICE=cpu diff --git a/.github/workflows/docs-build.yml b/.github/workflows/docs-build.yml index 23f16c4c47..70ea4cb9e4 100644 --- a/.github/workflows/docs-build.yml +++ b/.github/workflows/docs-build.yml @@ -21,7 +21,7 @@ jobs: should_run: ${{ steps.check.outputs.should_run }} steps: - id: token - uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0 + uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1 with: client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} @@ -54,7 +54,7 @@ jobs: steps: - id: token - uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0 + uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1 with: client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} diff --git a/.github/workflows/docs-deploy.yml b/.github/workflows/docs-deploy.yml index 3b789e810d..dd6ba4256e 100644 --- a/.github/workflows/docs-deploy.yml +++ b/.github/workflows/docs-deploy.yml @@ -20,7 +20,7 @@ jobs: artifact: ${{ steps.get-artifact.outputs.result }} steps: - id: token - uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0 + uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1 with: client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} @@ -98,9 +98,16 @@ jobs: shouldDeploy: true }; } else if (eventType == "release") { + const tag = context.payload.workflow_run.head_branch; + const { data: release } = await github.rest.repos.getReleaseByTag({ + owner: context.repo.owner, + repo: context.repo.repo, + tag, + }); parameters = { event: "release", - name: context.payload.workflow_run.head_branch, + name: tag, + prerelease: release.prerelease, shouldDeploy: !isFork }; } @@ -119,7 +126,7 @@ jobs: if: ${{ fromJson(needs.checks.outputs.artifact).found && fromJson(needs.checks.outputs.parameters).shouldDeploy }} steps: - id: token - uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0 + uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1 with: client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} @@ -146,6 +153,7 @@ jobs: const parameters = JSON.parse(process.env.PARAM_JSON); core.setOutput("event", parameters.event); core.setOutput("name", parameters.name); + core.setOutput("prerelease", parameters.prerelease); core.setOutput("shouldDeploy", parameters.shouldDeploy); - name: Download artifact @@ -203,7 +211,7 @@ jobs: run: mise run //docs:deploy - name: Deploy Docs Release Domain - if: ${{ steps.parameters.outputs.event == 'release' }} + if: ${{ steps.parameters.outputs.event == 'release' && steps.parameters.outputs.prerelease != 'true' }} env: TF_VAR_prefix_name: ${{ steps.parameters.outputs.name}} CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} diff --git a/.github/workflows/docs-destroy.yml b/.github/workflows/docs-destroy.yml index b1d75f241c..19554b5d20 100644 --- a/.github/workflows/docs-destroy.yml +++ b/.github/workflows/docs-destroy.yml @@ -17,7 +17,7 @@ jobs: pull-requests: write steps: - id: token - uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0 + uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1 with: client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} diff --git a/.github/workflows/fix-format.yml b/.github/workflows/fix-format.yml index 23f23c1f4c..6e48e89fd7 100644 --- a/.github/workflows/fix-format.yml +++ b/.github/workflows/fix-format.yml @@ -15,7 +15,7 @@ jobs: pull-requests: write steps: - id: token - uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0 + uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1 with: client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} diff --git a/.github/workflows/pr-label-validation.yml b/.github/workflows/pr-label-validation.yml index f5c1802bbc..d99ce46d29 100644 --- a/.github/workflows/pr-label-validation.yml +++ b/.github/workflows/pr-label-validation.yml @@ -14,7 +14,7 @@ jobs: pull-requests: write steps: - id: token - uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0 + uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1 with: client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} diff --git a/.github/workflows/pr-labeler.yml b/.github/workflows/pr-labeler.yml index 4df27e581e..3d1646a88d 100644 --- a/.github/workflows/pr-labeler.yml +++ b/.github/workflows/pr-labeler.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest steps: - id: token - uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0 + uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1 with: client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml index 4f6a7dc75b..6a7c2c9cf4 100644 --- a/.github/workflows/prepare-release.yml +++ b/.github/workflows/prepare-release.yml @@ -49,7 +49,7 @@ jobs: permissions: {} # No job-level permissions are needed because it uses the app-token steps: - id: token - uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0 + uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1 with: client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} @@ -137,7 +137,7 @@ jobs: github-token: ${{ steps.generate-token.outputs.token }} - name: Create draft release - uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2.6.2 + uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0 with: draft: true tag_name: ${{ needs.bump_version.outputs.version }} diff --git a/.github/workflows/preview-label.yaml b/.github/workflows/preview-label.yaml index 94fb9f797c..736507fee9 100644 --- a/.github/workflows/preview-label.yaml +++ b/.github/workflows/preview-label.yaml @@ -14,7 +14,7 @@ jobs: pull-requests: write steps: - id: token - uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0 + uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1 with: client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} @@ -32,7 +32,7 @@ jobs: pull-requests: write steps: - id: token - uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0 + uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1 with: client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} diff --git a/.github/workflows/sdk.yml b/.github/workflows/sdk.yml index ce632fafb7..4bff026205 100644 --- a/.github/workflows/sdk.yml +++ b/.github/workflows/sdk.yml @@ -16,7 +16,7 @@ jobs: packages: write steps: - id: token - uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0 + uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1 with: client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} @@ -39,4 +39,6 @@ jobs: run: pnpm --filter @immich/sdk build - name: Publish - run: pnpm --filter @immich/sdk publish --provenance --no-git-checks + env: + NPM_TAG: ${{ github.event.release.prerelease && 'rc' || 'latest' }} + run: pnpm --filter @immich/sdk publish --provenance --no-git-checks --tag "$NPM_TAG" diff --git a/.github/workflows/static_analysis.yml b/.github/workflows/static_analysis.yml index d347317793..872d0c08c7 100644 --- a/.github/workflows/static_analysis.yml +++ b/.github/workflows/static_analysis.yml @@ -20,7 +20,7 @@ jobs: should_run: ${{ steps.check.outputs.should_run }} steps: - id: token - uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0 + uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1 with: client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} @@ -49,7 +49,7 @@ jobs: working-directory: ./mobile steps: - id: token - uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0 + uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1 with: client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} @@ -72,10 +72,6 @@ jobs: run: flutter pub get working-directory: ./mobile/packages/ui - - name: Install dependencies for UI Showcase - run: flutter pub get - working-directory: ./mobile/packages/ui/showcase - - name: Generate translation files run: mise //mobile:codegen:translation diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e16e6f059d..fdaee15a59 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,7 +17,7 @@ jobs: should_run: ${{ steps.check.outputs.should_run }} steps: - id: token - uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0 + uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1 with: client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} @@ -71,7 +71,7 @@ jobs: contents: read steps: - id: token - uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0 + uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1 with: client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} @@ -102,7 +102,7 @@ jobs: working-directory: ./packages/cli steps: - id: token - uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0 + uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1 with: client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} @@ -133,7 +133,7 @@ jobs: working-directory: ./packages/cli steps: - id: token - uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0 + uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1 with: client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} @@ -177,7 +177,7 @@ jobs: working-directory: ./web steps: - id: token - uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0 + uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1 with: client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} @@ -215,7 +215,7 @@ jobs: working-directory: ./web steps: - id: token - uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0 + uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1 with: client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} @@ -243,7 +243,7 @@ jobs: contents: read steps: - id: token - uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0 + uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1 with: client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} @@ -293,7 +293,7 @@ jobs: working-directory: ./e2e steps: - id: token - uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0 + uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1 with: client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} @@ -325,7 +325,7 @@ jobs: working-directory: ./server steps: - id: token - uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0 + uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1 with: client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} @@ -361,7 +361,7 @@ jobs: runner: [ubuntu-latest, ubuntu-24.04-arm] steps: - id: token - uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0 + uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1 with: client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} @@ -374,7 +374,7 @@ jobs: token: ${{ steps.token.outputs.token }} - name: Setup pnpm - uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0 + uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8 - name: Setup Node uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 @@ -438,7 +438,7 @@ jobs: runner: [ubuntu-latest, ubuntu-24.04-arm] steps: - id: token - uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0 + uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1 with: client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} @@ -451,7 +451,7 @@ jobs: token: ${{ steps.token.outputs.token }} - name: Setup pnpm - uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0 + uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8 - name: Setup Node uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 @@ -546,7 +546,7 @@ jobs: contents: read steps: - id: token - uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0 + uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1 with: client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} @@ -583,7 +583,7 @@ jobs: working-directory: ./machine-learning steps: - id: token - uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0 + uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1 with: client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} @@ -613,7 +613,7 @@ jobs: working-directory: ./.github steps: - id: token - uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0 + uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1 with: client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} @@ -643,7 +643,7 @@ jobs: contents: read steps: - id: token - uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0 + uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1 with: client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} @@ -664,7 +664,7 @@ jobs: contents: read steps: - id: token - uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0 + uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1 with: client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} @@ -722,7 +722,7 @@ jobs: - 5432:5432 steps: - id: token - uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0 + uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1 with: client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} diff --git a/.github/workflows/weblate-lock.yml b/.github/workflows/weblate-lock.yml index 7063820839..af321ce534 100644 --- a/.github/workflows/weblate-lock.yml +++ b/.github/workflows/weblate-lock.yml @@ -24,7 +24,7 @@ jobs: should_run: ${{ steps.check.outputs.should_run }} steps: - id: token - uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0 + uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1 with: client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} @@ -47,7 +47,7 @@ jobs: if: ${{ fromJSON(needs.pre-job.outputs.should_run).i18n == true }} steps: - id: token - uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0 + uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1 with: client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index 1484498281..0000000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,134 +0,0 @@ -# Contributor Covenant Code of Conduct - -## Our Pledge - -We as members, contributors, and leaders pledge to make participation -in our community a harassment-free experience for everyone, regardless -of age, body size, visible or invisible disability, ethnicity, sex -characteristics, gender identity and expression, level of experience, -education, socio-economic status, nationality, personal appearance, -race, religion, or sexual identity and orientation. - -We pledge to act and interact in ways that contribute to an open, -welcoming, diverse, inclusive, and healthy community. - -## Our Standards - -Examples of behavior that contributes to a positive environment for -our community include: - -- Demonstrating empathy and kindness toward other people -- Being respectful of differing opinions, viewpoints, and experiences -- Giving and gracefully accepting constructive feedback -- Accepting responsibility and apologizing to those affected by our - mistakes, and learning from the experience -- Focusing on what is best not just for us as individuals, but for the - overall community - -Examples of unacceptable behavior include: - -- The use of sexualized language or imagery, and sexual attention or - advances of any kind -- Trolling, insulting or derogatory comments, and personal or - political attacks -- Public or private harassment -- Publishing others' private information, such as a physical or email - address, without their explicit permission -- Other conduct which could reasonably be considered inappropriate in - a professional setting - -## Enforcement Responsibilities - -Community leaders are responsible for clarifying and enforcing our -standards of acceptable behavior and will take appropriate and fair -corrective action in response to any behavior that they deem -inappropriate, threatening, offensive, or harmful. - -Community leaders have the right and responsibility to remove, edit, -or reject comments, commits, code, wiki edits, issues, and other -contributions that are not aligned to this Code of Conduct, and will -communicate reasons for moderation decisions when appropriate. - -## Scope - -This Code of Conduct applies within all community spaces, and also -applies when an individual is officially representing the community in -public spaces. Examples of representing our community include using an -official e-mail address, posting via an official social media account, -or acting as an appointed representative at an online or offline -event. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior -may be reported to the community leaders responsible for enforcement -at our Discord channel. All complaints -will be reviewed and investigated promptly and fairly. - -All community leaders are obligated to respect the privacy and -security of the reporter of any incident. - -## Enforcement Guidelines - -Community leaders will follow these Community Impact Guidelines in -determining the consequences for any action they deem in violation of -this Code of Conduct: - -### 1. Correction - -**Community Impact**: Use of inappropriate language or other behavior -deemed unprofessional or unwelcome in the community. - -**Consequence**: A private, written warning from community leaders, -providing clarity around the nature of the violation and an -explanation of why the behavior was inappropriate. A public apology -may be requested. - -### 2. Warning - -**Community Impact**: A violation through a single incident or series -of actions. - -**Consequence**: A warning with consequences for continued -behavior. No interaction with the people involved, including -unsolicited interaction with those enforcing the Code of Conduct, for -a specified period of time. This includes avoiding interactions in -community spaces as well as external channels like social -media. Violating these terms may lead to a temporary or permanent ban. - -### 3. Temporary Ban - -**Community Impact**: A serious violation of community standards, -including sustained inappropriate behavior. - -**Consequence**: A temporary ban from any sort of interaction or -public communication with the community for a specified period of -time. No public or private interaction with the people involved, -including unsolicited interaction with those enforcing the Code of -Conduct, is allowed during this period. Violating these terms may lead -to a permanent ban. - -### 4. Permanent Ban - -**Community Impact**: Demonstrating a pattern of violation of -community standards, including sustained inappropriate behavior, -harassment of an individual, or aggression toward or disparagement of -classes of individuals. - -**Consequence**: A permanent ban from any sort of public interaction -within the community. - -## Attribution - -This Code of Conduct is adapted from the [Contributor -Covenant][homepage], version 2.0, available at -https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. - -Community Impact Guidelines were inspired by [Mozilla's code of -conduct enforcement ladder](https://github.com/mozilla/diversity). - -[homepage]: https://www.contributor-covenant.org - -For answers to common questions about this code of conduct, see the -FAQ at https://www.contributor-covenant.org/faq. Translations are -available at https://www.contributor-covenant.org/translations. diff --git a/SECURITY.md b/SECURITY.md deleted file mode 100644 index 1877440a8d..0000000000 --- a/SECURITY.md +++ /dev/null @@ -1,5 +0,0 @@ -# Security Policy - -## Reporting a Vulnerability - -Please report security issues to `security@immich.app` diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.dev.yml index adcb6b628f..2398b971dd 100644 --- a/docker/docker-compose.dev.yml +++ b/docker/docker-compose.dev.yml @@ -154,7 +154,7 @@ services: redis: container_name: immich_redis - image: docker.io/valkey/valkey:9@sha256:8436e10bc65c94886a91d4415b6a6dfa9cb5a306fb3b996e5bb67cd2b4854193 + image: docker.io/valkey/valkey:9@sha256:4963247afc4cd33c7d3b2d2816b9f7f8eeebab148d29056c2ca4d7cbc966f2d9 healthcheck: test: redis-cli ping || exit 1 diff --git a/docker/docker-compose.prod.yml b/docker/docker-compose.prod.yml index 24ecb02624..bad7527ca8 100644 --- a/docker/docker-compose.prod.yml +++ b/docker/docker-compose.prod.yml @@ -56,7 +56,7 @@ services: redis: container_name: immich_redis - image: docker.io/valkey/valkey:9@sha256:8436e10bc65c94886a91d4415b6a6dfa9cb5a306fb3b996e5bb67cd2b4854193 + image: docker.io/valkey/valkey:9@sha256:4963247afc4cd33c7d3b2d2816b9f7f8eeebab148d29056c2ca4d7cbc966f2d9 healthcheck: test: redis-cli ping || exit 1 restart: always @@ -85,7 +85,7 @@ services: container_name: immich_prometheus ports: - 9090:9090 - image: prom/prometheus@sha256:e4254400b85610324913f0dc4acf92603d9984e7519414c5a12811aa6146acc3 + image: prom/prometheus@sha256:69f5241418838263316593f7274a304b095c40bcf22e57272865da91bd60a8ac volumes: - ./prometheus.yml:/etc/prometheus/prometheus.yml - prometheus-data:/prometheus diff --git a/docker/docker-compose.rootless.yml b/docker/docker-compose.rootless.yml index 3f3e53424b..33cbc4016a 100644 --- a/docker/docker-compose.rootless.yml +++ b/docker/docker-compose.rootless.yml @@ -61,7 +61,7 @@ services: redis: container_name: immich_redis - image: docker.io/valkey/valkey:9@sha256:8436e10bc65c94886a91d4415b6a6dfa9cb5a306fb3b996e5bb67cd2b4854193 + image: docker.io/valkey/valkey:9@sha256:4963247afc4cd33c7d3b2d2816b9f7f8eeebab148d29056c2ca4d7cbc966f2d9 user: '1000:1000' security_opt: - no-new-privileges:true diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 5f3ad35245..98444d8793 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -49,7 +49,7 @@ services: redis: container_name: immich_redis - image: docker.io/valkey/valkey:9@sha256:8436e10bc65c94886a91d4415b6a6dfa9cb5a306fb3b996e5bb67cd2b4854193 + image: docker.io/valkey/valkey:9@sha256:4963247afc4cd33c7d3b2d2816b9f7f8eeebab148d29056c2ca4d7cbc966f2d9 healthcheck: test: redis-cli ping || exit 1 restart: always diff --git a/docs/docs/developer/setup.md b/docs/docs/developer/setup.md index dd72baa45b..cac9f501d8 100644 --- a/docs/docs/developer/setup.md +++ b/docs/docs/developer/setup.md @@ -109,6 +109,24 @@ mise //mobile:translation The mobile app asks you what backend to connect to. You can utilize the demo backend (https://demo.immich.app/) if you don't need to change server code or upload photos. Alternatively, you can run the server yourself per the instructions above. +#### UI components and widget previews + +Shared design-system widgets (buttons, inputs, forms) live in the +[`immich_ui` package](https://github.com/immich-app/immich/tree/main/mobile/packages/ui/) +under `mobile/packages/ui/`. Components are defined in `lib/src/components/` +and have matching previews in `lib/src/previews/`. + +To inspect a component in isolation with a light/dark toggle and hot reload, +launch [Flutter's Widget Previewer](https://docs.flutter.dev/tools/widget-previewer): + +```bash +cd mobile/packages/ui +flutter widget-preview start +``` + +In VS Code or Android Studio with the Flutter plugin, the previewer +auto-starts when you open the **Flutter Widget Preview** tab in the sidebar. + ## IDE setup ### Lint / format extensions diff --git a/e2e/docker-compose.yml b/e2e/docker-compose.yml index 0ccd54cf3f..8a48cbbdd6 100644 --- a/e2e/docker-compose.yml +++ b/e2e/docker-compose.yml @@ -44,7 +44,7 @@ services: redis: container_name: immich-e2e-redis - image: docker.io/valkey/valkey:9@sha256:8436e10bc65c94886a91d4415b6a6dfa9cb5a306fb3b996e5bb67cd2b4854193 + image: docker.io/valkey/valkey:9@sha256:4963247afc4cd33c7d3b2d2816b9f7f8eeebab148d29056c2ca4d7cbc966f2d9 healthcheck: test: redis-cli ping || exit 1 diff --git a/e2e/src/specs/server/api/server.e2e-spec.ts b/e2e/src/specs/server/api/server.e2e-spec.ts index 1220e6cab5..902c5302e5 100644 --- a/e2e/src/specs/server/api/server.e2e-spec.ts +++ b/e2e/src/specs/server/api/server.e2e-spec.ts @@ -95,6 +95,7 @@ describe('/server', () => { major: expect.any(Number), minor: expect.any(Number), patch: expect.any(Number), + prerelease: null, }); }); }); @@ -115,6 +116,7 @@ describe('/server', () => { oauthAutoLaunch: false, ocr: false, passwordLogin: true, + realtimeTranscoding: false, search: true, sidecar: true, trash: true, @@ -139,6 +141,7 @@ describe('/server', () => { maintenanceMode: false, mapDarkStyleUrl: 'https://tiles.immich.cloud/v1/style/dark.json', mapLightStyleUrl: 'https://tiles.immich.cloud/v1/style/light.json', + minFaces: 3, }); }); }); diff --git a/e2e/src/specs/server/api/system-config.e2e-spec.ts b/e2e/src/specs/server/api/system-config.e2e-spec.ts index 1bd7bdc489..91b747cf28 100644 --- a/e2e/src/specs/server/api/system-config.e2e-spec.ts +++ b/e2e/src/specs/server/api/system-config.e2e-spec.ts @@ -21,18 +21,18 @@ describe('/system-config', () => { const response1 = await request(app) .put('/system-config') .set('Authorization', `Bearer ${admin.accessToken}`) - .send({ ...config, newVersionCheck: { enabled: false } }); + .send({ ...config, newVersionCheck: { enabled: false, channel: 'stable' } }); expect(response1.status).toBe(200); - expect(response1.body).toEqual({ ...config, newVersionCheck: { enabled: false } }); + expect(response1.body).toEqual({ ...config, newVersionCheck: { enabled: false, channel: 'stable' } }); const response2 = await request(app) .put('/system-config') .set('Authorization', `Bearer ${admin.accessToken}`) - .send({ ...config, newVersionCheck: { enabled: true } }); + .send({ ...config, newVersionCheck: { enabled: true, channel: 'stable' } }); expect(response2.status).toBe(200); - expect(response2.body).toEqual({ ...config, newVersionCheck: { enabled: true } }); + expect(response2.body).toEqual({ ...config, newVersionCheck: { enabled: true, channel: 'stable' } }); }); it('should reject an invalid config entry', async () => { diff --git a/e2e/src/specs/server/api/user.e2e-spec.ts b/e2e/src/specs/server/api/user.e2e-spec.ts index 8a2197efde..2dc789a91b 100644 --- a/e2e/src/specs/server/api/user.e2e-spec.ts +++ b/e2e/src/specs/server/api/user.e2e-spec.ts @@ -230,6 +230,21 @@ describe('/users', () => { const after = await getMyPreferences({ headers: asBearerAuth(admin.accessToken) }); expect(after).toMatchObject({ download: { includeEmbeddedVideos: true } }); }); + + it('should update minimum face count to display people', async () => { + const before = await getMyPreferences({ headers: asBearerAuth(admin.accessToken) }); + expect(before).toMatchObject({ people: { minimumFaces: 3 } }); + + const { status, body } = await request(app) + .put('/users/me/preferences') + .send({ people: { minimumFaces: 2 } }) + .set('Authorization', `Bearer ${admin.accessToken}`); + expect(status).toBe(200); + expect(body).toMatchObject({ people: { minimumFaces: 2 } }); + + const after = await getMyPreferences({ headers: asBearerAuth(admin.accessToken) }); + expect(after).toMatchObject({ people: { minimumFaces: 2 } }); + }); }); describe('GET /users/:id', () => { diff --git a/i18n/en.json b/i18n/en.json index 4ebd24b56b..f4ad3001c2 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -305,6 +305,8 @@ "refreshing_all_libraries": "Refreshing all libraries", "registration": "Admin Registration", "registration_description": "Since you are the first user on the system, you will be assigned as the Admin and are responsible for administrative tasks, and additional users will be created by you.", + "release_channel_release_candidate": "Release candidate", + "release_channel_stable": "Stable", "remove_failed_jobs": "Remove failed jobs", "require_password_change_on_login": "Require user to change password on first login", "reset_settings_to_default": "Reset settings to default", @@ -399,6 +401,10 @@ "transcoding_preferred_hardware_device_description": "Applies only to VAAPI and QSV. Sets the dri node used for hardware transcoding.", "transcoding_preset_preset": "Preset (-preset)", "transcoding_preset_preset_description": "Compression speed. Slower presets produce smaller files, and increase quality when targeting a certain bitrate. VP9 ignores speeds above 'faster'.", + "transcoding_realtime": "Real-time Transcoding [EXPERIMENTAL]", + "transcoding_realtime_description": "Allows transcoding to be performed in real-time as the video is being streamed. Enables quality switching, but may cause higher playback latency and stuttering depending on server capabilities.", + "transcoding_realtime_enabled": "Enable real-time transcoding", + "transcoding_realtime_enabled_description": "If disabled, the server will refuse to start new real-time transcoding sessions.", "transcoding_reference_frames": "Reference frames", "transcoding_reference_frames_description": "The number of frames to reference when compressing a given frame. Higher values improve compression efficiency, but slow down encoding. 0 sets this value automatically.", "transcoding_required_description": "Only videos not in an accepted format", @@ -442,6 +448,8 @@ "user_settings_description": "Manage user settings", "user_successfully_removed": "User {email} has been successfully removed.", "users_page_description": "Admin users page", + "version_check_channel": "Release channel", + "version_check_channel_description": "Pick the release channel you want to get version announcements for", "version_check_enabled_description": "Enable version check", "version_check_implications": "The version check feature relies on periodic communication with {server}", "version_check_settings": "Version Check", @@ -1584,6 +1592,8 @@ "merge_people_prompt": "Do you want to merge these people? This action is irreversible.", "merge_people_successfully": "Merge people successfully", "merged_people_count": "Merged {count, plural, one {# person} other {# people}}", + "minFaces": "Minimum faces", + "minFaces_description": "The minimum number of recognized faces for a person to be displayed", "minimize": "Minimize", "minute": "Minute", "minutes": "Minutes", @@ -2233,6 +2243,7 @@ "slideshow_repeat": "Repeat slideshow", "slideshow_repeat_description": "Loop back to beginning when slideshow ends", "slideshow_settings": "Slideshow settings", + "smart_album": "Smart album", "sort_albums_by": "Sort albums by...", "sort_created": "Date created", "sort_items": "Number of items", @@ -2451,6 +2462,7 @@ "video": "Video", "video_hover_setting": "Play video thumbnail on hover", "video_hover_setting_description": "Play video thumbnail when mouse is hovering over item. Even when disabled, playback can be started by hovering over the play icon.", + "video_quality": "Video quality", "videos": "Videos", "videos_count": "{count, plural, one {# Video} other {# Videos}}", "videos_only": "Videos only", diff --git a/machine-learning/Dockerfile b/machine-learning/Dockerfile index c6f9f01675..f6bbe9cebd 100644 --- a/machine-learning/Dockerfile +++ b/machine-learning/Dockerfile @@ -1,8 +1,8 @@ ARG DEVICE=cpu -FROM python:3.11-bookworm@sha256:970c99f886b839fc8829289040c1845dadaf2cae46b37acc7710333158ec29b4 AS builder-cpu +FROM python:3.11-bookworm@sha256:121d86b6d08752968a7dddbc708849e5f3a839bbff47f32212b46d2a1d842bab AS builder-cpu -FROM python:3.13-slim-trixie@sha256:d168b8d9eb761f4d3fe305ebd04aeb7e7f2de0297cec5fb2f8f6403244621664 AS builder-openvino +FROM python:3.13-slim-trixie@sha256:b04b5d7233d2ad9c379e22ea8927cd1378cd15c60d4ef876c065b25ea8fb3bf3 AS builder-openvino FROM builder-cpu AS builder-cuda @@ -39,12 +39,12 @@ RUN --mount=type=cache,target=/root/.cache/uv \ --mount=type=bind,source=pyproject.toml,target=pyproject.toml \ uv sync --frozen --extra ${DEVICE} --no-dev --no-editable --no-install-project --compile-bytecode --no-progress --active --link-mode copy -FROM python:3.11-slim-bookworm@sha256:9c6f90801e6b68e772b7c0ca74260cbf7af9f320acec894e26fccdaccfbe3b47 AS prod-cpu +FROM python:3.11-slim-bookworm@sha256:8dca233de9f3d9bb410665f00a4da6dd06f331083137e0e98ccf227236fcc438 AS prod-cpu ENV LD_PRELOAD=/usr/lib/libmimalloc.so.2 \ MACHINE_LEARNING_MODEL_ARENA=false -FROM python:3.13-slim-trixie@sha256:d168b8d9eb761f4d3fe305ebd04aeb7e7f2de0297cec5fb2f8f6403244621664 AS prod-openvino +FROM python:3.13-slim-trixie@sha256:b04b5d7233d2ad9c379e22ea8927cd1378cd15c60d4ef876c065b25ea8fb3bf3 AS prod-openvino RUN apt-get update && \ apt-get install --no-install-recommends -yqq ocl-icd-libopencl1 wget && \ diff --git a/machine-learning/immich_ml/__main__.py b/machine-learning/immich_ml/__main__.py index 8d575a58d5..f305c77657 100644 --- a/machine-learning/immich_ml/__main__.py +++ b/machine-learning/immich_ml/__main__.py @@ -49,6 +49,7 @@ try: str(settings.http_keepalive_timeout_s), "--graceful-timeout", "10", + "--no-control-socket", ], ) as cmd: cmd.wait() diff --git a/machine-learning/immich_ml/main.py b/machine-learning/immich_ml/main.py index 54f9a53930..4ac5b020ae 100644 --- a/machine-learning/immich_ml/main.py +++ b/machine-learning/immich_ml/main.py @@ -12,7 +12,7 @@ from zipfile import BadZipFile import orjson from fastapi import Depends, FastAPI, File, Form, HTTPException -from fastapi.responses import ORJSONResponse, PlainTextResponse +from fastapi.responses import PlainTextResponse from onnxruntime.capi.onnxruntime_pybind11_state import InvalidProtobuf, NoSuchFile from PIL.Image import Image from pydantic import ValidationError @@ -32,6 +32,7 @@ from .schemas import ( ModelIdentity, ModelTask, ModelType, + ORJSONResponse, PipelineRequest, T, ) diff --git a/machine-learning/immich_ml/models/clip/textual.py b/machine-learning/immich_ml/models/clip/textual.py index 7e1908e120..989f46a2af 100644 --- a/machine-learning/immich_ml/models/clip/textual.py +++ b/machine-learning/immich_ml/models/clip/textual.py @@ -89,7 +89,9 @@ class OpenClipTextualEncoder(BaseCLIPTextualEncoder): tokenizer: Tokenizer = Tokenizer.from_file(self.tokenizer_file_path.as_posix()) - pad_id: int = tokenizer.token_to_id(pad_token) + pad_id = tokenizer.token_to_id(pad_token) + if pad_id is None: + raise ValueError(f"Pad token '{pad_token}' not found in tokenizer vocab") tokenizer.enable_padding(length=context_length, pad_token=pad_token, pad_id=pad_id) tokenizer.enable_truncation(max_length=context_length) diff --git a/machine-learning/immich_ml/models/ocr/recognition.py b/machine-learning/immich_ml/models/ocr/recognition.py index 6408e4818f..94f40c9285 100644 --- a/machine-learning/immich_ml/models/ocr/recognition.py +++ b/machine-learning/immich_ml/models/ocr/recognition.py @@ -64,6 +64,7 @@ class TextRecognizer(InferenceModel): rec_batch_num=max_batch_size if max_batch_size else 6, rec_img_shape=(3, 48, 320), lang_type=self.language, + model_root_dir=self.cache_dir, ) ) return session diff --git a/machine-learning/immich_ml/schemas.py b/machine-learning/immich_ml/schemas.py index 41706180de..d5304cf763 100644 --- a/machine-learning/immich_ml/schemas.py +++ b/machine-learning/immich_ml/schemas.py @@ -3,9 +3,16 @@ from typing import Any, Literal, Protocol, TypeGuard, TypeVar import numpy as np import numpy.typing as npt +import orjson +from fastapi.responses import JSONResponse from typing_extensions import TypedDict +class ORJSONResponse(JSONResponse): + def render(self, content: Any) -> bytes: + return orjson.dumps(content, option=orjson.OPT_SERIALIZE_NUMPY) + + class StrEnum(str, Enum): value: str diff --git a/machine-learning/test_main.py b/machine-learning/test_main.py index b281c0d417..5145be0045 100644 --- a/machine-learning/test_main.py +++ b/machine-learning/test_main.py @@ -1028,7 +1028,12 @@ class TestOcr: text_recognizer.load() rapid_recognizer.assert_called_once_with( - OcrOptions(session=ort_session.return_value, rec_batch_num=6, rec_img_shape=(3, 48, 320)) + OcrOptions( + session=ort_session.return_value, + rec_batch_num=6, + rec_img_shape=(3, 48, 320), + model_root_dir=text_recognizer.cache_dir, + ) ) def test_set_custom_max_batch_size(self, ort_session: mock.Mock, path: mock.Mock, mocker: MockerFixture) -> None: @@ -1041,7 +1046,12 @@ class TestOcr: text_recognizer.load() rapid_recognizer.assert_called_once_with( - OcrOptions(session=ort_session.return_value, rec_batch_num=4, rec_img_shape=(3, 48, 320)) + OcrOptions( + session=ort_session.return_value, + rec_batch_num=4, + rec_img_shape=(3, 48, 320), + model_root_dir=text_recognizer.cache_dir, + ) ) def test_ignore_other_custom_max_batch_size( @@ -1056,7 +1066,12 @@ class TestOcr: text_recognizer.load() rapid_recognizer.assert_called_once_with( - OcrOptions(session=ort_session.return_value, rec_batch_num=6, rec_img_shape=(3, 48, 320)) + OcrOptions( + session=ort_session.return_value, + rec_batch_num=6, + rec_img_shape=(3, 48, 320), + model_root_dir=text_recognizer.cache_dir, + ) ) diff --git a/machine-learning/uv.lock b/machine-learning/uv.lock index 145ee5f9e5..933d19d23d 100644 --- a/machine-learning/uv.lock +++ b/machine-learning/uv.lock @@ -2,11 +2,14 @@ version = 1 revision = 3 requires-python = ">=3.11, <4.0" resolution-markers = [ - "python_full_version >= '3.13' and sys_platform == 'darwin'", + "python_full_version >= '3.15' and sys_platform == 'darwin'", + "python_full_version >= '3.13' and python_full_version < '3.15' and sys_platform == 'darwin'", "python_full_version == '3.12.*' and sys_platform == 'darwin'", - "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "python_full_version >= '3.15' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "python_full_version >= '3.13' and python_full_version < '3.15' 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.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.15' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.15' and sys_platform != 'darwin' and sys_platform != 'linux')", + "(python_full_version >= '3.13' and python_full_version < '3.15' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and python_full_version < '3.15' 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.12' and sys_platform == 'darwin'", "python_full_version < '3.12' and platform_machine == 'aarch64' and sys_platform == 'linux'", @@ -76,6 +79,46 @@ 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 = "ast-serialize" +version = "0.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/81/9d/09e27731bd5864a9ce04e3244074e674bb8936bf62b45e0357248717adac/ast_serialize-0.5.0.tar.gz", hash = "sha256:5880091bfe6f4f986f22866375c2e884843e7a0b6343ae41aeea659613d879b6", size = 61157, upload-time = "2026-05-17T17:48:29.429Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c0/9a/13dde51ba9e15f8b97957ab7cb0120d0e381524d651c6bd630b9c359227f/ast_serialize-0.5.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8f5c14f169eb0972c0c21bada5358b23d6047c76583b005234f865b11f1fa00a", size = 1183520, upload-time = "2026-05-17T17:47:30.831Z" }, + { url = "https://files.pythonhosted.org/packages/37/de/5a7f0a9fe68944f536632a5af84676739c7d2582be42deb082634bf3a754/ast_serialize-0.5.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7d1a2de9de5be04652f0ed60738356ef94f66db37924a9499fffe98dc491aa0b", size = 1175779, upload-time = "2026-05-17T17:47:32.551Z" }, + { url = "https://files.pythonhosted.org/packages/9c/81/0bb853e76e4f6e9a1855d569003c59e19ffac45f7079d91505d1bb212f92/ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be5173fb66f9b49026d9d5a2ff0fc7c7009077107c0eb285b2d60fdf1fe10bd1", size = 1233750, upload-time = "2026-05-17T17:47:34.731Z" }, + { url = "https://files.pythonhosted.org/packages/e5/d3/4cf705beeccc08754d0bbda99aefff26110e209b9a07ac8a6b60eec48531/ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f8015cd071ac1339924ee2b8098c93e00e155f30a16f40ec9816fcf84f4753f6", size = 1235942, upload-time = "2026-05-17T17:47:36.287Z" }, + { url = "https://files.pythonhosted.org/packages/26/c8/ee097e437ea27dd2b8b227865c875492b585650a5802a22d82b304c8201b/ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5499e8797edff2a9186aa313ed382c6b422e798e9332d9953badcee6e69a88f2", size = 1442517, upload-time = "2026-05-17T17:47:38.17Z" }, + { url = "https://files.pythonhosted.org/packages/ff/bd/68063442838f1ba68ec72b5436430bc75b3bb17a1a3c3063f09b0c05ae2b/ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6848f2a093fb5548751a9a09bff8fcd229e2bbeb0e3331f391b6ae6d26cd9903", size = 1254081, upload-time = "2026-05-17T17:47:39.826Z" }, + { url = "https://files.pythonhosted.org/packages/50/e2/1e520793bc6a4e4524a6ab022391e827825eaa0c3811828bfdc6852eca26/ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:832d4c998e0b091fd60a6d6bceee535483c4d490de9ba85003af835225719261", size = 1259910, upload-time = "2026-05-17T17:47:41.369Z" }, + { url = "https://files.pythonhosted.org/packages/4e/e1/49b60f467979979cfe6913b43948ff25bca971ad0591d181812f163a988e/ast_serialize-0.5.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:16db7c62ec0b8efe1d7afd283a388d8f74f2605d56032e5a37747d2de8dba027", size = 1250678, upload-time = "2026-05-17T17:47:43.702Z" }, + { url = "https://files.pythonhosted.org/packages/74/ba/66ab9555de6275677566f6574e5ef6c29cb185ea866f643bc06f8280a8ee/ast_serialize-0.5.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:baf5eb061eb5bccade4128ad42da33787d72f6013809cd1b590376ece8b3c937", size = 1301603, upload-time = "2026-05-17T17:47:46.256Z" }, + { url = "https://files.pythonhosted.org/packages/66/42/6aca9b9abc710014b2be9059689e5dd1679339e78f567ffb4d255a9e2050/ast_serialize-0.5.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:104e4a35bd7c124173c41760ef9aaea17ddb3f86c65cb643671d59afbe3ee94c", size = 1410332, upload-time = "2026-05-17T17:47:47.899Z" }, + { url = "https://files.pythonhosted.org/packages/47/68/2f76594432a22581ecf878b5e75a9b8601c24b2241cf0bbeb1e21fcf370c/ast_serialize-0.5.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:36be371028fc1675acb38a331bde160dbab7ff907fdf00b67eb6911aa106951b", size = 1509979, upload-time = "2026-05-17T17:47:50.942Z" }, + { url = "https://files.pythonhosted.org/packages/40/ac/a93c9b58292653f6c595752f677a08e608f903b710594909e9231a389b3b/ast_serialize-0.5.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:061ee58bdb52341c8201a6df41182a977736bae3b7ded87ca7176ca25a8a47ab", size = 1505002, upload-time = "2026-05-17T17:47:54.093Z" }, + { url = "https://files.pythonhosted.org/packages/14/2e/b278f68c497ee2f1d1576cbbef8db5281cd4a5f2db040537592ac9c8862e/ast_serialize-0.5.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b15219e9cdc9f53f6f4cb51c009203507228226148c05c5e8fe451c28b435eb3", size = 1456231, upload-time = "2026-05-17T17:47:56.311Z" }, + { url = "https://files.pythonhosted.org/packages/0b/43/419be1c566a4c504cd8fd60ce2f84e790f295495c0f327cfaeadf3d51012/ast_serialize-0.5.0-cp314-cp314t-win32.whl", hash = "sha256:842d1c004bb466c7df036f95fabef789570541922b10976b12f5592a69cf0b38", size = 1058668, upload-time = "2026-05-17T17:47:58.305Z" }, + { url = "https://files.pythonhosted.org/packages/03/6f/c9d4d549295ed05111aeb8853232d1afd9d0a179fddb01eeffbb3a4a6842/ast_serialize-0.5.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b0c06d760909b095cc466356dfccd05a1c7233a6ca191c020dca2c6a6f16c24c", size = 1101075, upload-time = "2026-05-17T17:48:00.35Z" }, + { url = "https://files.pythonhosted.org/packages/d0/8e/d00c5ab30c58222e07d62956fca86c59d91b9ad32997e633c38b526623a3/ast_serialize-0.5.0-cp314-cp314t-win_arm64.whl", hash = "sha256:787baedb0262cc49e8ce37cc15c00ae818e46a165a3b36f5e21ed174998104cb", size = 1075347, upload-time = "2026-05-17T17:48:01.753Z" }, + { url = "https://files.pythonhosted.org/packages/e0/9e/dc2530acb3a60dc6e46d65abf27d1d9f86721694757906a148d90a6860de/ast_serialize-0.5.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:0668aa9459cfa8c9c49ddd2163ebcf43088ba045ef7492af6fe22e0098303101", size = 1191380, upload-time = "2026-05-17T17:48:03.738Z" }, + { url = "https://files.pythonhosted.org/packages/26/0a/bd3d18a582f273d6c843d16bb9e22e9e16365ff7991e92f18f798e9f1224/ast_serialize-0.5.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:bf683d6363edf2b39eed6b6d4fe22d34b6203867a67e27134d9e2a2680c4bc4a", size = 1183879, upload-time = "2026-05-17T17:48:05.463Z" }, + { url = "https://files.pythonhosted.org/packages/40/ae/1f919100f8620887af58fcc381c61a1f218cdf89c6e155f87b213e61010a/ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cc22cf0c9be65e71cf88fda130af60d61eb4a79370ad4cfe7900d48a4aa2211", size = 1244529, upload-time = "2026-05-17T17:48:07.008Z" }, + { url = "https://files.pythonhosted.org/packages/c6/ca/6376559dcce707cdbc1d0d9a13c8d3baaaa501e949ce0ebdc4230cd881aa/ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f66173891548c9f2726bf27957b41cabce12fa679dc6da505ddbde4d4b3b31cf", size = 1240560, upload-time = "2026-05-17T17:48:08.46Z" }, + { url = "https://files.pythonhosted.org/packages/35/b2/a620e206b5aeb7efbf2710336df57d457cffbb3991076bbcc1147ef9abd4/ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e42d729ef2be96a14efbad355093284739e3670ece3e534f82cc8832790911d9", size = 1451172, upload-time = "2026-05-17T17:48:09.922Z" }, + { url = "https://files.pythonhosted.org/packages/fa/e0/4ad5c04c24a40481b2935ce9a0ccdb6023dc8b667167d06ae530cc3512f2/ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b725026bafa801dbd7310eb13a75f0a2e370e7e51b2cb225f9d21fcfadf919ee", size = 1265072, upload-time = "2026-05-17T17:48:11.469Z" }, + { url = "https://files.pythonhosted.org/packages/b2/71/4d1d479aa56d0101c40e17720c3d6ac2af7269ea0487a80b18e7bfd1a5b7/ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b54f60c1d78767a53b67eaa663f0dfac3afe606aa07f1301572f588b73d64809", size = 1270488, upload-time = "2026-05-17T17:48:13.575Z" }, + { url = "https://files.pythonhosted.org/packages/6d/4f/0de1bbe06f6edef9fde4ed12ca8e7b3ec7e6e2bd4e672c5af487f7957665/ast_serialize-0.5.0-cp39-abi3-manylinux_2_31_riscv64.whl", hash = "sha256:27d51654fc240a1e87e742d353d98eb45b75f62f129086b3596ab53df2ac2a43", size = 1260702, upload-time = "2026-05-17T17:48:15.141Z" }, + { url = "https://files.pythonhosted.org/packages/75/61/e00872439cfdddcc3c1b6cdaa6e5d904ba8e26a18807c67c4e14409d0ca8/ast_serialize-0.5.0-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c36237c46dd1674542f2109740ea5ea485a169bf1431939ada0434e17934", size = 1311182, upload-time = "2026-05-17T17:48:16.779Z" }, + { url = "https://files.pythonhosted.org/packages/76/8e/699a5b955f7926956c95e9e1d74132acad73c2fe7a426f94da89123c20aa/ast_serialize-0.5.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1943db345233cc7194a470f13afa9c59772c0b123dea0c9414c4d4ca54369759", size = 1421410, upload-time = "2026-05-17T17:48:18.527Z" }, + { url = "https://files.pythonhosted.org/packages/a9/ae/d5b7626874478997adc7a29ab28accf21e596fb590c944290401dfd0b29e/ast_serialize-0.5.0-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:df1c00022cbbcb064bfaa505aa9c9295362443ce5dacb459d1331d3da353f887", size = 1516587, upload-time = "2026-05-17T17:48:20.133Z" }, + { url = "https://files.pythonhosted.org/packages/0c/ce/b59e02a82d9c4244d64cde502e0b00e83e38816abe19155ceb5437402c7f/ast_serialize-0.5.0-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:cae65289fc456fde04af979a2be09302ef5d8ab92ef23e596d6746dc267ada27", size = 1515171, upload-time = "2026-05-17T17:48:21.921Z" }, + { url = "https://files.pythonhosted.org/packages/8b/38/d8d90042747d05aa08d4efcf1c99035a5f670a6bf4c214d31644392afbca/ast_serialize-0.5.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:239a4c354e8d676e9d94631d1d4a64edc6b266f86ff3a5a80aedd344f342c01d", size = 1464668, upload-time = "2026-05-17T17:48:23.544Z" }, + { url = "https://files.pythonhosted.org/packages/dd/51/5b840c4df7334104cecffa28f23904fe81ca89ca223d2450e288de39fd3c/ast_serialize-0.5.0-cp39-abi3-win32.whl", hash = "sha256:143a4ef63285a075871908fda3672dc21864b83a8ec3ee12304aa3e4c5387b9a", size = 1068311, upload-time = "2026-05-17T17:48:25.027Z" }, + { url = "https://files.pythonhosted.org/packages/41/11/ca5672c7d491825bc4cd6702dea106a6b60d928707712ec257c7833ae476/ast_serialize-0.5.0-cp39-abi3-win_amd64.whl", hash = "sha256:cf25572c526add400f26a4750dc6ce0c3bb93fc1f75e7ae0cad4ce4f2cd5c590", size = 1108931, upload-time = "2026-05-17T17:48:26.591Z" }, + { url = "https://files.pythonhosted.org/packages/45/19/cc8bd127d28a43da249aa955cfd164cf8fd534e79e42cea96c4854d72fd0/ast_serialize-0.5.0-cp39-abi3-win_arm64.whl", hash = "sha256:92a31c9c20d25a076edaeec76b128a3535d74a24f340b9a8a7e96c9b86dc9642", size = 1081181, upload-time = "2026-05-17T17:48:28.122Z" }, +] + [[package]] name = "bidict" version = "0.23.1" @@ -243,14 +286,14 @@ wheels = [ [[package]] name = "click" -version = "8.3.3" +version = "8.4.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/bb/63/f9e1ea081ce35720d8b92acde70daaedace594dc93b693c869e0d5910718/click-8.3.3.tar.gz", hash = "sha256:398329ad4837b2ff7cbe1dd166a4c0f8900c3ca3a218de04466f38f6497f18a2", size = 328061, upload-time = "2026-04-22T15:11:27.506Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9b/98/518d8e5081007684232226f475082b30087d0f585e8457db087298259f49/click-8.4.1.tar.gz", hash = "sha256:918b5633eddf6b41c32d4f454bf0de810065c74e3f7dbf8ee5452f8be88d3e96", size = 353007, upload-time = "2026-05-22T04:08:37.769Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ae/44/c1221527f6a71a01ec6fbad7fa78f1d50dfa02217385cf0fa3eec7087d59/click-8.3.3-py3-none-any.whl", hash = "sha256:a2bf429bb3033c89fa4936ffb35d5cb471e3719e1f3c8a7c3fff0b8314305613", size = 110502, upload-time = "2026-04-22T15:11:25.044Z" }, + { url = "https://files.pythonhosted.org/packages/c7/0d/67e5b4109ea4a837e80daa87c2c696711955e40449a97e8926672534def2/click-8.4.1-py3-none-any.whl", hash = "sha256:482be17c6991b8c19c5429a1e995d9b0efdbb63172824c41f99965dc0ade8ec2", size = 116639, upload-time = "2026-05-22T04:08:35.26Z" }, ] [[package]] @@ -511,7 +554,7 @@ sdist = { url = "https://files.pythonhosted.org/packages/0a/d2/deb3296d08097fedd [[package]] name = "fastapi" -version = "0.136.0" +version = "0.136.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-doc" }, @@ -520,9 +563,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4e/d9/e66315807e41e69e7f6a1b42a162dada2f249c5f06ad3f1a95f84ab336ef/fastapi-0.136.0.tar.gz", hash = "sha256:cf08e067cc66e106e102d9ba659463abfac245200752f8a5b7b1e813de4ff73e", size = 396607, upload-time = "2026-04-16T11:47:13.623Z" } +sdist = { url = "https://files.pythonhosted.org/packages/81/2d/ff8d91d7b564d464629a0fd50a4489c97fcb836ac230bf3a7269232a9b1f/fastapi-0.136.3.tar.gz", hash = "sha256:e487fae93ad408e6f47641ee4dfe389864fd7bec92e547ea8498fc13f43e83ab", size = 396410, upload-time = "2026-05-23T18:53:15.192Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/26/a3/0bd5f0cdb0bbc92650e8dc457e9250358411ee5d1b65e42b6632387daf81/fastapi-0.136.0-py3-none-any.whl", hash = "sha256:8793d44ec7378e2be07f8a013cf7f7aa47d6327d0dfe9804862688ec4541a6b4", size = 117556, upload-time = "2026-04-16T11:47:11.922Z" }, + { url = "https://files.pythonhosted.org/packages/e0/82/45359b62a067409bd929ae8a56b8ed13e5a8c8a61194b3c236920999ab83/fastapi-0.136.3-py3-none-any.whl", hash = "sha256:3d2a69bdf04b7e9f3afa292c3bc7a98816bbfafa10bc9b45f3f3700d2f761620", size = 117481, upload-time = "2026-05-23T18:53:16.924Z" }, ] [[package]] @@ -874,9 +917,10 @@ wheels = [ [[package]] name = "huggingface-hub" -version = "1.13.0" +version = "1.17.0" source = { registry = "https://pypi.org/simple" } dependencies = [ + { name = "click" }, { name = "filelock" }, { name = "fsspec" }, { name = "hf-xet", marker = "platform_machine == 'AMD64' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'" }, @@ -887,9 +931,9 @@ dependencies = [ { name = "typer" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/89/ff/ec7ed2eb43bd7ce8bb2233d109cc235c3e807ffe5e469dc09db261fac05e/huggingface_hub-1.13.0.tar.gz", hash = "sha256:f6df2dac5abe82ce2fe05873d10d5ff47bc677d616a2f521f4ee26db9415d9d0", size = 781788, upload-time = "2026-04-30T11:57:33.858Z" } +sdist = { url = "https://files.pythonhosted.org/packages/bd/65/9826515abb600b5722bcf53f8b4a2fb58340b1f8bfcaee19f83561c13a44/huggingface_hub-1.17.0.tar.gz", hash = "sha256:fad842b6763ef70ebc3919665b1b9273645203185400a7d6c5eddc2323cc3435", size = 797082, upload-time = "2026-05-28T15:12:13.347Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/93/db/4b1cdae9460ae1f3ca020cd767f013430ce23eb1d9c890ae3a0609b38d26/huggingface_hub-1.13.0-py3-none-any.whl", hash = "sha256:e942cb50d6a08dd5306688b1ac05bda157fd2fcc88b63dae405f7bd0d3234005", size = 660643, upload-time = "2026-04-30T11:57:31.802Z" }, + { url = "https://files.pythonhosted.org/packages/02/28/d7cef5e477b855c25d415b8f57e5bc7347c7a90cad3acf1725d0c92ca294/huggingface_hub-1.17.0-py3-none-any.whl", hash = "sha256:3b8156d23118e87f6a587648bfbc04f04a12a757ccb4ed298b35c4ae638bf24c", size = 671546, upload-time = "2026-05-28T15:12:11.441Z" }, ] [[package]] @@ -1178,80 +1222,80 @@ wheels = [ [[package]] name = "librt" -version = "0.9.0" +version = "0.11.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/eb/6b/3d5c13fb3e3c4f43206c8f9dfed13778c2ed4f000bacaa0b7ce3c402a265/librt-0.9.0.tar.gz", hash = "sha256:a0951822531e7aee6e0dfb556b30d5ee36bbe234faf60c20a16c01be3530869d", size = 184368, upload-time = "2026-04-09T16:06:26.173Z" } +sdist = { url = "https://files.pythonhosted.org/packages/40/08/9e7f6b5d2b5bed6ad055cdd5925f192bb403a51280f86b56554d9d0699a2/librt-0.11.0.tar.gz", hash = "sha256:075dc3ef4458a278e0195cbf6ac9d38808d9b906c5a6c7f7f79c3888276a3fb1", size = 200139, upload-time = "2026-05-10T18:17:25.138Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e2/1e/2ec7afcebcf3efea593d13aee18bbcfdd3a243043d848ebf385055e9f636/librt-0.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:90904fac73c478f4b83f4ed96c99c8208b75e6f9a8a1910548f69a00f1eaa671", size = 67155, upload-time = "2026-04-09T16:04:42.933Z" }, - { url = "https://files.pythonhosted.org/packages/18/77/72b85afd4435268338ad4ec6231b3da8c77363f212a0227c1ff3b45e4d35/librt-0.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:789fff71757facc0738e8d89e3b84e4f0251c1c975e85e81b152cdaca927cc2d", size = 69916, upload-time = "2026-04-09T16:04:44.042Z" }, - { url = "https://files.pythonhosted.org/packages/27/fb/948ea0204fbe2e78add6d46b48330e58d39897e425560674aee302dca81c/librt-0.9.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1bf465d1e5b0a27713862441f6467b5ab76385f4ecf8f1f3a44f8aa3c695b4b6", size = 199635, upload-time = "2026-04-09T16:04:45.5Z" }, - { url = "https://files.pythonhosted.org/packages/ac/cd/894a29e251b296a27957856804cfd21e93c194aa131de8bb8032021be07e/librt-0.9.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f819e0c6413e259a17a7c0d49f97f405abadd3c2a316a3b46c6440b7dbbedbb1", size = 211051, upload-time = "2026-04-09T16:04:47.016Z" }, - { url = "https://files.pythonhosted.org/packages/18/8f/dcaed0bc084a35f3721ff2d081158db569d2c57ea07d35623ddaca5cfc8e/librt-0.9.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e0785c2fb4a81e1aece366aa3e2e039f4a4d7d21aaaded5227d7f3c703427882", size = 224031, upload-time = "2026-04-09T16:04:48.207Z" }, - { url = "https://files.pythonhosted.org/packages/03/44/88f6c1ed1132cd418601cc041fbd92fed28b3a09f39de81978e0822d13ff/librt-0.9.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:80b25c7b570a86c03b5da69e665809deb39265476e8e21d96a9328f9762f9990", size = 218069, upload-time = "2026-04-09T16:04:50.025Z" }, - { url = "https://files.pythonhosted.org/packages/a3/90/7d02e981c2db12188d82b4410ff3e35bfdb844b26aecd02233626f46af2b/librt-0.9.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d4d16b608a1c43d7e33142099a75cd93af482dadce0bf82421e91cad077157f4", size = 224857, upload-time = "2026-04-09T16:04:51.684Z" }, - { url = "https://files.pythonhosted.org/packages/ef/c3/c77e706b7215ca32e928d47535cf13dbc3d25f096f84ddf8fbc06693e229/librt-0.9.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:194fc1a32e1e21fe809d38b5faea66cc65eaa00217c8901fbdb99866938adbdb", size = 219865, upload-time = "2026-04-09T16:04:52.949Z" }, - { url = "https://files.pythonhosted.org/packages/52/d1/32b0c1a0eb8461c70c11656c46a29f760b7c7edf3c36d6f102470c17170f/librt-0.9.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:8c6bc1384d9738781cfd41d09ad7f6e8af13cfea2c75ece6bd6d2566cdea2076", size = 218451, upload-time = "2026-04-09T16:04:54.174Z" }, - { url = "https://files.pythonhosted.org/packages/74/d1/adfd0f9c44761b1d49b1bec66173389834c33ee2bd3c7fd2e2367f1942d4/librt-0.9.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:15cb151e52a044f06e54ac7f7b47adbfc89b5c8e2b63e1175a9d587c43e8942a", size = 241300, upload-time = "2026-04-09T16:04:55.452Z" }, - { url = "https://files.pythonhosted.org/packages/09/b0/9074b64407712f0003c27f5b1d7655d1438979155f049720e8a1abd9b1a1/librt-0.9.0-cp311-cp311-win32.whl", hash = "sha256:f100bfe2acf8a3689af9d0cc660d89f17286c9c795f9f18f7b62dd1a6b247ae6", size = 55668, upload-time = "2026-04-09T16:04:56.689Z" }, - { url = "https://files.pythonhosted.org/packages/24/19/40b77b77ce80b9389fb03971431b09b6b913911c38d412059e0b3e2a9ef2/librt-0.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:0b73e4266307e51c95e09c0750b7ec383c561d2e97d58e473f6f6a209952fbb8", size = 62976, upload-time = "2026-04-09T16:04:57.733Z" }, - { url = "https://files.pythonhosted.org/packages/70/9d/9fa7a64041e29035cb8c575af5f0e3840be1b97b4c4d9061e0713f171849/librt-0.9.0-cp311-cp311-win_arm64.whl", hash = "sha256:bc5518873822d2faa8ebdd2c1a4d7c8ef47b01a058495ab7924cb65bdbf5fc9a", size = 53502, upload-time = "2026-04-09T16:04:58.806Z" }, - { url = "https://files.pythonhosted.org/packages/bf/90/89ddba8e1c20b0922783cd93ed8e64f34dc05ab59c38a9c7e313632e20ff/librt-0.9.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9b3e3bc363f71bda1639a4ee593cb78f7fbfeacc73411ec0d4c92f00730010a4", size = 68332, upload-time = "2026-04-09T16:05:00.09Z" }, - { url = "https://files.pythonhosted.org/packages/a8/40/7aa4da1fb08bdeeb540cb07bfc8207cb32c5c41642f2594dbd0098a0662d/librt-0.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0a09c2f5869649101738653a9b7ab70cf045a1105ac66cbb8f4055e61df78f2d", size = 70581, upload-time = "2026-04-09T16:05:01.213Z" }, - { url = "https://files.pythonhosted.org/packages/48/ac/73a2187e1031041e93b7e3a25aae37aa6f13b838c550f7e0f06f66766212/librt-0.9.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5ca8e133d799c948db2ab1afc081c333a825b5540475164726dcbf73537e5c2f", size = 203984, upload-time = "2026-04-09T16:05:02.542Z" }, - { url = "https://files.pythonhosted.org/packages/5e/3d/23460d571e9cbddb405b017681df04c142fb1b04cbfce77c54b08e28b108/librt-0.9.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:603138ee838ee1583f1b960b62d5d0007845c5c423feb68e44648b1359014e27", size = 215762, upload-time = "2026-04-09T16:05:04.127Z" }, - { url = "https://files.pythonhosted.org/packages/de/1e/42dc7f8ab63e65b20640d058e63e97fd3e482c1edbda3570d813b4d0b927/librt-0.9.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f4003f70c56a5addd6aa0897f200dd59afd3bf7bcd5b3cce46dd21f925743bc2", size = 230288, upload-time = "2026-04-09T16:05:05.883Z" }, - { url = "https://files.pythonhosted.org/packages/dc/08/ca812b6d8259ad9ece703397f8ad5c03af5b5fedfce64279693d3ce4087c/librt-0.9.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:78042f6facfd98ecb25e9829c7e37cce23363d9d7c83bc5f72702c5059eb082b", size = 224103, upload-time = "2026-04-09T16:05:07.148Z" }, - { url = "https://files.pythonhosted.org/packages/b6/3f/620490fb2fa66ffd44e7f900254bc110ebec8dac6c1b7514d64662570e6f/librt-0.9.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a361c9434a64d70a7dbb771d1de302c0cc9f13c0bffe1cf7e642152814b35265", size = 232122, upload-time = "2026-04-09T16:05:08.386Z" }, - { url = "https://files.pythonhosted.org/packages/e9/83/12864700a1b6a8be458cf5d05db209b0d8e94ae281e7ec261dbe616597b4/librt-0.9.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:dd2c7e082b0b92e1baa4da28163a808672485617bc855cc22a2fd06978fa9084", size = 225045, upload-time = "2026-04-09T16:05:09.707Z" }, - { url = "https://files.pythonhosted.org/packages/fd/1b/845d339c29dc7dbc87a2e992a1ba8d28d25d0e0372f9a0a2ecebde298186/librt-0.9.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:7e6274fd33fc5b2a14d41c9119629d3ff395849d8bcbc80cf637d9e8d2034da8", size = 227372, upload-time = "2026-04-09T16:05:10.942Z" }, - { url = "https://files.pythonhosted.org/packages/8d/fe/277985610269d926a64c606f761d58d3db67b956dbbf40024921e95e7fcb/librt-0.9.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5093043afb226ecfa1400120d1ebd4442b4f99977783e4f4f7248879009b227f", size = 248224, upload-time = "2026-04-09T16:05:12.254Z" }, - { url = "https://files.pythonhosted.org/packages/92/1b/ee486d244b8de6b8b5dbaefabe6bfdd4a72e08f6353edf7d16d27114da8d/librt-0.9.0-cp312-cp312-win32.whl", hash = "sha256:9edcc35d1cae9fd5320171b1a838c7da8a5c968af31e82ecc3dff30b4be0957f", size = 55986, upload-time = "2026-04-09T16:05:13.529Z" }, - { url = "https://files.pythonhosted.org/packages/89/7a/ba1737012308c17dc6d5516143b5dce9a2c7ba3474afd54e11f44a4d1ef3/librt-0.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:3cc2917258e131ae5f958a4d872e07555b51cb7466a43433218061c74ef33745", size = 63260, upload-time = "2026-04-09T16:05:14.68Z" }, - { url = "https://files.pythonhosted.org/packages/36/e4/01752c113da15127f18f7bf11142f5640038f062407a611c059d0036c6aa/librt-0.9.0-cp312-cp312-win_arm64.whl", hash = "sha256:90e6d5420fc8a300518d4d2288154ff45005e920425c22cbbfe8330f3f754bd9", size = 53694, upload-time = "2026-04-09T16:05:16.095Z" }, - { url = "https://files.pythonhosted.org/packages/5f/d7/1b3e26fffde1452d82f5666164858a81c26ebe808e7ae8c9c88628981540/librt-0.9.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f29b68cd9714531672db62cc54f6e8ff981900f824d13fa0e00749189e13778e", size = 68367, upload-time = "2026-04-09T16:05:17.243Z" }, - { url = "https://files.pythonhosted.org/packages/a5/5b/c61b043ad2e091fbe1f2d35d14795e545d0b56b03edaa390fa1dcee3d160/librt-0.9.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7d5c8a5929ac325729f6119802070b561f4db793dffc45e9ac750992a4ed4d22", size = 70595, upload-time = "2026-04-09T16:05:18.471Z" }, - { url = "https://files.pythonhosted.org/packages/a3/22/2448471196d8a73370aa2f23445455dc42712c21404081fcd7a03b9e0749/librt-0.9.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:756775d25ec8345b837ab52effee3ad2f3b2dfd6bbee3e3f029c517bd5d8f05a", size = 204354, upload-time = "2026-04-09T16:05:19.593Z" }, - { url = "https://files.pythonhosted.org/packages/ac/5e/39fc4b153c78cfd2c8a2dcb32700f2d41d2312aa1050513183be4540930d/librt-0.9.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2b8f5d00b49818f4e2b1667db994488b045835e0ac16fe2f924f3871bd2b8ac5", size = 216238, upload-time = "2026-04-09T16:05:20.868Z" }, - { url = "https://files.pythonhosted.org/packages/d7/42/bc2d02d0fa7badfa63aa8d6dcd8793a9f7ef5a94396801684a51ed8d8287/librt-0.9.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c81aef782380f0f13ead670aae01825eb653b44b046aa0e5ebbb79f76ed4aa11", size = 230589, upload-time = "2026-04-09T16:05:22.305Z" }, - { url = "https://files.pythonhosted.org/packages/c8/7b/e2d95cc513866373692aa5edf98080d5602dd07cabfb9e5d2f70df2f25f7/librt-0.9.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:66b58fed90a545328e80d575467244de3741e088c1af928f0b489ebec3ef3858", size = 224610, upload-time = "2026-04-09T16:05:23.647Z" }, - { url = "https://files.pythonhosted.org/packages/31/d5/6cec4607e998eaba57564d06a1295c21b0a0c8de76e4e74d699e627bd98c/librt-0.9.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e78fb7419e07d98c2af4b8567b72b3eaf8cb05caad642e9963465569c8b2d87e", size = 232558, upload-time = "2026-04-09T16:05:25.025Z" }, - { url = "https://files.pythonhosted.org/packages/95/8c/27f1d8d3aaf079d3eb26439bf0b32f1482340c3552e324f7db9dca858671/librt-0.9.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2c3786f0f4490a5cd87f1ed6cefae833ad6b1060d52044ce0434a2e85893afd0", size = 225521, upload-time = "2026-04-09T16:05:26.311Z" }, - { url = "https://files.pythonhosted.org/packages/6b/d8/1e0d43b1c329b416017619469b3c3801a25a6a4ef4a1c68332aeaa6f72ca/librt-0.9.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:8494cfc61e03542f2d381e71804990b3931175a29b9278fdb4a5459948778dc2", size = 227789, upload-time = "2026-04-09T16:05:27.624Z" }, - { url = "https://files.pythonhosted.org/packages/2c/b4/d3d842e88610fcd4c8eec7067b0c23ef2d7d3bff31496eded6a83b0f99be/librt-0.9.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:07cf11f769831186eeac424376e6189f20ace4f7263e2134bdb9757340d84d4d", size = 248616, upload-time = "2026-04-09T16:05:29.181Z" }, - { url = "https://files.pythonhosted.org/packages/ec/28/527df8ad0d1eb6c8bdfa82fc190f1f7c4cca5a1b6d7b36aeabf95b52d74d/librt-0.9.0-cp313-cp313-win32.whl", hash = "sha256:850d6d03177e52700af605fd60db7f37dcb89782049a149674d1a9649c2138fd", size = 56039, upload-time = "2026-04-09T16:05:30.709Z" }, - { url = "https://files.pythonhosted.org/packages/f3/a7/413652ad0d92273ee5e30c000fc494b361171177c83e57c060ecd3c21538/librt-0.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:a5af136bfba820d592f86c67affcef9b3ff4d4360ac3255e341e964489b48519", size = 63264, upload-time = "2026-04-09T16:05:31.881Z" }, - { url = "https://files.pythonhosted.org/packages/a4/0a/92c244309b774e290ddb15e93363846ae7aa753d9586b8aad511c5e6145b/librt-0.9.0-cp313-cp313-win_arm64.whl", hash = "sha256:4c4d0440a3a8e31d962340c3e1cc3fc9ee7febd34c8d8f770d06adb947779ea5", size = 53728, upload-time = "2026-04-09T16:05:33.31Z" }, - { url = "https://files.pythonhosted.org/packages/cd/c1/184e539543f06ea2912f4b92a5ffaede4f9b392689e3f00acbf8134bee92/librt-0.9.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:3f05d145df35dca5056a8bc3838e940efebd893a54b3e19b2dda39ceaa299bcb", size = 67830, upload-time = "2026-04-09T16:05:34.517Z" }, - { url = "https://files.pythonhosted.org/packages/f3/ad/23399bdcb7afca819acacdef31b37ee59de261bd66b503a7995c03c4b0dc/librt-0.9.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1c587494461ebd42229d0f1739f3aa34237dd9980623ecf1be8d3bcba79f4499", size = 70280, upload-time = "2026-04-09T16:05:35.649Z" }, - { url = "https://files.pythonhosted.org/packages/9f/0b/4542dc5a2b8772dbf92cafb9194701230157e73c14b017b6961a23598b03/librt-0.9.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:b0a2040f801406b93657a70b72fa12311063a319fee72ce98e1524da7200171f", size = 201925, upload-time = "2026-04-09T16:05:36.739Z" }, - { url = "https://files.pythonhosted.org/packages/31/d4/8ee7358b08fd0cfce051ef96695380f09b3c2c11b77c9bfbc367c921cce5/librt-0.9.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f38bc489037eca88d6ebefc9c4d41a4e07c8e8b4de5188a9e6d290273ad7ebb1", size = 212381, upload-time = "2026-04-09T16:05:38.043Z" }, - { url = "https://files.pythonhosted.org/packages/f2/94/a2025fe442abedf8b038038dab3dba942009ad42b38ea064a1a9e6094241/librt-0.9.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f3fd278f5e6bf7c75ccd6d12344eb686cc020712683363b66f46ac79d37c799f", size = 227065, upload-time = "2026-04-09T16:05:39.394Z" }, - { url = "https://files.pythonhosted.org/packages/7c/e9/b9fcf6afa909f957cfbbf918802f9dada1bd5d3c1da43d722fd6a310dc3f/librt-0.9.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fcbdf2a9ca24e87bbebb47f1fe34e531ef06f104f98c9ccfc953a3f3344c567a", size = 221333, upload-time = "2026-04-09T16:05:40.999Z" }, - { url = "https://files.pythonhosted.org/packages/ac/7c/ba54cd6aa6a3c8cd12757a6870e0c79a64b1e6327f5248dcff98423f4d43/librt-0.9.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e306d956cfa027fe041585f02a1602c32bfa6bb8ebea4899d373383295a6c62f", size = 229051, upload-time = "2026-04-09T16:05:42.605Z" }, - { url = "https://files.pythonhosted.org/packages/4b/4b/8cfdbad314c8677a0148bf0b70591d6d18587f9884d930276098a235461b/librt-0.9.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:465814ab157986acb9dfa5ccd7df944be5eefc0d08d31ec6e8d88bc71251d845", size = 222492, upload-time = "2026-04-09T16:05:43.842Z" }, - { url = "https://files.pythonhosted.org/packages/1f/d1/2eda69563a1a88706808decdce035e4b32755dbfbb0d05e1a65db9547ed1/librt-0.9.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:703f4ae36d6240bfe24f542bac784c7e4194ec49c3ba5a994d02891649e2d85b", size = 223849, upload-time = "2026-04-09T16:05:45.054Z" }, - { url = "https://files.pythonhosted.org/packages/04/44/b2ed37df6be5b3d42cfe36318e0598e80843d5c6308dd63d0bf4e0ce5028/librt-0.9.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:3be322a15ee5e70b93b7a59cfd074614f22cc8c9ff18bd27f474e79137ea8d3b", size = 245001, upload-time = "2026-04-09T16:05:46.34Z" }, - { url = "https://files.pythonhosted.org/packages/47/e7/617e412426df89169dd2a9ed0cc8752d5763336252c65dbf945199915119/librt-0.9.0-cp314-cp314-win32.whl", hash = "sha256:b8da9f8035bb417770b1e1610526d87ad4fc58a2804dc4d79c53f6d2cf5a6eb9", size = 51799, upload-time = "2026-04-09T16:05:47.738Z" }, - { url = "https://files.pythonhosted.org/packages/24/ed/c22ca4db0ca3cbc285e4d9206108746beda561a9792289c3c31281d7e9df/librt-0.9.0-cp314-cp314-win_amd64.whl", hash = "sha256:b8bd70d5d816566a580d193326912f4a76ec2d28a97dc4cd4cc831c0af8e330e", size = 59165, upload-time = "2026-04-09T16:05:49.198Z" }, - { url = "https://files.pythonhosted.org/packages/24/56/875398fafa4cbc8f15b89366fc3287304ddd3314d861f182a4b87595ace0/librt-0.9.0-cp314-cp314-win_arm64.whl", hash = "sha256:fc5758e2b7a56532dc33e3c544d78cbaa9ecf0a0f2a2da2df882c1d6b99a317f", size = 49292, upload-time = "2026-04-09T16:05:50.362Z" }, - { url = "https://files.pythonhosted.org/packages/4c/61/bc448ecbf9b2d69c5cff88fe41496b19ab2a1cbda0065e47d4d0d51c0867/librt-0.9.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:f24b90b0e0c8cc9491fb1693ae91fe17cb7963153a1946395acdbdd5818429a4", size = 70175, upload-time = "2026-04-09T16:05:51.564Z" }, - { url = "https://files.pythonhosted.org/packages/60/f2/c47bb71069a73e2f04e70acbd196c1e5cc411578ac99039a224b98920fd4/librt-0.9.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3fe56e80badb66fdcde06bef81bbaa5bfcf6fbd7aefb86222d9e369c38c6b228", size = 72951, upload-time = "2026-04-09T16:05:52.699Z" }, - { url = "https://files.pythonhosted.org/packages/29/19/0549df59060631732df758e8886d92088da5fdbedb35b80e4643664e8412/librt-0.9.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:527b5b820b47a09e09829051452bb0d1dd2122261254e2a6f674d12f1d793d54", size = 225864, upload-time = "2026-04-09T16:05:53.895Z" }, - { url = "https://files.pythonhosted.org/packages/9d/f8/3b144396d302ac08e50f89e64452c38db84bc7b23f6c60479c5d3abd303c/librt-0.9.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7d429bdd4ac0ab17c8e4a8af0ed2a7440b16eba474909ab357131018fe8c7e71", size = 241155, upload-time = "2026-04-09T16:05:55.191Z" }, - { url = "https://files.pythonhosted.org/packages/7a/ce/ee67ec14581de4043e61d05786d2aed6c9b5338816b7859bcf07455c6a9f/librt-0.9.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7202bdcac47d3a708271c4304a474a8605a4a9a4a709e954bf2d3241140aa938", size = 252235, upload-time = "2026-04-09T16:05:56.549Z" }, - { url = "https://files.pythonhosted.org/packages/8a/fa/0ead15daa2b293a54101550b08d4bafe387b7d4a9fc6d2b985602bae69b6/librt-0.9.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0d620e74897f8c2613b3c4e2e9c1e422eb46d2ddd07df540784d44117836af3", size = 244963, upload-time = "2026-04-09T16:05:57.858Z" }, - { url = "https://files.pythonhosted.org/packages/29/68/9fbf9a9aa704ba87689e40017e720aced8d9a4d2b46b82451d8142f91ec9/librt-0.9.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d69fc39e627908f4c03297d5a88d9284b73f4d90b424461e32e8c2485e21c283", size = 257364, upload-time = "2026-04-09T16:05:59.686Z" }, - { url = "https://files.pythonhosted.org/packages/1a/8d/9d60869f1b6716c762e45f66ed945b1e5dd649f7377684c3b176ae424648/librt-0.9.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:c2640e23d2b7c98796f123ffd95cf2022c7777aa8a4a3b98b36c570d37e85eee", size = 247661, upload-time = "2026-04-09T16:06:00.938Z" }, - { url = "https://files.pythonhosted.org/packages/70/ff/a5c365093962310bfdb4f6af256f191085078ffb529b3f0cbebb5b33ebe2/librt-0.9.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:451daa98463b7695b0a30aa56bf637831ea559e7b8101ac2ef6382e8eb15e29c", size = 248238, upload-time = "2026-04-09T16:06:02.537Z" }, - { url = "https://files.pythonhosted.org/packages/a0/3c/2d34365177f412c9e19c0a29f969d70f5343f27634b76b765a54d8b27705/librt-0.9.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:928bd06eca2c2bbf4349e5b817f837509b0604342e65a502de1d50a7570afd15", size = 269457, upload-time = "2026-04-09T16:06:03.833Z" }, - { url = "https://files.pythonhosted.org/packages/bc/cd/de45b239ea3bdf626f982a00c14bfcf2e12d261c510ba7db62c5969a27cd/librt-0.9.0-cp314-cp314t-win32.whl", hash = "sha256:a9c63e04d003bc0fb6a03b348018b9a3002f98268200e22cc80f146beac5dc40", size = 52453, upload-time = "2026-04-09T16:06:05.229Z" }, - { url = "https://files.pythonhosted.org/packages/7f/f9/bfb32ae428aa75c0c533915622176f0a17d6da7b72b5a3c6363685914f70/librt-0.9.0-cp314-cp314t-win_amd64.whl", hash = "sha256:f162af66a2ed3f7d1d161a82ca584efd15acd9c1cff190a373458c32f7d42118", size = 60044, upload-time = "2026-04-09T16:06:06.398Z" }, - { url = "https://files.pythonhosted.org/packages/aa/47/7d70414bcdbb3bc1f458a8d10558f00bbfdb24e5a11740fc8197e12c3255/librt-0.9.0-cp314-cp314t-win_arm64.whl", hash = "sha256:a4b25c6c25cac5d0d9d6d6da855195b254e0021e513e0249f0e3b444dc6e0e61", size = 50009, upload-time = "2026-04-09T16:06:07.995Z" }, + { url = "https://files.pythonhosted.org/packages/fe/87/2bf31fe17587b29e3f93ec31421e2b1e1c3e349b8bf6c7c313dbad1d5340/librt-0.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:93d95bd45b7d58343d8b90d904450a545144eec19a002511163426f8ab1fae29", size = 141092, upload-time = "2026-05-10T18:15:34.795Z" }, + { url = "https://files.pythonhosted.org/packages/cf/08/5c5bf772920b7ebac6e32bc91a643e0ab3870199c0b542356d3baa83970a/librt-0.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4ee278c769a713638cdacd4c0436d72156e75df3ebc0166ab2b9dc43acc386c9", size = 142035, upload-time = "2026-05-10T18:15:36.242Z" }, + { url = "https://files.pythonhosted.org/packages/06/20/662a03d254e5b000d838e8b345d83303ddb768c080fd488e40634c0fa66b/librt-0.11.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f230cb1cbc9faaa616f9a678f530ebcf186e414b6bcbd88b960e4ba1b92428d5", size = 475022, upload-time = "2026-05-10T18:15:37.56Z" }, + { url = "https://files.pythonhosted.org/packages/de/f3/aa81523e45184c6ec23dc7f63263362ec55f80a09d424c012359ecbe7e35/librt-0.11.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:5d63c855d86938d9de93e265c9bd8c705b51ec494de5738340ee93767a686e4b", size = 467273, upload-time = "2026-05-10T18:15:39.182Z" }, + { url = "https://files.pythonhosted.org/packages/6b/6f/59c74b560ca8853834d5501d589c8a2519f4184f273a085ffd0f37a1cc47/librt-0.11.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:993f028be9e96a08d31df3479ac80d99be374d17f3b78e4796b3fd3c913d4e89", size = 497083, upload-time = "2026-05-10T18:15:40.634Z" }, + { url = "https://files.pythonhosted.org/packages/fe/7b/5aa4d2c9600a719401160bf7055417df0b2a47439b9d88286ce45e56b65f/librt-0.11.0-cp311-cp311-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:258d73a0aa66a055e65b2e4d1b8cdb23b9d132c5bb915d9547d804fcaed116cc", size = 489139, upload-time = "2026-05-10T18:15:41.934Z" }, + { url = "https://files.pythonhosted.org/packages/d6/31/9143803d7da6856a69153785768c4936864430eec0fd9461c3ea527d9922/librt-0.11.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0827efe7854718f04aaddf6496e96960a956e676fe1d0f04eb41511fd8ad06d5", size = 508442, upload-time = "2026-05-10T18:15:43.206Z" }, + { url = "https://files.pythonhosted.org/packages/2f/5a/bce08184488426bda4ccc2c4964ac048c8f68ae89bd7120082eef4233cfd/librt-0.11.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7753e57d6e12d019c0d8786f1c09c709f4c3fcc57c3887b24e36e6c06ec938b7", size = 514230, upload-time = "2026-05-10T18:15:44.761Z" }, + { url = "https://files.pythonhosted.org/packages/89/8c/bb5e213d254b7505a0e658da199d8ab719086632ce09eef311ab27976523/librt-0.11.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:11bd19822431cc21af9f27374e7ae2e58103c7d98bda823536a6c47f6bb2bb3d", size = 494231, upload-time = "2026-05-10T18:15:46.308Z" }, + { url = "https://files.pythonhosted.org/packages/9d/fb/541cdad5b1ab1300398c74c4c9a497b88e5074c21b1244c8f49731d3a284/librt-0.11.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:22bdf239b219d3993761a148ffa134b19e52e9989c84f845d5d7b71d70a17412", size = 537585, upload-time = "2026-05-10T18:15:47.629Z" }, + { url = "https://files.pythonhosted.org/packages/8f/f2/464bb69295c320cb06bddb4f14a4ec67934ee14b2bffb12b19fb7ab287ba/librt-0.11.0-cp311-cp311-win32.whl", hash = "sha256:46c60b61e308eb535fbd6fa622b1ee1bb2815691c1ad9c98bf7b84952ec3bc8d", size = 100509, upload-time = "2026-05-10T18:15:49.157Z" }, + { url = "https://files.pythonhosted.org/packages/6d/e7/a17ee1788f9e4fbf548c19f4afa07c92089b9e24fef6cb2410863781ef4c/librt-0.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:902e546ff044f579ff1c953ff5fce97b636fe9e3943996b2177710c6ef076f73", size = 118628, upload-time = "2026-05-10T18:15:50.345Z" }, + { url = "https://files.pythonhosted.org/packages/cc/c7/6c766214f9f9903bcfcfbef97d807af8d8f5aa3502d247858ab17582d212/librt-0.11.0-cp311-cp311-win_arm64.whl", hash = "sha256:65ac3bc20f78aa0ee5ae84baa68917f89fef4af63e941084dd019a0d0e749f0c", size = 103122, upload-time = "2026-05-10T18:15:52.068Z" }, + { url = "https://files.pythonhosted.org/packages/8b/d0/07c77e067f0838949b43bd89232c29d72efebb9d2801a9750184eb706b71/librt-0.11.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b87504f1690a23b9a2cca841191a04f83895d4fc2dd04df91d82b1a04ca2ad46", size = 144147, upload-time = "2026-05-10T18:15:53.227Z" }, + { url = "https://files.pythonhosted.org/packages/7a/24/8493538fa4f62f982686398a5b8f68008138a75086abdea19ade64bf4255/librt-0.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40071fc5fe0ce8daa6de616702314a01e1250711682b0523d6ab8d4525910cb3", size = 143614, upload-time = "2026-05-10T18:15:54.657Z" }, + { url = "https://files.pythonhosted.org/packages/ff/1e/f8bad050810d9171f34a1648ed910e56814c2ba61639f2bd53c6377ae24b/librt-0.11.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:137e79445c896a0ea7b265f52d23954e05b64222ee1af69e2cb34219067cbb67", size = 485538, upload-time = "2026-05-10T18:15:56.117Z" }, + { url = "https://files.pythonhosted.org/packages/c0/fe/3594ebfbaf03084ba4b120c9ba5c3183fd938a48725e9bbe6ff0a5159ad8/librt-0.11.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:cca6644054e78746d8d4ef238681f9c34ff8b584fe6b988ecebb8db3b15e622a", size = 479623, upload-time = "2026-05-10T18:15:57.544Z" }, + { url = "https://files.pythonhosted.org/packages/b0/da/5d1876984b3746c85dbd219dbfcb73c85f54ee263fd32e5b2a632ec14571/librt-0.11.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d5b0eea49f5562861ee8d757a32ef7d559c1d35be2aaaa1ec28941d74c9ffc8a", size = 513082, upload-time = "2026-05-10T18:15:58.805Z" }, + { url = "https://files.pythonhosted.org/packages/19/6e/55bdf5d5ca00c3e18430690bf2c953d8d3ffd3c337418173d33dec985dc9/librt-0.11.0-cp312-cp312-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0d1029d7e1ae1a7e647ed6fb5df8c4ce2dffefb7a9f5fd1376a4554d96dac09f", size = 508105, upload-time = "2026-05-10T18:16:00.2Z" }, + { url = "https://files.pythonhosted.org/packages/07/10/f1f23a7c595ee90ece4d35c851e5d104b1311a887ed1b4ac4c35bbd13da8/librt-0.11.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bc3ce6b33c5828d9e80592011a5c584cb2ce86edbc4088405f70da47dc1d1b3b", size = 522268, upload-time = "2026-05-10T18:16:01.708Z" }, + { url = "https://files.pythonhosted.org/packages/b6/02/5720f5697a7f54b78b3aefbe20df3a48cedcff1276618c4aa481177942ed/librt-0.11.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:936c5995f3514a42111f20099397d8177c79b4d7e70961e396c6f5a0a3566766", size = 527348, upload-time = "2026-05-10T18:16:03.496Z" }, + { url = "https://files.pythonhosted.org/packages/50/db/b4a47c6f91db4ff76348a0b3dd0cc65e090a078b765a810a62ff9434c3d3/librt-0.11.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:9bc0ca6ad9381cbe8e4aa6e5726e4c80c78115a6e9723c599ed1d73e092bc49d", size = 516294, upload-time = "2026-05-10T18:16:05.173Z" }, + { url = "https://files.pythonhosted.org/packages/9e/58/9384b2f4eb1ed1d273d40948a7c5c4b2360213b402ef3be4641c06299f9c/librt-0.11.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:070aa8c26c0a74774317a72df8851facc7f0f012a5b406557ac56992d92e1ec8", size = 553608, upload-time = "2026-05-10T18:16:06.839Z" }, + { url = "https://files.pythonhosted.org/packages/21/7b/5aa8848a7c6a9278c79375146da1812e695754ceec5f005e6043461a7315/librt-0.11.0-cp312-cp312-win32.whl", hash = "sha256:6bf14feb84b05ae945277395451998c89c54d0def4070eb5c08de544930b245a", size = 101879, upload-time = "2026-05-10T18:16:08.103Z" }, + { url = "https://files.pythonhosted.org/packages/37/33/8a745436944947575b584231750a41417de1a38cf6a2e9251d1065651c09/librt-0.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:75672f0bc524ede266287d532d7923dbce94c7514ad07627bac3d0c6d92cc4d9", size = 119831, upload-time = "2026-05-10T18:16:09.174Z" }, + { url = "https://files.pythonhosted.org/packages/59/67/a6739ac96e28b7855808bdb0370e250606104a859750d209e5a0716fe7ab/librt-0.11.0-cp312-cp312-win_arm64.whl", hash = "sha256:2f10cf143e4a9bb0f4f5af568a00df94a2d69ef41c2579584454bb0fe5cc642c", size = 103470, upload-time = "2026-05-10T18:16:10.369Z" }, + { url = "https://files.pythonhosted.org/packages/82/61/e59168d4d0bf2bf90f4f0caf7a001bfc60254c3af4586013b04dc3ef517b/librt-0.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:78dc31f7fdfe9c9d0eb0e8f42d139db230e826415bbcabd9f0e9faaaee909894", size = 144119, upload-time = "2026-05-10T18:16:11.771Z" }, + { url = "https://files.pythonhosted.org/packages/61/fd/caa1d60b12f7dd79ccea23054e06eeaebe266a5f52c40a6b651069200ce5/librt-0.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:fa475675db22290c3158e1d42326d0f5a65f04f44a0e68c3630a25b53560fb9c", size = 143565, upload-time = "2026-05-10T18:16:13.334Z" }, + { url = "https://files.pythonhosted.org/packages/b8/a9/dc744f5c2b4978d48db970be29f22716d3413d28b14ad99740817315cf2c/librt-0.11.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:621db29691044bdeda22e789e482e1b0f3a985d90e3426c9c6d17606416205ea", size = 485395, upload-time = "2026-05-10T18:16:14.729Z" }, + { url = "https://files.pythonhosted.org/packages/8f/21/7f8e97a1e4dae952a5a95948f6f8507a173bc1e669f54340bba6ca1ca31b/librt-0.11.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:a9010e2ed5b3a9e158c5fd966b3ab7e834bb3d3aacc8f66c91dd4b57a3799230", size = 479383, upload-time = "2026-05-10T18:16:16.321Z" }, + { url = "https://files.pythonhosted.org/packages/a6/6d/d8ee9c114bebf2c50e29ec2aa940826fccb62a645c3e4c18760987d0e16d/librt-0.11.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7c39513d8b7477a2e1ed8c43fc21c524e8d5a0f8d4e8b7b074dbdbe7820a08e2", size = 513010, upload-time = "2026-05-10T18:16:17.647Z" }, + { url = "https://files.pythonhosted.org/packages/f0/43/0b5708af2bd30a46400e72ba6bdaa8f066f15fb9a688527e34220e8d6c06/librt-0.11.0-cp313-cp313-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7aef3cf1d5af86e770ab04bfd993dfc4ae8b8c17f66fb77dd4a7d50de7bbb1a3", size = 508433, upload-time = "2026-05-10T18:16:19.309Z" }, + { url = "https://files.pythonhosted.org/packages/4a/50/356187247d09013490481033183b3532b58acf8028bcb34b2b56a375c9b2/librt-0.11.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:557183ddc36babe46b27dd60facbd5adb4492181a5be887587d57cda6e092f21", size = 522595, upload-time = "2026-05-10T18:16:20.642Z" }, + { url = "https://files.pythonhosted.org/packages/40/e7/c6ac4240899c7f3248079d5a9900debe0dadb3fdeaf856684c987105ba47/librt-0.11.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:83d3e1f72bd42f6c5c0b7daec530c3f829bd02db42c70b8ddf0c2d90a2459930", size = 527255, upload-time = "2026-05-10T18:16:22.352Z" }, + { url = "https://files.pythonhosted.org/packages/eb/b5/a81322dbeedeeaf9c1ee6f001734d28a09d8383ac9e6779bc24bbd0743c6/librt-0.11.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:4ce1f21fbe589bc1afd7872dece84fb0e1144f794a288e58a10d2c54a55c43be", size = 516847, upload-time = "2026-05-10T18:16:23.627Z" }, + { url = "https://files.pythonhosted.org/packages/ae/66/6e6323787d592b55204a42595ff1102da5115601b53a7e9ddebc889a6da5/librt-0.11.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:970b09f7044ea2b64c9da42fd3d335666518cfd1c6e8a182c95da73d0214b41e", size = 553920, upload-time = "2026-05-10T18:16:25.025Z" }, + { url = "https://files.pythonhosted.org/packages/9c/21/623f8ca230857102066d9ca8c6c1734995908c4d0d1bee7bb2ef0021cb33/librt-0.11.0-cp313-cp313-win32.whl", hash = "sha256:78fddc31cd4d3caa897ad5d31f856b1faadc9474021ad6cb182b9018793e254e", size = 101898, upload-time = "2026-05-10T18:16:26.649Z" }, + { url = "https://files.pythonhosted.org/packages/b3/1d/b4ebd44dd723f768469007515cb92251e0ae286c94c140f374801140fa74/librt-0.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:8ca8aa88751a775870b764e93bad5135385f563cb8dcee399abf034ea4d3cb47", size = 119812, upload-time = "2026-05-10T18:16:27.859Z" }, + { url = "https://files.pythonhosted.org/packages/3b/e4/b2f4ca7965ca373b491cdb4bc25cdb30c1649ca81a8782056a83850292a9/librt-0.11.0-cp313-cp313-win_arm64.whl", hash = "sha256:96f044bb325fd9cf1a723015638c219e9143f0dfbc0ca54c565df2b7fc748b44", size = 103448, upload-time = "2026-05-10T18:16:29.066Z" }, + { url = "https://files.pythonhosted.org/packages/29/eb/dbce197da4e227779e56b5735f2decc3eb36e55a1cdbf1bd65d6639d76c1/librt-0.11.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:4a017a95e5837dc15a8c5661d60e05daa96b90908b1aa6b7acdf443cd25c8ebd", size = 143345, upload-time = "2026-05-10T18:16:30.674Z" }, + { url = "https://files.pythonhosted.org/packages/76/a3/254bebd0c11c8ba684018efb8006ff22e466abce445215cca6c778e7d9de/librt-0.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b1ecbd9819deccc39b7542bf4d2a740d8a620694d39989e58661d3763458f8d4", size = 143131, upload-time = "2026-05-10T18:16:32.037Z" }, + { url = "https://files.pythonhosted.org/packages/f1/3f/f77d6122d21ac7bf6ae8a7dfced1bd2a7ac545d3273ebdcaf8042f6d619f/librt-0.11.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7da327dacd7be8f8ec36547373550744a3cc0e536d54665cd83f8bcd961200e8", size = 477024, upload-time = "2026-05-10T18:16:33.493Z" }, + { url = "https://files.pythonhosted.org/packages/ac/0a/2c996dadebaa7d9bbbd43ef2d4f3e66b6da545f838a41694ef6172cebec8/librt-0.11.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:0dc56b1f8d06e60db362cc3fdae206681817f86ce4725d34511473487f12a34b", size = 474221, upload-time = "2026-05-10T18:16:34.864Z" }, + { url = "https://files.pythonhosted.org/packages/0a/7e/f5d92af8486b8272c23b3e686b46ff72d89c8169585eb61eef01a2ac7147/librt-0.11.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:05fb8fb2ab90e21c8d12ea240d744ad514da9baf381ebfa70d91d20d21713175", size = 505174, upload-time = "2026-05-10T18:16:36.705Z" }, + { url = "https://files.pythonhosted.org/packages/af/1a/cb0734fe86398eb33193ab753b7326255c74cac5eb09e76b9b16536e7adb/librt-0.11.0-cp314-cp314-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cae74872be221df4374d10fec61f93ed1513b9546ea84f2c0bf73ab3e9bd0b03", size = 497216, upload-time = "2026-05-10T18:16:38.418Z" }, + { url = "https://files.pythonhosted.org/packages/18/06/094820f91558b66e29943c0ec41c9914f460f48dd51fc503c3101e10842d/librt-0.11.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:32bcc918c0148eb7e3d57385125bac7e5f9e4359d05f07448b09f6f778c2f31c", size = 513921, upload-time = "2026-05-10T18:16:39.848Z" }, + { url = "https://files.pythonhosted.org/packages/0b/c2/00de9018871a282f530cacb457d5ec0428f6ac7e6fedde9aff7468d9fb04/librt-0.11.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:f9743fc99135d5f78d2454435615f6dec0473ca507c26ce9d92b10b562a280d3", size = 520850, upload-time = "2026-05-10T18:16:41.471Z" }, + { url = "https://files.pythonhosted.org/packages/51/9d/64631832348fd1834fb3a61b996434edddaaf25a31d03b0a76273159d2cf/librt-0.11.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:5ba067f4aadae8fda802d91d2124c90c42195ff32d9161d3549e6d05cfe26f96", size = 504237, upload-time = "2026-05-10T18:16:43.15Z" }, + { url = "https://files.pythonhosted.org/packages/a5/ec/ae5525eb16edc827a044e7bb8777a455ff95d4bca9379e7e6bddd7383647/librt-0.11.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:de3bf945454d032f9e390b85c4072e0a0570bf825421c8be0e71209fa65e1abe", size = 546261, upload-time = "2026-05-10T18:16:44.408Z" }, + { url = "https://files.pythonhosted.org/packages/5a/09/adce371f27ca039411da9659f7430fcc2ba6cd0c7b3e4467a0f091be7fa9/librt-0.11.0-cp314-cp314-win32.whl", hash = "sha256:d2277a05f6dcb9fd13db9566aac4fabd68c3ea1ea46ee5567d4eef8efa495a2f", size = 96965, upload-time = "2026-05-10T18:16:46.039Z" }, + { url = "https://files.pythonhosted.org/packages/d6/ee/8ac720d98548f173c7ce2e632a7ca94673f74cacd5c8162a84af5b35958a/librt-0.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:ab73e8db5e3f564d812c1f5c3a175930a5f9bc96ccb5e3b22a34d7858b401cf7", size = 115151, upload-time = "2026-05-10T18:16:47.133Z" }, + { url = "https://files.pythonhosted.org/packages/94/20/c900cf14efeb09b6bef2b2dff20779f73464b97fd58d1c6bccc379588ae3/librt-0.11.0-cp314-cp314-win_arm64.whl", hash = "sha256:aea3caa317752e3a466fa8af45d91ee0ea8c7fdd96e42b0a8dd9b76a7931eba1", size = 98850, upload-time = "2026-05-10T18:16:48.597Z" }, + { url = "https://files.pythonhosted.org/packages/0c/71/944bfe4b64e12abffcd3c15e1cce07f72f3d55655083786285f4dedeb532/librt-0.11.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d1b36540d7aaf9b9101b3a6f376c8d8e9f7a9aec93ed05918f2c69d493ffef72", size = 151138, upload-time = "2026-05-10T18:16:49.839Z" }, + { url = "https://files.pythonhosted.org/packages/b6/10/99e64a5c86989357fda078c8143c533389585f6473b7439172dd8f3b3b2d/librt-0.11.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:efbb343ab2ce3540f4ecbe6315d677ed70f37cd9a72b1e58066c918ca83acbaa", size = 151976, upload-time = "2026-05-10T18:16:51.062Z" }, + { url = "https://files.pythonhosted.org/packages/21/31/5072ad880946d83e5ea4147d6d018c78eefce85b77819b19bdd0ee229435/librt-0.11.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa0dd688aab3f7914d3e6e5e3554978e0383312fb8e771d84be008a35b9ee548", size = 557927, upload-time = "2026-05-10T18:16:52.632Z" }, + { url = "https://files.pythonhosted.org/packages/5e/8d/70b5fb7cfbab60edbe7381614ab985da58e144fbf465c86d44c95f43cdca/librt-0.11.0-cp314-cp314t-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:f5fb36b8c6c63fdcbb1d526d94c0d1331610d43f4118cc1beb4efef4f3faacb2", size = 539698, upload-time = "2026-05-10T18:16:53.934Z" }, + { url = "https://files.pythonhosted.org/packages/fa/a3/ba3495a0b3edbd24a4cae0d1d3c64f39a9fc45d06e812101289b50c1a619/librt-0.11.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4a9a237d13addb93715b6fee74023d5ee3469b53fce527626c0e088aa585805f", size = 577162, upload-time = "2026-05-10T18:16:55.589Z" }, + { url = "https://files.pythonhosted.org/packages/f7/db/36e25fb81f99937ff1b96612a1dc9fd66f039cb9cc3aee12c01fac31aab9/librt-0.11.0-cp314-cp314t-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5ddd17bd87b2c56ddd60e546a7984a2e64c4e8eab92fb4cf3830a48ad5469d51", size = 566494, upload-time = "2026-05-10T18:16:56.975Z" }, + { url = "https://files.pythonhosted.org/packages/33/0d/3f622b47f0b013eeb9cf4cc07ae9bfe378d832a4eec998b2b209fe84244d/librt-0.11.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bd43992b4473d42f12ff9e68326079f0696d9d4e6000e8f39a0238d482ba6ee2", size = 596858, upload-time = "2026-05-10T18:16:58.374Z" }, + { url = "https://files.pythonhosted.org/packages/a9/02/71b90bc93039c46a2000651f6ad60122b114c8f54c4ad306e0e96f5b75ad/librt-0.11.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:f8e3e8056dd674e279741485e2e512d6e9a751c7455809d0114e6ebf8d781085", size = 590318, upload-time = "2026-05-10T18:16:59.676Z" }, + { url = "https://files.pythonhosted.org/packages/04/04/418cb3f75621e2b761fb1ab0f017f4d70a1a72a6e7c74ee4f7e8d198c2f3/librt-0.11.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:c1f708d8ae9c56cf38a903c44297243d2ec83fd82b396b977e0144a3e76217e3", size = 575115, upload-time = "2026-05-10T18:17:01.007Z" }, + { url = "https://files.pythonhosted.org/packages/cc/2c/5a2183ac58dd911f26b5d7e7d7d8f1d87fcecdddd99d6c12169a258ff62c/librt-0.11.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0add982e0e7b9fc14cf4b33789d5f13f66581889b88c2f58099f6ce8f92617bd", size = 617918, upload-time = "2026-05-10T18:17:02.682Z" }, + { url = "https://files.pythonhosted.org/packages/15/1f/dc6771a52592a4451be6effa200cbfc9cec61e4393d3033d81a9d307961d/librt-0.11.0-cp314-cp314t-win32.whl", hash = "sha256:2b481d846ac894c4e8403c5fd0e87c5d11d6499e404b474602508a224ff531c8", size = 103562, upload-time = "2026-05-10T18:17:03.99Z" }, + { url = "https://files.pythonhosted.org/packages/62/4a/7d1415567027286a75ba1093ec4aca11f073e0f559c530cf3e0a757ad55c/librt-0.11.0-cp314-cp314t-win_amd64.whl", hash = "sha256:28edb433edde181112a908c78907af28f964eabc15f4dd16c9d66c834302677c", size = 124327, upload-time = "2026-05-10T18:17:05.465Z" }, + { url = "https://files.pythonhosted.org/packages/ce/62/b40b382fa0c66fee1478073eb8db352a4a6beda4a1adccf1df911d8c289c/librt-0.11.0-cp314-cp314t-win_arm64.whl", hash = "sha256:dee008f20b542e3cd162ba338a7f9ec0f6d23d395f66fe8aeeec3c9d067ea253", size = 102572, upload-time = "2026-05-10T18:17:06.809Z" }, ] [[package]] name = "locust" -version = "2.43.4" +version = "2.44.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "configargparse" }, @@ -1271,9 +1315,9 @@ dependencies = [ { name = "typing-extensions", marker = "python_full_version < '3.12'" }, { name = "werkzeug" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/52/be/6df1c778f673e1e2d785f262d20a4e130fdb8e51242466d7ae434b66a587/locust-2.43.4.tar.gz", hash = "sha256:4ace60f07f5fa9bf08d1b64da25915707befca19a790897eed6372656824deee", size = 1434321, upload-time = "2026-04-01T20:43:04.322Z" } +sdist = { url = "https://files.pythonhosted.org/packages/43/98/1501e70415157ec47c9020a682a0119c8ead46ae9f1aa79e007f779fda14/locust-2.44.1.tar.gz", hash = "sha256:962e6498431f152eca26d9cb158c23e3f61bf26e026f4f4171bed3f713379820", size = 1437151, upload-time = "2026-06-01T22:41:58.604Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c5/2c/a90d0b6fc476eb0f8e5a705f49e410563450b9b087688cc93a50eab54d63/locust-2.43.4-py3-none-any.whl", hash = "sha256:a4f40403e9f665e0dcb94991d9a8f19317d0d36afe88400833c5fab99ba942ed", size = 1454332, upload-time = "2026-04-01T20:43:02.767Z" }, + { url = "https://files.pythonhosted.org/packages/9f/f9/45b4c7849051deb080ba90c86bbe7d96969ba8858894eebcfdd921a173ac/locust-2.44.1-py3-none-any.whl", hash = "sha256:dba774c609fd56402ddf1ca07e3040ae7b436b4c2027393288e83167d24ac397", size = 1458058, upload-time = "2026-06-01T22:41:57.063Z" }, ] [[package]] @@ -1490,52 +1534,53 @@ wheels = [ [[package]] name = "mypy" -version = "1.20.1" +version = "2.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ + { name = "ast-serialize" }, { name = "librt", marker = "platform_python_implementation != 'PyPy'" }, { name = "mypy-extensions" }, { name = "pathspec" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0b/3d/5b373635b3146264eb7a68d09e5ca11c305bbb058dfffbb47c47daf4f632/mypy-1.20.1.tar.gz", hash = "sha256:6fc3f4ecd52de81648fed1945498bf42fa2993ddfad67c9056df36ae5757f804", size = 3815892, upload-time = "2026-04-13T02:46:51.474Z" } +sdist = { url = "https://files.pythonhosted.org/packages/82/15/cca9d88503549ed6fedeaa1d448cdddd542ee8a490232d732e278036fbf2/mypy-2.1.0.tar.gz", hash = "sha256:81e76ad12c2d804512e9b13240d1588316531bfba07558286078bfbce9613633", size = 3898359, upload-time = "2026-05-11T18:37:36.237Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/82/0d/555ab7453cc4a4a8643b7f21c842b1a84c36b15392061ae7b052ee119320/mypy-1.20.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c01eb9bac2c6a962d00f9d23421cd2913840e65bba365167d057bd0b4171a92e", size = 14336012, upload-time = "2026-04-13T02:45:39.935Z" }, - { url = "https://files.pythonhosted.org/packages/57/26/85a28893f7db8a16ebb41d1e9dfcb4475844d06a88480b6639e32a74d6ef/mypy-1.20.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:55d12ddbd8a9cac5b276878bd534fa39fff5bf543dc6ae18f25d30c8d7d27fca", size = 13224636, upload-time = "2026-04-13T02:45:49.659Z" }, - { url = "https://files.pythonhosted.org/packages/93/41/bd4cd3c2caeb6c448b669222b8cfcbdee4a03b89431527b56fca9e56b6f3/mypy-1.20.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0aa322c1468b6cdfc927a44ce130f79bb44bcd34eb4a009eb9f96571fd80955", size = 13663471, upload-time = "2026-04-13T02:46:20.276Z" }, - { url = "https://files.pythonhosted.org/packages/3e/56/7ee8c471e10402d64b6517ae10434541baca053cffd81090e4097d5609d4/mypy-1.20.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3f8bc95899cf676b6e2285779a08a998cc3a7b26f1026752df9d2741df3c79e8", size = 14532344, upload-time = "2026-04-13T02:46:44.205Z" }, - { url = "https://files.pythonhosted.org/packages/b5/95/b37d1fa859a433f6156742e12f62b0bb75af658544fb6dada9363918743a/mypy-1.20.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:47c2b90191a870a04041e910277494b0d92f0711be9e524d45c074fe60c00b65", size = 14776670, upload-time = "2026-04-13T02:45:52.481Z" }, - { url = "https://files.pythonhosted.org/packages/03/77/b302e4cb0b80d2bdf6bf4fce5864bb4cbfa461f7099cea544eaf2457df78/mypy-1.20.1-cp311-cp311-win_amd64.whl", hash = "sha256:9857dc8d2ec1a392ffbda518075beb00ac58859979c79f9e6bdcb7277082c2f2", size = 10816524, upload-time = "2026-04-13T02:45:37.711Z" }, - { url = "https://files.pythonhosted.org/packages/7f/21/d969d7a68eb964993ebcc6170d5ecaf0cf65830c58ac3344562e16dc42a9/mypy-1.20.1-cp311-cp311-win_arm64.whl", hash = "sha256:09d8df92bb25b6065ab91b178da843dda67b33eb819321679a6e98a907ce0e10", size = 9750419, upload-time = "2026-04-13T02:45:08.542Z" }, - { url = "https://files.pythonhosted.org/packages/69/1b/75a7c825a02781ca10bc2f2f12fba2af5202f6d6005aad8d2d1f264d8d78/mypy-1.20.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:36ee2b9c6599c230fea89bbd79f401f9f9f8e9fcf0c777827789b19b7da90f51", size = 14494077, upload-time = "2026-04-13T02:45:55.085Z" }, - { url = "https://files.pythonhosted.org/packages/b0/54/5e5a569ea5c2b4d48b729fb32aa936eeb4246e4fc3e6f5b3d36a2dfbefb9/mypy-1.20.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fba3fb0968a7b48806b0c90f38d39296f10766885a94c83bd21399de1e14eb28", size = 13319495, upload-time = "2026-04-13T02:45:29.674Z" }, - { url = "https://files.pythonhosted.org/packages/6f/a4/a1945b19f33e91721b59deee3abb484f2fa5922adc33bb166daf5325d76d/mypy-1.20.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef1415a637cd3627d6304dfbeddbadd21079dafc2a8a753c477ce4fc0c2af54f", size = 13696948, upload-time = "2026-04-13T02:46:15.006Z" }, - { url = "https://files.pythonhosted.org/packages/b2/c6/75e969781c2359b2f9c15b061f28ec6d67c8b61865ceda176e85c8e7f2de/mypy-1.20.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ef3461b1ad5cd446e540016e90b5984657edda39f982f4cc45ca317b628f5a37", size = 14706744, upload-time = "2026-04-13T02:46:00.482Z" }, - { url = "https://files.pythonhosted.org/packages/a8/6e/b221b1de981fc4262fe3e0bf9ec272d292dfe42394a689c2d49765c144c4/mypy-1.20.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:542dd63c9e1339b6092eb25bd515f3a32a1453aee8c9521d2ddb17dacd840237", size = 14949035, upload-time = "2026-04-13T02:45:06.021Z" }, - { url = "https://files.pythonhosted.org/packages/ca/4b/298ba2de0aafc0da3ff2288da06884aae7ba6489bc247c933f87847c41b3/mypy-1.20.1-cp312-cp312-win_amd64.whl", hash = "sha256:1d55c7cd8ca22e31f93af2a01160a9e95465b5878de23dba7e48116052f20a8d", size = 10883216, upload-time = "2026-04-13T02:45:47.232Z" }, - { url = "https://files.pythonhosted.org/packages/c7/f9/5e25b8f0b8cb92f080bfed9c21d3279b2a0b6a601cdca369a039ba84789d/mypy-1.20.1-cp312-cp312-win_arm64.whl", hash = "sha256:f5b84a79070586e0d353ee07b719d9d0a4aa7c8ee90c0ea97747e98cbe193019", size = 9814299, upload-time = "2026-04-13T02:45:21.934Z" }, - { url = "https://files.pythonhosted.org/packages/21/e8/ef0991aa24c8f225df10b034f3c2681213cb54cf247623c6dec9a5744e70/mypy-1.20.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8f3886c03e40afefd327bd70b3f634b39ea82e87f314edaa4d0cce4b927ddcc1", size = 14500739, upload-time = "2026-04-13T02:46:05.442Z" }, - { url = "https://files.pythonhosted.org/packages/23/73/416ebec3047636ed89fa871dc8c54bf05e9e20aa9499da59790d7adb312d/mypy-1.20.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e860eb3904f9764e83bafd70c8250bdffdc7dde6b82f486e8156348bf7ceb184", size = 13314735, upload-time = "2026-04-13T02:46:47.154Z" }, - { url = "https://files.pythonhosted.org/packages/10/1e/1505022d9c9ac2e014a384eb17638fb37bf8e9d0a833ea60605b66f8f7ba/mypy-1.20.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a4b5aac6e785719da51a84f5d09e9e843d473170a9045b1ea7ea1af86225df4b", size = 13704356, upload-time = "2026-04-13T02:45:19.773Z" }, - { url = "https://files.pythonhosted.org/packages/98/91/275b01f5eba5c467a3318ec214dd865abb66e9c811231c8587287b92876a/mypy-1.20.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f37b6cd0fe2ad3a20f05ace48ca3523fc52ff86940e34937b439613b6854472e", size = 14696420, upload-time = "2026-04-13T02:45:24.205Z" }, - { url = "https://files.pythonhosted.org/packages/a1/57/b3779e134e1b7250d05f874252780d0a88c068bc054bcff99ca20a3a2986/mypy-1.20.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e4bbb0f6b54ce7cc350ef4a770650d15fa70edd99ad5267e227133eda9c94218", size = 14936093, upload-time = "2026-04-13T02:45:32.087Z" }, - { url = "https://files.pythonhosted.org/packages/be/33/81b64991b0f3f278c3b55c335888794af190b2d59031a5ad1401bcb69f1e/mypy-1.20.1-cp313-cp313-win_amd64.whl", hash = "sha256:c3dc20f8ec76eecd77148cdd2f1542ed496e51e185713bf488a414f862deb8f2", size = 10889659, upload-time = "2026-04-13T02:46:02.926Z" }, - { url = "https://files.pythonhosted.org/packages/1b/fd/7adcb8053572edf5ef8f3db59599dfeeee3be9cc4c8c97e2d28f66f42ac5/mypy-1.20.1-cp313-cp313-win_arm64.whl", hash = "sha256:a9d62bbac5d6d46718e2b0330b25e6264463ed832722b8f7d4440ff1be3ca895", size = 9815515, upload-time = "2026-04-13T02:46:32.103Z" }, - { url = "https://files.pythonhosted.org/packages/40/cd/db831e84c81d57d4886d99feee14e372f64bbec6a9cb1a88a19e243f2ef5/mypy-1.20.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:12927b9c0ed794daedcf1dab055b6c613d9d5659ac511e8d936d96f19c087d12", size = 14483064, upload-time = "2026-04-13T02:45:26.901Z" }, - { url = "https://files.pythonhosted.org/packages/d5/82/74e62e7097fa67da328ac8ece8de09133448c04d20ddeaeba251a3000f01/mypy-1.20.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:752507dd481e958b2c08fc966d3806c962af5a9433b5bf8f3bdd7175c20e34fe", size = 13335694, upload-time = "2026-04-13T02:46:12.514Z" }, - { url = "https://files.pythonhosted.org/packages/74/c4/97e9a0abe4f3cdbbf4d079cb87a03b786efeccf5bf2b89fe4f96939ab2e6/mypy-1.20.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c614655b5a065e56274c6cbbe405f7cf7e96c0654db7ba39bc680238837f7b08", size = 13726365, upload-time = "2026-04-13T02:45:17.422Z" }, - { url = "https://files.pythonhosted.org/packages/d7/aa/a19d884a8d28fcd3c065776323029f204dbc774e70ec9c85eba228b680de/mypy-1.20.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2c3f6221a76f34d5100c6d35b3ef6b947054123c3f8d6938a4ba00b1308aa572", size = 14693472, upload-time = "2026-04-13T02:46:41.253Z" }, - { url = "https://files.pythonhosted.org/packages/84/44/cc9324bd21cf786592b44bf3b5d224b3923c1230ec9898d508d00241d465/mypy-1.20.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4bdfc06303ac06500af71ea0cdbe995c502b3c9ba32f3f8313523c137a25d1b6", size = 14919266, upload-time = "2026-04-13T02:46:28.37Z" }, - { url = "https://files.pythonhosted.org/packages/6e/dc/779abb25a8c63e8f44bf5a336217fa92790fa17e0c40e0c725d10cb01bbd/mypy-1.20.1-cp314-cp314-win_amd64.whl", hash = "sha256:0131edd7eba289973d1ba1003d1a37c426b85cdef76650cd02da6420898a5eb3", size = 11049713, upload-time = "2026-04-13T02:45:57.673Z" }, - { url = "https://files.pythonhosted.org/packages/28/08/4172be2ad7de9119b5a92ca36abbf641afdc5cb1ef4ae0c3a8182f29674f/mypy-1.20.1-cp314-cp314-win_arm64.whl", hash = "sha256:33f02904feb2c07e1fdf7909026206396c9deeb9e6f34d466b4cfedb0aadbbe4", size = 9999819, upload-time = "2026-04-13T02:46:35.039Z" }, - { url = "https://files.pythonhosted.org/packages/2d/af/af9e46b0c8eabbce9fc04a477564170f47a1c22b308822282a59b7ff315f/mypy-1.20.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:168472149dd8cc505c98cefd21ad77e4257ed6022cd5ed2fe2999bed56977a5a", size = 15547508, upload-time = "2026-04-13T02:46:25.588Z" }, - { url = "https://files.pythonhosted.org/packages/a7/cd/39c9e4ad6ba33e069e5837d772a9e6c304b4a5452a14a975d52b36444650/mypy-1.20.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:eb674600309a8f22790cca883a97c90299f948183ebb210fbef6bcee07cb1986", size = 14399557, upload-time = "2026-04-13T02:46:10.021Z" }, - { url = "https://files.pythonhosted.org/packages/83/c1/3fd71bdc118ffc502bf57559c909927bb7e011f327f7bb8e0488e98a5870/mypy-1.20.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef2b2e4cc464ba9795459f2586923abd58a0055487cbe558cb538ea6e6bc142a", size = 15045789, upload-time = "2026-04-13T02:45:10.81Z" }, - { url = "https://files.pythonhosted.org/packages/8e/73/6f07ff8b57a7d7b3e6e5bf34685d17632382395c8bb53364ec331661f83e/mypy-1.20.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dee461d396dd46b3f0ed5a098dbc9b8860c81c46ad44fa071afcfbc149f167c9", size = 15850795, upload-time = "2026-04-13T02:45:03.349Z" }, - { url = "https://files.pythonhosted.org/packages/ec/e2/f7dffec1c7767078f9e9adf0c786d1fe0ff30964a77eb213c09b8b58cb76/mypy-1.20.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e364926308b3e66f1361f81a566fc1b2f8cd47fc8525e8136d4058a65a4b4f02", size = 16088539, upload-time = "2026-04-13T02:46:17.841Z" }, - { url = "https://files.pythonhosted.org/packages/1a/76/e0dee71035316e75a69d73aec2f03c39c21c967b97e277fd0ef8fd6aec66/mypy-1.20.1-cp314-cp314t-win_amd64.whl", hash = "sha256:a0c17fbd746d38c70cbc42647cfd884f845a9708a4b160a8b4f7e70d41f4d7fa", size = 12575567, upload-time = "2026-04-13T02:45:34.795Z" }, - { url = "https://files.pythonhosted.org/packages/22/a8/7ed43c9d9c3d1468f86605e323a5d97e411a448790a00f07e779f3211a46/mypy-1.20.1-cp314-cp314t-win_arm64.whl", hash = "sha256:db2cb89654626a912efda69c0d5c1d22d948265e2069010d3dde3abf751c7d08", size = 10378823, upload-time = "2026-04-13T02:45:13.35Z" }, - { url = "https://files.pythonhosted.org/packages/d8/28/926bd972388e65a39ee98e188ccf67e81beb3aacfd5d6b310051772d974b/mypy-1.20.1-py3-none-any.whl", hash = "sha256:1aae28507f253fe82d883790d1c0a0d35798a810117c88184097fe8881052f06", size = 2636553, upload-time = "2026-04-13T02:46:30.45Z" }, + { url = "https://files.pythonhosted.org/packages/0a/a1/639f3024794a2a15899cb90707fe02e044c4412794c39c5769fd3df2e2ef/mypy-2.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a683016b16fe2f572dc04c72be7ee0504ac1605a265d0200f5cea695fb788f41", size = 14691685, upload-time = "2026-05-11T18:33:27.973Z" }, + { url = "https://files.pythonhosted.org/packages/3b/08/9a585dea4325f20d8b80dc78623fa50d1fd2173b710f6237afd6ba6ab39b/mypy-2.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1a293c534adb55271fef24a26da04b855540a8c13cc07bc5917b9fd2c394f2ca", size = 13555165, upload-time = "2026-05-11T18:32:16.107Z" }, + { url = "https://files.pythonhosted.org/packages/81/dc/7c42cc9c6cb01e8eb09961f1f738741d3e9c7e9d5c5b30ec69222625cd5f/mypy-2.1.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7406f4d048e71e576f5356d317e5b0a9e666dfd966bd99f9d14ca06e1a341538", size = 13994376, upload-time = "2026-05-11T18:32:39.256Z" }, + { url = "https://files.pythonhosted.org/packages/d4/fa/285946c33bce716e082c11dfeee9ee196eaf1f5042efb3581a31f9f205e4/mypy-2.1.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e0210d626fc8b31ccc90233754c7bc90e1f43205e85d96387f7db1285b55c398", size = 14864618, upload-time = "2026-05-11T18:34:49.765Z" }, + { url = "https://files.pythonhosted.org/packages/2b/83/82397f48af6c27e295d57979ded8490c9829040152cf7571b2f026aeb9a0/mypy-2.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3712c20deed54e814eaaa825603bada8ea1c390670a397c95b98405347acc563", size = 15102063, upload-time = "2026-05-11T18:34:05.855Z" }, + { url = "https://files.pythonhosted.org/packages/40/68/b02dec39057b88eb03dc0aa854732e26e8361f34f9d0e20c7614967d1eba/mypy-2.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fcaa0e479066e31f7cceb6a3bea39cb22b2ff51a6b2f24f193d19179ba17c389", size = 11060564, upload-time = "2026-05-11T18:35:36.494Z" }, + { url = "https://files.pythonhosted.org/packages/cf/a8/ea3dcbef31f99b634f2ee23bb0321cbc8c1b388b76a861eb849f13c347dc/mypy-2.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:0b1a5260c95aa443083f9ed3592662941951bca3d4ca224a5dc517c38b7cf666", size = 9966983, upload-time = "2026-05-11T18:37:14.139Z" }, + { url = "https://files.pythonhosted.org/packages/95/b1/55861beb5c339b44f9a2ba92df9e2cb1eeb4ae1eee674cdf7772c797778b/mypy-2.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:244358bf1c0da7722230bce60683d52e8e9fd030554926f15b747a84efb5b3af", size = 14874381, upload-time = "2026-05-11T18:37:31.784Z" }, + { url = "https://files.pythonhosted.org/packages/0b/b3/b7f770114b7d0ac92d0f76e8d93c2780844a70488a90e91821927850da86/mypy-2.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4ec7c57657493c7a75534df2751c8ae2cda383c16ecc55d2106c54476b1b16f6", size = 13665501, upload-time = "2026-05-11T18:34:23.063Z" }, + { url = "https://files.pythonhosted.org/packages/b6/f3/8ae2037967e2126689a0c11d99e2b707134a565191e92c60ca2572aec60a/mypy-2.1.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8161b6ff4392410023224f0969d17db93e1e154bc3e4ba62598e720723ae211", size = 14045750, upload-time = "2026-05-11T18:31:48.151Z" }, + { url = "https://files.pythonhosted.org/packages/a0/32/615eb5911859e43d054941b0d0a7d06cfa2870eba86529cf385b052b111c/mypy-2.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bf03e12003084a67395184d3eb8cbd6a489dc3655b5664b28c210a9e2403ab0b", size = 15061630, upload-time = "2026-05-11T18:37:06.898Z" }, + { url = "https://files.pythonhosted.org/packages/d4/03/4eafbfff8bfab1b87082741eae6e6a624028c984e6708b73bce2a8570c9d/mypy-2.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:20509760fd791c51579d573153407d226385ec1f8bcce55d730b354f3336bc22", size = 15288831, upload-time = "2026-05-11T18:31:18.07Z" }, + { url = "https://files.pythonhosted.org/packages/99/ee/919661478e5891a3c96e549c036e467e64563ab85995b10c53c8358e16a3/mypy-2.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:6753d0c1fdd6b1a23b9e4f283ce80b2153b724adcb2653b20b85a8a28ac6436b", size = 11135228, upload-time = "2026-05-11T18:34:31.23Z" }, + { url = "https://files.pythonhosted.org/packages/24/0a/6a12b9782ca0831a553192f351679f4548abc9d19a7cc93bb7feb02084c7/mypy-2.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:98ebb6589bb3b6d0c6f0c459d53ca55b8091fbc13d277c4041c885392e8195e8", size = 10040684, upload-time = "2026-05-11T18:36:48.199Z" }, + { url = "https://files.pythonhosted.org/packages/6e/dd/c7191469c777f07689c032a8f7326e393ea34c92d6d76eb7ce5ba57ea66d/mypy-2.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:35aac3bb114e03888f535d5eb51b8bafbb3266586b599da1940f9b1be3ec5bd5", size = 14852174, upload-time = "2026-05-11T18:31:38.929Z" }, + { url = "https://files.pythonhosted.org/packages/55/8c/aed55408879043d72bb9135f4d0d19a02b886dd569631e113e3d2706cb8d/mypy-2.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8de55a8c861f2a49331f807be98d90caeceeef520bde13d43a160207f8af613e", size = 13651542, upload-time = "2026-05-11T18:36:04.636Z" }, + { url = "https://files.pythonhosted.org/packages/3a/8e/f371a824b1f1fa8ea6e3dbb8703d232977d572be2329554a3bc4d960302f/mypy-2.1.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5fdf2941a07434af755837d9880f7d7d25f1dacb1af9dcd4b9b66f2220a3024e", size = 14033929, upload-time = "2026-05-11T18:35:55.742Z" }, + { url = "https://files.pythonhosted.org/packages/94/21/f54be870d6dd53a82c674407e0f8eed7174b05ec78d42e5abd7b42e84fd5/mypy-2.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e195b817c13f02352a9c124301f9f30f078405444679b6753c1b96b6eed37285", size = 15039200, upload-time = "2026-05-11T18:33:10.281Z" }, + { url = "https://files.pythonhosted.org/packages/17/99/bf21748626a40ce59fd29a39386ab46afec88b7bd2f0fa6c3a97c995523f/mypy-2.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5431d42af987ebd92ba2f71d45c85ed41d8e6ca9f5fd209a69f68f707d2469e5", size = 15272690, upload-time = "2026-05-11T18:32:07.205Z" }, + { url = "https://files.pythonhosted.org/packages/d6/d7/9e90d2cf47100bea550ed2bc7b0d4de3a62181d84d5e37da0003e8462637/mypy-2.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:767fe8c66dc3e01e19e1737d4c38ebefead16125e1b8e58ad421903b376f5c65", size = 11147435, upload-time = "2026-05-11T18:33:56.477Z" }, + { url = "https://files.pythonhosted.org/packages/ec/46/e5c449e858798e35ffc90946282a27c62a77be743fe17480e4977374eb91/mypy-2.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:ecfe70d43775ab99562ab128ce49854a362044c9f894961f68f898c23cb7429d", size = 10035052, upload-time = "2026-05-11T18:32:30.049Z" }, + { url = "https://files.pythonhosted.org/packages/b0/ca/b279a672e874aedd5498ae25f722dacc8aa86bbffb939b3f97cbb1cf6686/mypy-2.1.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:7354c5a7f69d9345c3d6e69921d57088eea3ddeeb6b20d34c1b3855b02c36ec2", size = 14848422, upload-time = "2026-05-11T18:35:45.984Z" }, + { url = "https://files.pythonhosted.org/packages/27/e6/3efe56c631d959b9b4454e208b0ac4b7f4f58b404c89f8bec7b49efdfc21/mypy-2.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:49890d4f76ac9e06ec117f9e09f3174da70a620a0c300953d8595c926e80947f", size = 13677374, upload-time = "2026-05-11T18:36:57.188Z" }, + { url = "https://files.pythonhosted.org/packages/84/7f/8107ea87a44fd1f1b59882442f033c9c3488c127201b1d1d15f1cbd6022e/mypy-2.1.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:761be68e023ef5d94678772396a8af1220030f80837a3afd8d0aef3b419666f4", size = 14055743, upload-time = "2026-05-11T18:35:18.361Z" }, + { url = "https://files.pythonhosted.org/packages/51/4d/b6d34db183133b83761b9199a82d31557cdbb70a380d8c3b3438e11882a3/mypy-2.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c90345fc182dc363b891350457ec69c35140858538f38b4540845afcc32b1aef", size = 15020937, upload-time = "2026-05-11T18:34:59.618Z" }, + { url = "https://files.pythonhosted.org/packages/ff/d7/f08360c691d758acb02f45022c34d98b92892f4ea756644e1000d4b9f3d8/mypy-2.1.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b84802e7b5a6daf1f5e15bc9fcd7ddae77be13981ffab037f1c67bb84d67d135", size = 15253371, upload-time = "2026-05-11T18:36:41.081Z" }, + { url = "https://files.pythonhosted.org/packages/67/1b/09460a13719530a19bce27bd3bc8449e83569dd2ba7faf51c9c3c30c0b61/mypy-2.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:022c771234936ceac541ebaf836fe9e2abeb3f5e09aff21588fe543ff006fe21", size = 11326429, upload-time = "2026-05-11T18:34:13.526Z" }, + { url = "https://files.pythonhosted.org/packages/40/62/75dbf0f82f7b6680340efc614af29dd0b3c17b8a4f1cd09b8bd2fd6bc814/mypy-2.1.0-cp314-cp314-win_arm64.whl", hash = "sha256:498207db725cec88829a6a5c2fc771205fd043719ef98bc49aba8fb9fc4e6d57", size = 10218799, upload-time = "2026-05-11T18:32:23.491Z" }, + { url = "https://files.pythonhosted.org/packages/b2/66/caca04ed7d972fb6eb6dd1ccd6df1de5c38fae8c5b3dc1c4e8e0d85ee6b9/mypy-2.1.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:7d5e5cad0efeba72b93cd17490cc0d69c5ac9ca132994fe3fb0314808aeeb83e", size = 15923458, upload-time = "2026-05-11T18:35:28.64Z" }, + { url = "https://files.pythonhosted.org/packages/ed/52/2d90cbe49d014b13ed7ff337930c30bad35893fe38a1e4641e756bb62191/mypy-2.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ff715050c127d724fd260a2e666e7747fdd83511c0c47d449d98238970aef780", size = 14757697, upload-time = "2026-05-11T18:36:14.208Z" }, + { url = "https://files.pythonhosted.org/packages/ac/37/d98f4a14e081b238992d0ed96b6d39c7cc0148c9699eb71eaa68629665ea/mypy-2.1.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:82208da9e09414d520e912d3e462d454854bed0810b71540bb016dcbca7308fd", size = 15405638, upload-time = "2026-05-11T18:33:48.249Z" }, + { url = "https://files.pythonhosted.org/packages/a3/c2/15c46613b24a84fad2aea1248bf9619b99c2767ae9071fe224c179a0b7d4/mypy-2.1.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e79ebc1b904b84f0310dff7469655a9c36c7a68bddb37bdd42b67a332df61d08", size = 16215852, upload-time = "2026-05-11T18:32:50.296Z" }, + { url = "https://files.pythonhosted.org/packages/5c/90/9c16a57f482c76d25f6379762b56bbf65c711d8158cf271fb2802cfb0640/mypy-2.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e583edc957cfb0deb142079162ae826f58449b116c1d442f2d91c69d9fced081", size = 16452695, upload-time = "2026-05-11T18:33:38.182Z" }, + { url = "https://files.pythonhosted.org/packages/0f/4c/215a4eeb63cacc5f17f516691ea7285d11e249802b942476bff15922a314/mypy-2.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b33b6cd332695bba180d55e717a79d3038e479a2c49cc5eb3d53603409b9a5d7", size = 12866622, upload-time = "2026-05-11T18:34:39.945Z" }, + { url = "https://files.pythonhosted.org/packages/4b/50/1043e1db5f455ffe4c9ab22747cd8ca2bc492b1e4f4e21b130a44ee2b217/mypy-2.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:4f910fe825376a7b66ef7ca8c98e5a149e8cd64c19ae71d84047a74ee060d4e6", size = 10610798, upload-time = "2026-05-11T18:36:31.444Z" }, + { url = "https://files.pythonhosted.org/packages/0d/2a/13ca1f292f6db1b98ff495ef3467736b331621c5917cad984b7043e7348d/mypy-2.1.0-py3-none-any.whl", hash = "sha256:a663814603a5c563fb87a4f96fb473eeb30d1f5a4885afcf44f9db000a366289", size = 2693302, upload-time = "2026-05-11T18:31:29.246Z" }, ] [[package]] @@ -1558,81 +1603,81 @@ wheels = [ [[package]] name = "numpy" -version = "2.4.4" +version = "2.4.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d7/9f/b8cef5bffa569759033adda9481211426f12f53299629b410340795c2514/numpy-2.4.4.tar.gz", hash = "sha256:2d390634c5182175533585cc89f3608a4682ccb173cc9bb940b2881c8d6f8fa0", size = 20731587, upload-time = "2026-03-29T13:22:01.298Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/ad/fed0499ce6a338d2a03ebae59cd15093910c8875328855781952abf6c2fe/numpy-2.4.6.tar.gz", hash = "sha256:f3a3570c4a2a16746ac2c31a7c7c7b0c186b95ce902e33db6f28094ed7387dda", size = 20735807, upload-time = "2026-05-18T23:37:14.07Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/c6/4218570d8c8ecc9704b5157a3348e486e84ef4be0ed3e38218ab473c83d2/numpy-2.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f983334aea213c99992053ede6168500e5f086ce74fbc4acc3f2b00f5762e9db", size = 16976799, upload-time = "2026-03-29T13:18:15.438Z" }, - { url = "https://files.pythonhosted.org/packages/dd/92/b4d922c4a5f5dab9ed44e6153908a5c665b71acf183a83b93b690996e39b/numpy-2.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:72944b19f2324114e9dc86a159787333b77874143efcf89a5167ef83cfee8af0", size = 14971552, upload-time = "2026-03-29T13:18:18.606Z" }, - { url = "https://files.pythonhosted.org/packages/8a/dc/df98c095978fa6ee7b9a9387d1d58cbb3d232d0e69ad169a4ce784bde4fd/numpy-2.4.4-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:86b6f55f5a352b48d7fbfd2dbc3d5b780b2d79f4d3c121f33eb6efb22e9a2015", size = 5476566, upload-time = "2026-03-29T13:18:21.532Z" }, - { url = "https://files.pythonhosted.org/packages/28/34/b3fdcec6e725409223dd27356bdf5a3c2cc2282e428218ecc9cb7acc9763/numpy-2.4.4-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:ba1f4fc670ed79f876f70082eff4f9583c15fb9a4b89d6188412de4d18ae2f40", size = 6806482, upload-time = "2026-03-29T13:18:23.634Z" }, - { url = "https://files.pythonhosted.org/packages/68/62/63417c13aa35d57bee1337c67446761dc25ea6543130cf868eace6e8157b/numpy-2.4.4-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8a87ec22c87be071b6bdbd27920b129b94f2fc964358ce38f3822635a3e2e03d", size = 15973376, upload-time = "2026-03-29T13:18:26.677Z" }, - { url = "https://files.pythonhosted.org/packages/cf/c5/9fcb7e0e69cef59cf10c746b84f7d58b08bc66a6b7d459783c5a4f6101a6/numpy-2.4.4-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:df3775294accfdd75f32c74ae39fcba920c9a378a2fc18a12b6820aa8c1fb502", size = 16925137, upload-time = "2026-03-29T13:18:30.14Z" }, - { url = "https://files.pythonhosted.org/packages/7e/43/80020edacb3f84b9efdd1591120a4296462c23fd8db0dde1666f6ef66f13/numpy-2.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0d4e437e295f18ec29bc79daf55e8a47a9113df44d66f702f02a293d93a2d6dd", size = 17329414, upload-time = "2026-03-29T13:18:33.733Z" }, - { url = "https://files.pythonhosted.org/packages/fd/06/af0658593b18a5f73532d377188b964f239eb0894e664a6c12f484472f97/numpy-2.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6aa3236c78803afbcb255045fbef97a9e25a1f6c9888357d205ddc42f4d6eba5", size = 18658397, upload-time = "2026-03-29T13:18:37.511Z" }, - { url = "https://files.pythonhosted.org/packages/e6/ce/13a09ed65f5d0ce5c7dd0669250374c6e379910f97af2c08c57b0608eee4/numpy-2.4.4-cp311-cp311-win32.whl", hash = "sha256:30caa73029a225b2d40d9fae193e008e24b2026b7ee1a867b7ee8d96ca1a448e", size = 6239499, upload-time = "2026-03-29T13:18:40.372Z" }, - { url = "https://files.pythonhosted.org/packages/bd/63/05d193dbb4b5eec1eca73822d80da98b511f8328ad4ae3ca4caf0f4db91d/numpy-2.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:6bbe4eb67390b0a0265a2c25458f6b90a409d5d069f1041e6aff1e27e3d9a79e", size = 12614257, upload-time = "2026-03-29T13:18:42.95Z" }, - { url = "https://files.pythonhosted.org/packages/87/c5/8168052f080c26fa984c413305012be54741c9d0d74abd7fbeeccae3889f/numpy-2.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:fcfe2045fd2e8f3cb0ce9d4ba6dba6333b8fa05bb8a4939c908cd43322d14c7e", size = 10486775, upload-time = "2026-03-29T13:18:45.835Z" }, - { url = "https://files.pythonhosted.org/packages/28/05/32396bec30fb2263770ee910142f49c1476d08e8ad41abf8403806b520ce/numpy-2.4.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:15716cfef24d3a9762e3acdf87e27f58dc823d1348f765bbea6bef8c639bfa1b", size = 16689272, upload-time = "2026-03-29T13:18:49.223Z" }, - { url = "https://files.pythonhosted.org/packages/c5/f3/a983d28637bfcd763a9c7aafdb6d5c0ebf3d487d1e1459ffdb57e2f01117/numpy-2.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:23cbfd4c17357c81021f21540da84ee282b9c8fba38a03b7b9d09ba6b951421e", size = 14699573, upload-time = "2026-03-29T13:18:52.629Z" }, - { url = "https://files.pythonhosted.org/packages/9b/fd/e5ecca1e78c05106d98028114f5c00d3eddb41207686b2b7de3e477b0e22/numpy-2.4.4-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:8b3b60bb7cba2c8c81837661c488637eee696f59a877788a396d33150c35d842", size = 5204782, upload-time = "2026-03-29T13:18:55.579Z" }, - { url = "https://files.pythonhosted.org/packages/de/2f/702a4594413c1a8632092beae8aba00f1d67947389369b3777aed783fdca/numpy-2.4.4-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:e4a010c27ff6f210ff4c6ef34394cd61470d01014439b192ec22552ee867f2a8", size = 6552038, upload-time = "2026-03-29T13:18:57.769Z" }, - { url = "https://files.pythonhosted.org/packages/7f/37/eed308a8f56cba4d1fdf467a4fc67ef4ff4bf1c888f5fc980481890104b1/numpy-2.4.4-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f9e75681b59ddaa5e659898085ae0eaea229d054f2ac0c7e563a62205a700121", size = 15670666, upload-time = "2026-03-29T13:19:00.341Z" }, - { url = "https://files.pythonhosted.org/packages/0a/0d/0e3ecece05b7a7e87ab9fb587855548da437a061326fff64a223b6dcb78a/numpy-2.4.4-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:81f4a14bee47aec54f883e0cad2d73986640c1590eb9bfaaba7ad17394481e6e", size = 16645480, upload-time = "2026-03-29T13:19:03.63Z" }, - { url = "https://files.pythonhosted.org/packages/34/49/f2312c154b82a286758ee2f1743336d50651f8b5195db18cdb63675ff649/numpy-2.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:62d6b0f03b694173f9fcb1fb317f7222fd0b0b103e784c6549f5e53a27718c44", size = 17020036, upload-time = "2026-03-29T13:19:07.428Z" }, - { url = "https://files.pythonhosted.org/packages/7b/e9/736d17bd77f1b0ec4f9901aaec129c00d59f5d84d5e79bba540ef12c2330/numpy-2.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fbc356aae7adf9e6336d336b9c8111d390a05df88f1805573ebb0807bd06fd1d", size = 18368643, upload-time = "2026-03-29T13:19:10.775Z" }, - { url = "https://files.pythonhosted.org/packages/63/f6/d417977c5f519b17c8a5c3bc9e8304b0908b0e21136fe43bf628a1343914/numpy-2.4.4-cp312-cp312-win32.whl", hash = "sha256:0d35aea54ad1d420c812bfa0385c71cd7cc5bcf7c65fed95fc2cd02fe8c79827", size = 5961117, upload-time = "2026-03-29T13:19:13.464Z" }, - { url = "https://files.pythonhosted.org/packages/2d/5b/e1deebf88ff431b01b7406ca3583ab2bbb90972bbe1c568732e49c844f7e/numpy-2.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:b5f0362dc928a6ecd9db58868fca5e48485205e3855957bdedea308f8672ea4a", size = 12320584, upload-time = "2026-03-29T13:19:16.155Z" }, - { url = "https://files.pythonhosted.org/packages/58/89/e4e856ac82a68c3ed64486a544977d0e7bdd18b8da75b78a577ca31c4395/numpy-2.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:846300f379b5b12cc769334464656bc882e0735d27d9726568bc932fdc49d5ec", size = 10221450, upload-time = "2026-03-29T13:19:18.994Z" }, - { url = "https://files.pythonhosted.org/packages/14/1d/d0a583ce4fefcc3308806a749a536c201ed6b5ad6e1322e227ee4848979d/numpy-2.4.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:08f2e31ed5e6f04b118e49821397f12767934cfdd12a1ce86a058f91e004ee50", size = 16684933, upload-time = "2026-03-29T13:19:22.47Z" }, - { url = "https://files.pythonhosted.org/packages/c1/62/2b7a48fbb745d344742c0277f01286dead15f3f68e4f359fbfcf7b48f70f/numpy-2.4.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e823b8b6edc81e747526f70f71a9c0a07ac4e7ad13020aa736bb7c9d67196115", size = 14694532, upload-time = "2026-03-29T13:19:25.581Z" }, - { url = "https://files.pythonhosted.org/packages/e5/87/499737bfba066b4a3bebff24a8f1c5b2dee410b209bc6668c9be692580f0/numpy-2.4.4-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:4a19d9dba1a76618dd86b164d608566f393f8ec6ac7c44f0cc879011c45e65af", size = 5199661, upload-time = "2026-03-29T13:19:28.31Z" }, - { url = "https://files.pythonhosted.org/packages/cd/da/464d551604320d1491bc345efed99b4b7034143a85787aab78d5691d5a0e/numpy-2.4.4-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:d2a8490669bfe99a233298348acc2d824d496dee0e66e31b66a6022c2ad74a5c", size = 6547539, upload-time = "2026-03-29T13:19:30.97Z" }, - { url = "https://files.pythonhosted.org/packages/7d/90/8d23e3b0dafd024bf31bdec225b3bb5c2dbfa6912f8a53b8659f21216cbf/numpy-2.4.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:45dbed2ab436a9e826e302fcdcbe9133f9b0006e5af7168afb8963a6520da103", size = 15668806, upload-time = "2026-03-29T13:19:33.887Z" }, - { url = "https://files.pythonhosted.org/packages/d1/73/a9d864e42a01896bb5974475438f16086be9ba1f0d19d0bb7a07427c4a8b/numpy-2.4.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c901b15172510173f5cb310eae652908340f8dede90fff9e3bf6c0d8dfd92f83", size = 16632682, upload-time = "2026-03-29T13:19:37.336Z" }, - { url = "https://files.pythonhosted.org/packages/34/fb/14570d65c3bde4e202a031210475ae9cde9b7686a2e7dc97ee67d2833b35/numpy-2.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:99d838547ace2c4aace6c4f76e879ddfe02bb58a80c1549928477862b7a6d6ed", size = 17019810, upload-time = "2026-03-29T13:19:40.963Z" }, - { url = "https://files.pythonhosted.org/packages/8a/77/2ba9d87081fd41f6d640c83f26fb7351e536b7ce6dd9061b6af5904e8e46/numpy-2.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0aec54fd785890ecca25a6003fd9a5aed47ad607bbac5cd64f836ad8666f4959", size = 18357394, upload-time = "2026-03-29T13:19:44.859Z" }, - { url = "https://files.pythonhosted.org/packages/a2/23/52666c9a41708b0853fa3b1a12c90da38c507a3074883823126d4e9d5b30/numpy-2.4.4-cp313-cp313-win32.whl", hash = "sha256:07077278157d02f65c43b1b26a3886bce886f95d20aabd11f87932750dfb14ed", size = 5959556, upload-time = "2026-03-29T13:19:47.661Z" }, - { url = "https://files.pythonhosted.org/packages/57/fb/48649b4971cde70d817cf97a2a2fdc0b4d8308569f1dd2f2611959d2e0cf/numpy-2.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:5c70f1cc1c4efbe316a572e2d8b9b9cc44e89b95f79ca3331553fbb63716e2bf", size = 12317311, upload-time = "2026-03-29T13:19:50.67Z" }, - { url = "https://files.pythonhosted.org/packages/ba/d8/11490cddd564eb4de97b4579ef6bfe6a736cc07e94c1598590ae25415e01/numpy-2.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:ef4059d6e5152fa1a39f888e344c73fdc926e1b2dd58c771d67b0acfbf2aa67d", size = 10222060, upload-time = "2026-03-29T13:19:54.229Z" }, - { url = "https://files.pythonhosted.org/packages/99/5d/dab4339177a905aad3e2221c915b35202f1ec30d750dd2e5e9d9a72b804b/numpy-2.4.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4bbc7f303d125971f60ec0aaad5e12c62d0d2c925f0ab1273debd0e4ba37aba5", size = 14822302, upload-time = "2026-03-29T13:19:57.585Z" }, - { url = "https://files.pythonhosted.org/packages/eb/e4/0564a65e7d3d97562ed6f9b0fd0fb0a6f559ee444092f105938b50043876/numpy-2.4.4-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:4d6d57903571f86180eb98f8f0c839fa9ebbfb031356d87f1361be91e433f5b7", size = 5327407, upload-time = "2026-03-29T13:20:00.601Z" }, - { url = "https://files.pythonhosted.org/packages/29/8d/35a3a6ce5ad371afa58b4700f1c820f8f279948cca32524e0a695b0ded83/numpy-2.4.4-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:4636de7fd195197b7535f231b5de9e4b36d2c440b6e566d2e4e4746e6af0ca93", size = 6647631, upload-time = "2026-03-29T13:20:02.855Z" }, - { url = "https://files.pythonhosted.org/packages/f4/da/477731acbd5a58a946c736edfdabb2ac5b34c3d08d1ba1a7b437fa0884df/numpy-2.4.4-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ad2e2ef14e0b04e544ea2fa0a36463f847f113d314aa02e5b402fdf910ef309e", size = 15727691, upload-time = "2026-03-29T13:20:06.004Z" }, - { url = "https://files.pythonhosted.org/packages/e6/db/338535d9b152beabeb511579598418ba0212ce77cf9718edd70262cc4370/numpy-2.4.4-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a285b3b96f951841799528cd1f4f01cd70e7e0204b4abebac9463eecfcf2a40", size = 16681241, upload-time = "2026-03-29T13:20:09.417Z" }, - { url = "https://files.pythonhosted.org/packages/e2/a9/ad248e8f58beb7a0219b413c9c7d8151c5d285f7f946c3e26695bdbbe2df/numpy-2.4.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:f8474c4241bc18b750be2abea9d7a9ec84f46ef861dbacf86a4f6e043401f79e", size = 17085767, upload-time = "2026-03-29T13:20:13.126Z" }, - { url = "https://files.pythonhosted.org/packages/b5/1a/3b88ccd3694681356f70da841630e4725a7264d6a885c8d442a697e1146b/numpy-2.4.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4e874c976154687c1f71715b034739b45c7711bec81db01914770373d125e392", size = 18403169, upload-time = "2026-03-29T13:20:17.096Z" }, - { url = "https://files.pythonhosted.org/packages/c2/c9/fcfd5d0639222c6eac7f304829b04892ef51c96a75d479214d77e3ce6e33/numpy-2.4.4-cp313-cp313t-win32.whl", hash = "sha256:9c585a1790d5436a5374bac930dad6ed244c046ed91b2b2a3634eb2971d21008", size = 6083477, upload-time = "2026-03-29T13:20:20.195Z" }, - { url = "https://files.pythonhosted.org/packages/d5/e3/3938a61d1c538aaec8ed6fd6323f57b0c2d2d2219512434c5c878db76553/numpy-2.4.4-cp313-cp313t-win_amd64.whl", hash = "sha256:93e15038125dc1e5345d9b5b68aa7f996ec33b98118d18c6ca0d0b7d6198b7e8", size = 12457487, upload-time = "2026-03-29T13:20:22.946Z" }, - { url = "https://files.pythonhosted.org/packages/97/6a/7e345032cc60501721ef94e0e30b60f6b0bd601f9174ebd36389a2b86d40/numpy-2.4.4-cp313-cp313t-win_arm64.whl", hash = "sha256:0dfd3f9d3adbe2920b68b5cd3d51444e13a10792ec7154cd0a2f6e74d4ab3233", size = 10292002, upload-time = "2026-03-29T13:20:25.909Z" }, - { url = "https://files.pythonhosted.org/packages/6e/06/c54062f85f673dd5c04cbe2f14c3acb8c8b95e3384869bb8cc9bff8cb9df/numpy-2.4.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:f169b9a863d34f5d11b8698ead99febeaa17a13ca044961aa8e2662a6c7766a0", size = 16684353, upload-time = "2026-03-29T13:20:29.504Z" }, - { url = "https://files.pythonhosted.org/packages/4c/39/8a320264a84404c74cc7e79715de85d6130fa07a0898f67fb5cd5bd79908/numpy-2.4.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2483e4584a1cb3092da4470b38866634bafb223cbcd551ee047633fd2584599a", size = 14704914, upload-time = "2026-03-29T13:20:33.547Z" }, - { url = "https://files.pythonhosted.org/packages/91/fb/287076b2614e1d1044235f50f03748f31fa287e3dbe6abeb35cdfa351eca/numpy-2.4.4-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:2d19e6e2095506d1736b7d80595e0f252d76b89f5e715c35e06e937679ea7d7a", size = 5210005, upload-time = "2026-03-29T13:20:36.45Z" }, - { url = "https://files.pythonhosted.org/packages/63/eb/fcc338595309910de6ecabfcef2419a9ce24399680bfb149421fa2df1280/numpy-2.4.4-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:6a246d5914aa1c820c9443ddcee9c02bec3e203b0c080349533fae17727dfd1b", size = 6544974, upload-time = "2026-03-29T13:20:39.014Z" }, - { url = "https://files.pythonhosted.org/packages/44/5d/e7e9044032a716cdfaa3fba27a8e874bf1c5f1912a1ddd4ed071bf8a14a6/numpy-2.4.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:989824e9faf85f96ec9c7761cd8d29c531ad857bfa1daa930cba85baaecf1a9a", size = 15684591, upload-time = "2026-03-29T13:20:42.146Z" }, - { url = "https://files.pythonhosted.org/packages/98/7c/21252050676612625449b4807d6b695b9ce8a7c9e1c197ee6216c8a65c7c/numpy-2.4.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:27a8d92cd10f1382a67d7cf4db7ce18341b66438bdd9f691d7b0e48d104c2a9d", size = 16637700, upload-time = "2026-03-29T13:20:46.204Z" }, - { url = "https://files.pythonhosted.org/packages/b1/29/56d2bbef9465db24ef25393383d761a1af4f446a1df9b8cded4fe3a5a5d7/numpy-2.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e44319a2953c738205bf3354537979eaa3998ed673395b964c1176083dd46252", size = 17035781, upload-time = "2026-03-29T13:20:50.242Z" }, - { url = "https://files.pythonhosted.org/packages/e3/2b/a35a6d7589d21f44cea7d0a98de5ddcbb3d421b2622a5c96b1edf18707c3/numpy-2.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e892aff75639bbef0d2a2cfd55535510df26ff92f63c92cd84ef8d4ba5a5557f", size = 18362959, upload-time = "2026-03-29T13:20:54.019Z" }, - { url = "https://files.pythonhosted.org/packages/64/c9/d52ec581f2390e0f5f85cbfd80fb83d965fc15e9f0e1aec2195faa142cde/numpy-2.4.4-cp314-cp314-win32.whl", hash = "sha256:1378871da56ca8943c2ba674530924bb8ca40cd228358a3b5f302ad60cf875fc", size = 6008768, upload-time = "2026-03-29T13:20:56.912Z" }, - { url = "https://files.pythonhosted.org/packages/fa/22/4cc31a62a6c7b74a8730e31a4274c5dc80e005751e277a2ce38e675e4923/numpy-2.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:715d1c092715954784bc79e1174fc2a90093dc4dc84ea15eb14dad8abdcdeb74", size = 12449181, upload-time = "2026-03-29T13:20:59.548Z" }, - { url = "https://files.pythonhosted.org/packages/70/2e/14cda6f4d8e396c612d1bf97f22958e92148801d7e4f110cabebdc0eef4b/numpy-2.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:2c194dd721e54ecad9ad387c1d35e63dce5c4450c6dc7dd5611283dda239aabb", size = 10496035, upload-time = "2026-03-29T13:21:02.524Z" }, - { url = "https://files.pythonhosted.org/packages/b1/e8/8fed8c8d848d7ecea092dc3469643f9d10bc3a134a815a3b033da1d2039b/numpy-2.4.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2aa0613a5177c264ff5921051a5719d20095ea586ca88cc802c5c218d1c67d3e", size = 14824958, upload-time = "2026-03-29T13:21:05.671Z" }, - { url = "https://files.pythonhosted.org/packages/05/1a/d8007a5138c179c2bf33ef44503e83d70434d2642877ee8fbb230e7c0548/numpy-2.4.4-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:42c16925aa5a02362f986765f9ebabf20de75cdefdca827d14315c568dcab113", size = 5330020, upload-time = "2026-03-29T13:21:08.635Z" }, - { url = "https://files.pythonhosted.org/packages/99/64/ffb99ac6ae93faf117bcbd5c7ba48a7f45364a33e8e458545d3633615dda/numpy-2.4.4-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:874f200b2a981c647340f841730fc3a2b54c9d940566a3c4149099591e2c4c3d", size = 6650758, upload-time = "2026-03-29T13:21:10.949Z" }, - { url = "https://files.pythonhosted.org/packages/6e/6e/795cc078b78a384052e73b2f6281ff7a700e9bf53bcce2ee579d4f6dd879/numpy-2.4.4-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c9b39d38a9bd2ae1becd7eac1303d031c5c110ad31f2b319c6e7d98b135c934d", size = 15729948, upload-time = "2026-03-29T13:21:14.047Z" }, - { url = "https://files.pythonhosted.org/packages/5f/86/2acbda8cc2af5f3d7bfc791192863b9e3e19674da7b5e533fded124d1299/numpy-2.4.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b268594bccac7d7cf5844c7732e3f20c50921d94e36d7ec9b79e9857694b1b2f", size = 16679325, upload-time = "2026-03-29T13:21:17.561Z" }, - { url = "https://files.pythonhosted.org/packages/bc/59/cafd83018f4aa55e0ac6fa92aa066c0a1877b77a615ceff1711c260ffae8/numpy-2.4.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ac6b31e35612a26483e20750126d30d0941f949426974cace8e6b5c58a3657b0", size = 17084883, upload-time = "2026-03-29T13:21:21.106Z" }, - { url = "https://files.pythonhosted.org/packages/f0/85/a42548db84e65ece46ab2caea3d3f78b416a47af387fcbb47ec28e660dc2/numpy-2.4.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8e3ed142f2728df44263aaf5fb1f5b0b99f4070c553a0d7f033be65338329150", size = 18403474, upload-time = "2026-03-29T13:21:24.828Z" }, - { url = "https://files.pythonhosted.org/packages/ed/ad/483d9e262f4b831000062e5d8a45e342166ec8aaa1195264982bca267e62/numpy-2.4.4-cp314-cp314t-win32.whl", hash = "sha256:dddbbd259598d7240b18c9d87c56a9d2fb3b02fe266f49a7c101532e78c1d871", size = 6155500, upload-time = "2026-03-29T13:21:28.205Z" }, - { url = "https://files.pythonhosted.org/packages/c7/03/2fc4e14c7bd4ff2964b74ba90ecb8552540b6315f201df70f137faa5c589/numpy-2.4.4-cp314-cp314t-win_amd64.whl", hash = "sha256:a7164afb23be6e37ad90b2f10426149fd75aee07ca55653d2aa41e66c4ef697e", size = 12637755, upload-time = "2026-03-29T13:21:31.107Z" }, - { url = "https://files.pythonhosted.org/packages/58/78/548fb8e07b1a341746bfbecb32f2c268470f45fa028aacdbd10d9bc73aab/numpy-2.4.4-cp314-cp314t-win_arm64.whl", hash = "sha256:ba203255017337d39f89bdd58417f03c4426f12beed0440cfd933cb15f8669c7", size = 10566643, upload-time = "2026-03-29T13:21:34.339Z" }, - { url = "https://files.pythonhosted.org/packages/6b/33/8fae8f964a4f63ed528264ddf25d2b683d0b663e3cba26961eb838a7c1bd/numpy-2.4.4-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:58c8b5929fcb8287cbd6f0a3fae19c6e03a5c48402ae792962ac465224a629a4", size = 16854491, upload-time = "2026-03-29T13:21:38.03Z" }, - { url = "https://files.pythonhosted.org/packages/bc/d0/1aabee441380b981cf8cdda3ae7a46aa827d1b5a8cce84d14598bc94d6d9/numpy-2.4.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:eea7ac5d2dce4189771cedb559c738a71512768210dc4e4753b107a2048b3d0e", size = 14895830, upload-time = "2026-03-29T13:21:41.509Z" }, - { url = "https://files.pythonhosted.org/packages/a5/b8/aafb0d1065416894fccf4df6b49ef22b8db045187949545bced89c034b8e/numpy-2.4.4-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:51fc224f7ca4d92656d5a5eb315f12eb5fe2c97a66249aa7b5f562528a3be38c", size = 5400927, upload-time = "2026-03-29T13:21:44.747Z" }, - { url = "https://files.pythonhosted.org/packages/d6/77/063baa20b08b431038c7f9ff5435540c7b7265c78cf56012a483019ca72d/numpy-2.4.4-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:28a650663f7314afc3e6ec620f44f333c386aad9f6fc472030865dc0ebb26ee3", size = 6715557, upload-time = "2026-03-29T13:21:47.406Z" }, - { url = "https://files.pythonhosted.org/packages/c7/a8/379542d45a14f149444c5c4c4e7714707239ce9cc1de8c2803958889da14/numpy-2.4.4-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:19710a9ca9992d7174e9c52f643d4272dcd1558c5f7af7f6f8190f633bd651a7", size = 15804253, upload-time = "2026-03-29T13:21:50.753Z" }, - { url = "https://files.pythonhosted.org/packages/a2/c8/f0a45426d6d21e7ea3310a15cf90c43a14d9232c31a837702dba437f3373/numpy-2.4.4-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9b2aec6af35c113b05695ebb5749a787acd63cafc83086a05771d1e1cd1e555f", size = 16753552, upload-time = "2026-03-29T13:21:54.344Z" }, - { url = "https://files.pythonhosted.org/packages/04/74/f4c001f4714c3ad9ce037e18cf2b9c64871a84951eaa0baf683a9ca9301c/numpy-2.4.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f2cf083b324a467e1ab358c105f6cad5ea950f50524668a80c486ff1db24e119", size = 12509075, upload-time = "2026-03-29T13:21:57.644Z" }, + { url = "https://files.pythonhosted.org/packages/b3/49/ec46835a70be8fa6446c495126ac84fdb28cb2558e1620ffb87a10c8b64c/numpy-2.4.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0280e0356c0829a18d9de1cb7eee50ec22ca639878d7240307ca0943d73cd2c4", size = 16969194, upload-time = "2026-05-18T23:33:13.503Z" }, + { url = "https://files.pythonhosted.org/packages/0e/0d/f5957185c0ee2f3e12f78715aa9e3b353fd83633316c8532b38faa37e3f6/numpy-2.4.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:110f8b71aacb688ec69062bb7f6938a0f8acb01b7c1c4beb453c65b6d234584d", size = 14964111, upload-time = "2026-05-18T23:33:17.795Z" }, + { url = "https://files.pythonhosted.org/packages/ad/40/40a40ee0ddf7ceb782c49af278894b686e586d65d8c1889c8b5da01a3d7d/numpy-2.4.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:4cfe66903cc32a9921a6733d96b19bb6abf310397581bbad89c228f5abaf0ee8", size = 5469159, upload-time = "2026-05-18T23:33:20.654Z" }, + { url = "https://files.pythonhosted.org/packages/63/13/f9a8046535cb21deae82f8d03de9617e08882d274fad2539630761888228/numpy-2.4.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8155154c7c691289fe18f510b5d4657c68c67989f293f0535a91360392ff6538", size = 6798936, upload-time = "2026-05-18T23:33:22.987Z" }, + { url = "https://files.pythonhosted.org/packages/33/a8/6fa8c1a345a8c85dbb21932c447bee07c30a2c2a3f31e369c0a84b300147/numpy-2.4.6-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ab0a9c4ffb1a6d95ef519fe4247dba8eb6b18ad93999f76b7f657039acabd47", size = 15966692, upload-time = "2026-05-18T23:33:26.62Z" }, + { url = "https://files.pythonhosted.org/packages/02/03/74fe2a4cb3817d94d86402f2506554130a2f01414e299b5a843e5a8a957f/numpy-2.4.6-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:89cd468399cfd2504718f0ba50e410dca55a170b61a02ad92bb18c8a65186e93", size = 16918164, upload-time = "2026-05-18T23:33:29.955Z" }, + { url = "https://files.pythonhosted.org/packages/c5/80/3615be3313f7e7696609bc194b9f0101da809df79e859bdb84e0cd043f46/numpy-2.4.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c2d37ab77531417474168eb79d6d80b14f821a966818505d03013d0833edb7a8", size = 17322877, upload-time = "2026-05-18T23:33:34.724Z" }, + { url = "https://files.pythonhosted.org/packages/ca/ac/a691e0fe2675e370d0e08ff905adc49a1c8830e8cae03efe4477e92cd55d/numpy-2.4.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f407cb6b8e9d6d8c626bc73c945db1706035af8fd632295547bf1c9e46d092d6", size = 18651487, upload-time = "2026-05-18T23:33:38.217Z" }, + { url = "https://files.pythonhosted.org/packages/15/a7/9bc1cd626d7bf6869bfedf27b91b6ab5dd607758bf8e959d6fa80c6a59cb/numpy-2.4.6-cp311-cp311-win32.whl", hash = "sha256:ddea102b48f9e339f3948bf22040944184627a30fdf7f858667673b9c5f033c8", size = 6233945, upload-time = "2026-05-18T23:33:41.331Z" }, + { url = "https://files.pythonhosted.org/packages/c5/31/7fc6239c12bce7e931463251cca4426c465e1876ba3cc785402ef4dd8f4e/numpy-2.4.6-cp311-cp311-win_amd64.whl", hash = "sha256:1e254a00cdf42b1e4d5b3d68d33af63268d41340d8885df2ab6470f2e1500147", size = 12608406, upload-time = "2026-05-18T23:33:44.131Z" }, + { url = "https://files.pythonhosted.org/packages/27/83/140f85a466595a16382996a1bf06b2b54bcd597488921b0c9daaeeda72af/numpy-2.4.6-cp311-cp311-win_arm64.whl", hash = "sha256:ed9749eef4cbd126da3dc1d6bcb3a57f5eb7ac6a6484146bdbf743f552dfc577", size = 10479528, upload-time = "2026-05-18T23:33:50.725Z" }, + { url = "https://files.pythonhosted.org/packages/95/2a/3d7b5ac8aac24feaf9ad7ed58f45b0bbc06d37e4338ae84c9f2298b570f9/numpy-2.4.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:001fbb8e08d942dd57599e781f2472269ee7f2755fae407b4f67b2f0b17da3f1", size = 16689119, upload-time = "2026-05-18T23:33:54.065Z" }, + { url = "https://files.pythonhosted.org/packages/ea/12/92c4c131527599e8288d6918e888d88726f84d805d784b771f32408aeaef/numpy-2.4.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ebfb099f8dcf083deef3ac1ca4c1503f387cf76296fcb3816b66f5ecb5f54fdb", size = 14699246, upload-time = "2026-05-18T23:33:57.621Z" }, + { url = "https://files.pythonhosted.org/packages/ad/fe/c0a6b7b2ca128a8fb228575147073b660656734b8ebe4d76c8fd748dcc79/numpy-2.4.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:3213d622a0283a39a93d188f3cf72b26862df52fbb4ca3697f51705016523d41", size = 5204410, upload-time = "2026-05-18T23:34:00.302Z" }, + { url = "https://files.pythonhosted.org/packages/f3/d4/9770d14ba719432bb90a421bfd443872ed0f70f7264b64bec12ea363d5fd/numpy-2.4.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:357cc07a6d7b0b182ff02249616a03742827ebb1277546b5c7cd7f7620a45698", size = 6551240, upload-time = "2026-05-18T23:34:02.852Z" }, + { url = "https://files.pythonhosted.org/packages/c9/c6/50a46a6205feba2343f1d6d17438107c5dc491ed1c736e6ea68689fd906b/numpy-2.4.6-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f9fb9157b4ce2971008323afe46053787b526ef624fea915b261468a8421a0f", size = 15671012, upload-time = "2026-05-18T23:34:05.485Z" }, + { url = "https://files.pythonhosted.org/packages/99/60/14115e6364fa676c5397c2ad3004e527e9aa487abf5d0706ec81bbd08529/numpy-2.4.6-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:90f9849678c75fe7afa2d348ac842c168b0a4d3d61919687216dfc547976d853", size = 16645538, upload-time = "2026-05-18T23:34:09.265Z" }, + { url = "https://files.pythonhosted.org/packages/ae/c5/693cbe59e57db94d2231fa519ca3978dc9e19da5a8f088588f5c6e947ff2/numpy-2.4.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c1a2af6c6ef86344a6b0db6b97834208bf598db514f2b155042439b62605601a", size = 17020706, upload-time = "2026-05-18T23:34:13.053Z" }, + { url = "https://files.pythonhosted.org/packages/ef/fc/85b7c4eff9b4966ade25c2273cf7e7012e92366c032058653934b37de044/numpy-2.4.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e5805d5a22fd19c8ccff10a9561f9df94436b0545619ea579db2d3c35294bce2", size = 18368541, upload-time = "2026-05-18T23:34:17.024Z" }, + { url = "https://files.pythonhosted.org/packages/f6/81/e1b27545deedce7f4a0b348618c6b62d74e36a4dc9ccd42f3eb2f85eee32/numpy-2.4.6-cp312-cp312-win32.whl", hash = "sha256:e3eeb0aabd6bd5ce64faae67e9935203a6991b4bc2a485a767fbafb2c5125f45", size = 5962825, upload-time = "2026-05-18T23:34:20.3Z" }, + { url = "https://files.pythonhosted.org/packages/ab/ca/feab00bd44aa5fe1ad2c18f08b4d3bb92e26484b0b1d1443897809ed528c/numpy-2.4.6-cp312-cp312-win_amd64.whl", hash = "sha256:d8e8286dd7cea7895157318d1b91cdacac64c479f3cbc8dce548331728484751", size = 12321687, upload-time = "2026-05-18T23:34:23.095Z" }, + { url = "https://files.pythonhosted.org/packages/63/cf/5a6d34850a39d1093558564f77ee8e8e0bee5061151b8f05a55711001ec7/numpy-2.4.6-cp312-cp312-win_arm64.whl", hash = "sha256:4081eb135ac24158bd51cdfbef16f1c64df7063b1143f24731387137c092bec8", size = 10221482, upload-time = "2026-05-18T23:34:25.876Z" }, + { url = "https://files.pythonhosted.org/packages/fb/82/bdab26d7438c6791ca31b7c024ca37c1eab8b726ba236129005cd4a06e45/numpy-2.4.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:511dbaf848decaaaf4b4ca48032619fb3138710c4bf7da7617765edad1ef96b0", size = 16684648, upload-time = "2026-05-18T23:34:29.41Z" }, + { url = "https://files.pythonhosted.org/packages/1b/30/a80189bcc7f5e4258b3fbc3968d909d1756f54d023299ecc39ad6fdb9ef8/numpy-2.4.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bf162abab1c1a736333192707cef898e735a5ca00f38f27eeedf44b39d9e85eb", size = 14693902, upload-time = "2026-05-18T23:34:33.013Z" }, + { url = "https://files.pythonhosted.org/packages/97/12/70b5d0d7c15e1ebb8a6a84a8caa1d19e181d84fb58bb6d70aca29099dec1/numpy-2.4.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:043191bfa8eab18c776647b62723ac9dddece59743b13f49b2016094129c2b3f", size = 5198992, upload-time = "2026-05-18T23:34:36.132Z" }, + { url = "https://files.pythonhosted.org/packages/ba/8c/ebd2a8f8a83541f8d38cc5667e8c2b69cecfd30da6e45693e8158857d44b/numpy-2.4.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:6180d8b35af935aed8ece3a85e0a43f87393ae0ac87c8d2c8bd2c993f7270ef3", size = 6546944, upload-time = "2026-05-18T23:34:38.484Z" }, + { url = "https://files.pythonhosted.org/packages/bb/c5/7b863a97a91671a0338f4253bd3b5a3d3852f0692dae91711c9f4a10e787/numpy-2.4.6-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:72fbe16c6fac95aedf5937fa873445cec2110be35d8a4e9433d7501fd98dae6b", size = 15669392, upload-time = "2026-05-18T23:34:41.257Z" }, + { url = "https://files.pythonhosted.org/packages/a5/9d/3584b9984ca4c047aea75214ce1a4c4c73d849bd71b604264b7f5653f8a8/numpy-2.4.6-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a7830bab239b79cda9c08c2da014761cafb48da6150e1da17ac06283f43b6089", size = 16633220, upload-time = "2026-05-18T23:34:45.075Z" }, + { url = "https://files.pythonhosted.org/packages/05/ae/7c67fba23bd98caec7c99261f3a16072ade14813486b0282cb29846de832/numpy-2.4.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ef4aea96ce4d3b074422cb4f2f64e216bf9e213004bb58ecfdf50ea02ea8eb9a", size = 17020800, upload-time = "2026-05-18T23:34:49.065Z" }, + { url = "https://files.pythonhosted.org/packages/d9/5d/3b6725cb31d983c5e66916f5d36f6d7e5521129e4c4404d64f918292a5b6/numpy-2.4.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dfa20cc6ca228e6b155b11da03825975ce66aea520985dbbddf0f2a5a495c605", size = 18357600, upload-time = "2026-05-18T23:34:52.709Z" }, + { url = "https://files.pythonhosted.org/packages/f7/da/2ccc6c2fe8898dee01d90c75c5f5f914a23daf99e3e0f59516a08760c8b5/numpy-2.4.6-cp313-cp313-win32.whl", hash = "sha256:56b39e5e0622a09a25bf5baf62f4bcf0cb8a41ae6e2819cf49bbc5a74c083f91", size = 5961134, upload-time = "2026-05-18T23:34:55.618Z" }, + { url = "https://files.pythonhosted.org/packages/b5/cd/9cc4dc876fb065d5c220aae4d5e14826b2715331bb7618ce1fb07a679d99/numpy-2.4.6-cp313-cp313-win_amd64.whl", hash = "sha256:c4fc99836233ea196540b17ab0983aff60ed07941751930f5f4d05bc3b3b7359", size = 12318598, upload-time = "2026-05-18T23:34:58.928Z" }, + { url = "https://files.pythonhosted.org/packages/39/1e/c0bcba1f8694116485fe28fd1be698c278fcda4141c5b0e53a2aed8b12a8/numpy-2.4.6-cp313-cp313-win_arm64.whl", hash = "sha256:a7c711e21628b52034bb5ab8d1bce291f752fcc5e92accc615778acee1ff4778", size = 10222272, upload-time = "2026-05-18T23:35:02.167Z" }, + { url = "https://files.pythonhosted.org/packages/63/6d/cc5619247c8f4204e507f5883528372e4ac4bb189e579fb859a12e480b1f/numpy-2.4.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:112b06a867b235ef466ed3508ddf0238050df9c727cafb5301ac385b899189a1", size = 14821197, upload-time = "2026-05-18T23:35:05.468Z" }, + { url = "https://files.pythonhosted.org/packages/00/58/f1c39161c87d9e9bed660f1ed4bafc0e403d5ec9650b6dd77aead07d489b/numpy-2.4.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:eaf7fa2de5c0be8ae6ff8e9bea2ccd725e980541244521d8d4b5f3354a27babe", size = 5326287, upload-time = "2026-05-18T23:35:08.693Z" }, + { url = "https://files.pythonhosted.org/packages/af/57/3917ab0fd97f271a8694513581b8a36c655f111c446852c302f04ccdb6fc/numpy-2.4.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:7265a2f3d436e54ef9f2b52b5c937e6be778781bd97a590319d7348f1c1ca997", size = 6646763, upload-time = "2026-05-18T23:35:11.459Z" }, + { url = "https://files.pythonhosted.org/packages/eb/0f/037e64c494b67581ae18193d770adef354c41f3f2c8ebf865602d949bf8f/numpy-2.4.6-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f74a575920ab21fe304421a3fc28793d82e299cae9eccb37084e9fc7f3617c20", size = 15728070, upload-time = "2026-05-18T23:35:14.79Z" }, + { url = "https://files.pythonhosted.org/packages/21/a6/5d2bae9c9542eb4df16dc9c46dc79c186e9bad53805dfa5399a6023c6db0/numpy-2.4.6-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ede83e07a75dd06bc501566c1eca2afc0d61677c1472ac9ad93fdee6e638a48d", size = 16681752, upload-time = "2026-05-18T23:35:18.836Z" }, + { url = "https://files.pythonhosted.org/packages/92/14/23d1dfb410ae362cd59ce53e936b1513d545eb40db3949ced632e19a459e/numpy-2.4.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:68bb27509ac1b9a3443094260f6326150663b06abe40b73a2f81160623da5b67", size = 17086024, upload-time = "2026-05-18T23:35:22.52Z" }, + { url = "https://files.pythonhosted.org/packages/4b/6e/23595a2c642cdf3bc567877064bdd7f91c8b0038a4453cf2daf7248eafe9/numpy-2.4.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a0df0043bdb289bde1f62da130d20df23d58b45429f752bc7a8fc5325a225ecd", size = 18403398, upload-time = "2026-05-18T23:35:26.398Z" }, + { url = "https://files.pythonhosted.org/packages/8a/90/0ac3bc947217e66dec77e7cbc6a1979d1af70b6461b82f620d3bccd5e4c8/numpy-2.4.6-cp313-cp313t-win32.whl", hash = "sha256:29a287e0cf63ff528da061de6b9f64a4618da591ca1046aafc54062e40ca7eab", size = 6084971, upload-time = "2026-05-18T23:35:29.387Z" }, + { url = "https://files.pythonhosted.org/packages/77/71/5673e351671a1d2bd6063b91b44f70c0affea7d1516fa7a6572941ba4aa1/numpy-2.4.6-cp313-cp313t-win_amd64.whl", hash = "sha256:25c692919ac5a01f170a3bfcd62d745b24fd095c353d50812637d6fcab442e75", size = 12458532, upload-time = "2026-05-18T23:35:32.175Z" }, + { url = "https://files.pythonhosted.org/packages/3f/88/19d3503c5046e688f049274b27a3ef3d771152fa80d3ba3d01a3dff61abe/numpy-2.4.6-cp313-cp313t-win_arm64.whl", hash = "sha256:1e978ec1e8bd0e0e4de6bb75de9d30cbb74db6b6a2bb727618613703ca0167dd", size = 10291881, upload-time = "2026-05-18T23:35:35.465Z" }, + { url = "https://files.pythonhosted.org/packages/f8/91/3ab2044d05fd16d343c5ac2e69b127f1b2854040dd20b193257c78028bd3/numpy-2.4.6-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06ca2f61ec4385a07a6977c55ba998a4466c123642b4a32694d3128fce18c079", size = 16683458, upload-time = "2026-05-18T23:35:38.353Z" }, + { url = "https://files.pythonhosted.org/packages/8e/62/764ce66fa4147ae6d73071a3abf804ffe606f174618697c571acdf26a7c9/numpy-2.4.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:38efbc8de75c7a0fc1ac190162d892787f3f47b57cc291231aafee36b80982b7", size = 14704559, upload-time = "2026-05-18T23:35:42.14Z" }, + { url = "https://files.pythonhosted.org/packages/60/61/23f27c172f022e04025b7dc2367f4d63c1a398120607ec896228649a6f48/numpy-2.4.6-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:d581b735e177fdcdce6fed8e7e8880a3fb6ee4e3653a3ac6af01c6f4c03effc5", size = 5209716, upload-time = "2026-05-18T23:35:45.377Z" }, + { url = "https://files.pythonhosted.org/packages/03/71/21cf70dc6ea3e3acb95fc53a265b2fc248b981f0194ceb5b475271b8809d/numpy-2.4.6-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:0a041d3d761dc3c35cc56ce0351506a02bcbc25f7b169f652435141a17db9096", size = 6543947, upload-time = "2026-05-18T23:35:47.926Z" }, + { url = "https://files.pythonhosted.org/packages/d5/91/64288395ee1799bd2e0b04a305dce9666da90c961e1f3fe982a05ee1c036/numpy-2.4.6-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:40fdc1ae7125e518ea98e53e69a4ebc27e1fd50510c47b7ea130cf21e5e1d42b", size = 15685197, upload-time = "2026-05-18T23:35:50.863Z" }, + { url = "https://files.pythonhosted.org/packages/f3/eb/ebffaa97dc55502df69584a8f0dcf07f69a3e0b3e2323670a2722db9aa39/numpy-2.4.6-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a2c306dea656c12c68f51f4cea133cbe78ca7435eb28c735eac1d3ebe73be6e8", size = 16638245, upload-time = "2026-05-18T23:35:54.752Z" }, + { url = "https://files.pythonhosted.org/packages/b8/0b/54f9da33128d7e350fab89c7455902eeae70349ee52bddb448dc4a576f45/numpy-2.4.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:33111801a01c12a8a1e3721f0a9232f8cfc8ae2c6b7098167e6f623c6073f402", size = 17036587, upload-time = "2026-05-18T23:35:58.355Z" }, + { url = "https://files.pythonhosted.org/packages/b6/f0/fdebc1052db1cc37c64beb22072d67cd6d1c71adca1299f53dec2b5e20d3/numpy-2.4.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ae506e6902902557576a26ff33eda8695e7ecb3cb36c3b573a0765dee114ebdb", size = 18363226, upload-time = "2026-05-18T23:36:02.845Z" }, + { url = "https://files.pythonhosted.org/packages/aa/b4/298628d98c72b57e57f7165ae6a481a1deaf6f3c28262a6e4c739c275930/numpy-2.4.6-cp314-cp314-win32.whl", hash = "sha256:aaf159caa35993cb1f56fb9b8e4610d35758e7ca005412eb1daa856a78c9c4b1", size = 6010196, upload-time = "2026-05-18T23:36:05.92Z" }, + { url = "https://files.pythonhosted.org/packages/df/ac/46de6dda46478f7942f839e094970be2d4a861e005c4b3bf07c92e291a09/numpy-2.4.6-cp314-cp314-win_amd64.whl", hash = "sha256:b507f5c4c1d508876d1819b6bf9a49d365b96320b5d4993426b33a23ca4b8261", size = 12450334, upload-time = "2026-05-18T23:36:09.107Z" }, + { url = "https://files.pythonhosted.org/packages/78/92/b8b798ac784102c0da830d2257d59358e3d3d90d1e2b3f2575dad976c5cf/numpy-2.4.6-cp314-cp314-win_arm64.whl", hash = "sha256:6f41ae150c4e32db4f3310cdaf64b1593a03dbabe29eec77fc9b50fe64061df6", size = 10495678, upload-time = "2026-05-18T23:36:12.766Z" }, + { url = "https://files.pythonhosted.org/packages/30/34/ec28d1aa8115971537c01469ab2011ee96827930f0a124de1000cc2a7ed7/numpy-2.4.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ece3d2cfe132e7d51f44a832b303895e6f2d499c5e74dfbdb06ee246147a304a", size = 14823672, upload-time = "2026-05-18T23:36:16.473Z" }, + { url = "https://files.pythonhosted.org/packages/16/bd/f6d1fede4e54e8042a7ff97bb495510f3c220f94bcd9e8b228e87c92cc0d/numpy-2.4.6-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:e3e5193ef5a3dc73bceee50f7fdc2c90dbb76c42df8d8fae3d1067a583df579e", size = 5328731, upload-time = "2026-05-18T23:36:19.767Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f0/e105b9e2fd728a9910103884decd6951d9dd73896b914a98d9a231de02ee/numpy-2.4.6-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:17f9ade344e7d9b464a084d69bcf18fc691cb1db67c62ed80820bf4926d78f0e", size = 6649805, upload-time = "2026-05-18T23:36:22.266Z" }, + { url = "https://files.pythonhosted.org/packages/82/dd/1206a7ca6ab15e3f02069707ca96222e202af681bb73756da7527f3cb837/numpy-2.4.6-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9cd5ffd25db4e7ba6a375693b3fc0fc1791ec636c17db3720da19bde7180ec43", size = 15730496, upload-time = "2026-05-18T23:36:25.713Z" }, + { url = "https://files.pythonhosted.org/packages/51/e7/38d3ea825dcab85a591734decb2f6c67caa7c8367d374df1a1c3842f9b07/numpy-2.4.6-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7d92c3819208a60205a12a245c91ad70cb0a85336659b19b834205573ac8456e", size = 16679616, upload-time = "2026-05-18T23:36:29.652Z" }, + { url = "https://files.pythonhosted.org/packages/93/b7/caabfdf53edf663e0b4eb74d7d405d83baef09eb5e83bcd32d601d72b93e/numpy-2.4.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e85b752a1e912b70eaad4fafbd4d1238007ab221de2009b9a2f5ae7461239895", size = 17085145, upload-time = "2026-05-18T23:36:33.449Z" }, + { url = "https://files.pythonhosted.org/packages/f9/45/68d7c33a6bcf3e5aa3bdbd57a367e6f615286dfd6482f97e8ffeb734306e/numpy-2.4.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:29cb7f67d10b479ff07c17d33e39f78c07f71c40ef30d63c153d340e96cd3fb4", size = 18403813, upload-time = "2026-05-18T23:36:37.369Z" }, + { url = "https://files.pythonhosted.org/packages/9c/50/0753655aa844c99cd9e018aacf76f130f1bd81d881bb74bc0aef5d73a8ba/numpy-2.4.6-cp314-cp314t-win32.whl", hash = "sha256:260a5d70215b61ab4fadf5c7baacd64821842975eea312125ed3c39a6391b063", size = 6156982, upload-time = "2026-05-18T23:36:40.817Z" }, + { url = "https://files.pythonhosted.org/packages/b2/d4/7c67becf668f973cb490cec3e98dfd799d866f9c989a54d355672cfa0db6/numpy-2.4.6-cp314-cp314t-win_amd64.whl", hash = "sha256:81a1cca95ed5bb92aa8b10dd2cdc9a0d3853a50fad926c28b5d7e8ea54389627", size = 12638908, upload-time = "2026-05-18T23:36:43.996Z" }, + { url = "https://files.pythonhosted.org/packages/43/bb/e1c71a4295b1b1d1393d50dbb4f2a36283c6859d9d3892e84f00ec5a91d5/numpy-2.4.6-cp314-cp314t-win_arm64.whl", hash = "sha256:0c9136e14ed34a9e343a31c533d78a9813a69a3148332bce5e9821cb2f996e66", size = 10565867, upload-time = "2026-05-18T23:36:47.114Z" }, + { url = "https://files.pythonhosted.org/packages/de/12/b422cc84439adc0d00de605bf4a308890ae5c26f2c71fbd73e5d08fbb0dd/numpy-2.4.6-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:55cced7c52e981362f708ad635198e97a752dfba412cc03c23bbf3bd8d5cd662", size = 16847511, upload-time = "2026-05-18T23:36:50.673Z" }, + { url = "https://files.pythonhosted.org/packages/44/53/f481bef68011740f8849418d82db07230e825013f31f4eef5ba5b805316a/numpy-2.4.6-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d6da64deb6b8ed903e7560180a92f2d804ee1ba5eeb849ac2748b8c1aba1f6d7", size = 14889064, upload-time = "2026-05-18T23:36:53.879Z" }, + { url = "https://files.pythonhosted.org/packages/7f/57/42ed575c10ced8af951d426bc4e1f8aff16fd851db33f067036215a7f860/numpy-2.4.6-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:68a5124b13fa6cc2086764a20005d30bc0548146f7f5322f02fce212ca14317f", size = 5394157, upload-time = "2026-05-18T23:36:57.194Z" }, + { url = "https://files.pythonhosted.org/packages/6a/ef/f66cc724fcc36c1e364c67f51ae9146090b8b584f27d58b97fdae3edd737/numpy-2.4.6-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:948424b06129ce883307e8cff868c31396d8dc7630a59c61d70d98dbe70f222c", size = 6708728, upload-time = "2026-05-18T23:36:59.575Z" }, + { url = "https://files.pythonhosted.org/packages/1a/9c/c531f2293b91265d8b48e9b329f54fdd7ffae73cb4134ea10cca4237e9cc/numpy-2.4.6-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5dbbdb29840ca3d91ee0fece42fc29278886d908280bfec0a5846c6f901a3eb0", size = 15798374, upload-time = "2026-05-18T23:37:02.674Z" }, + { url = "https://files.pythonhosted.org/packages/1a/b0/413077f6b1153ed3cba361401c6783bbad6114804a000cc22eb71c13e190/numpy-2.4.6-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8ad03c0965fb3c692200e74d458ca28c1dbb4ce96f9a479a8aa041ad5fabca02", size = 16747286, upload-time = "2026-05-18T23:37:06.327Z" }, + { url = "https://files.pythonhosted.org/packages/15/ce/e5ec180bc41812edcd8daeb8639d205622c0e8c02259d8ab25a0201b3c2a/numpy-2.4.6-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:2803abfebfc990042cd494d8ce2d5f82e9d847af6d35ec486923aa19dbad5e73", size = 12504263, upload-time = "2026-05-18T23:37:09.715Z" }, ] [[package]] @@ -1687,82 +1732,79 @@ wheels = [ [[package]] name = "onnxruntime" -version = "1.24.4" +version = "1.26.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "flatbuffers" }, { name = "numpy" }, { name = "packaging" }, { name = "protobuf" }, - { name = "sympy" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/60/69/6c40720201012c6af9aa7d4ecdd620e521bd806dc6269d636fdd5c5aeebe/onnxruntime-1.24.4-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:0bdfce8e9a6497cec584aab407b71bf697dac5e1b7b7974adc50bf7533bdb3a2", size = 17332131, upload-time = "2026-03-17T22:05:49.005Z" }, - { url = "https://files.pythonhosted.org/packages/38/e9/8c901c150ce0c368da38638f44152fb411059c0c7364b497c9e5c957321a/onnxruntime-1.24.4-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:046ff290045a387676941a02a8ae5c3ebec6b4f551ae228711968c4a69d8f6b7", size = 15152472, upload-time = "2026-03-17T22:03:26.176Z" }, - { url = "https://files.pythonhosted.org/packages/d5/b6/7a4df417cdd01e8f067a509e123ac8b31af450a719fa7ed81787dd6057ec/onnxruntime-1.24.4-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e54ad52e61d2d4618dcff8fa1480ac66b24ee2eab73331322db1049f11ccf330", size = 17222993, upload-time = "2026-03-17T22:04:34.485Z" }, - { url = "https://files.pythonhosted.org/packages/dd/59/8febe015f391aa1757fa5ba82c759ea4b6c14ef970132efb5e316665ba61/onnxruntime-1.24.4-cp311-cp311-win_amd64.whl", hash = "sha256:b43b63eb24a2bc8fc77a09be67587a570967a412cccb837b6245ccb546691153", size = 12594863, upload-time = "2026-03-17T22:05:38.749Z" }, - { url = "https://files.pythonhosted.org/packages/32/84/4155fcd362e8873eb6ce305acfeeadacd9e0e59415adac474bea3d9281bb/onnxruntime-1.24.4-cp311-cp311-win_arm64.whl", hash = "sha256:e26478356dba25631fb3f20112e345f8e8bf62c499bb497e8a559f7d69cf7e7b", size = 12259895, upload-time = "2026-03-17T22:05:28.812Z" }, - { url = "https://files.pythonhosted.org/packages/d7/38/31db1b232b4ba960065a90c1506ad7a56995cd8482033184e97fadca17cc/onnxruntime-1.24.4-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:cad1c2b3f455c55678ab2a8caa51fb420c25e6e3cf10f4c23653cdabedc8de78", size = 17341875, upload-time = "2026-03-17T22:05:51.669Z" }, - { url = "https://files.pythonhosted.org/packages/aa/60/c4d1c8043eb42f8a9aa9e931c8c293d289c48ff463267130eca97d13357f/onnxruntime-1.24.4-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1a5c5a544b22f90859c88617ecb30e161ee3349fcc73878854f43d77f00558b5", size = 15172485, upload-time = "2026-03-17T22:03:32.182Z" }, - { url = "https://files.pythonhosted.org/packages/6d/ab/5b68110e0460d73fad814d5bd11c7b1ddcce5c37b10177eb264d6a36e331/onnxruntime-1.24.4-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0d640eb9f3782689b55cfa715094474cd5662f2f137be6a6f847a594b6e9705c", size = 17244912, upload-time = "2026-03-17T22:04:37.251Z" }, - { url = "https://files.pythonhosted.org/packages/8b/f4/6b89e297b93704345f0f3f8c62229bee323ef25682a3f9b4f89a39324950/onnxruntime-1.24.4-cp312-cp312-win_amd64.whl", hash = "sha256:535b29475ca42b593c45fbb2152fbf1cdf3f287315bf650e6a724a0a1d065cdb", size = 12596856, upload-time = "2026-03-17T22:05:41.224Z" }, - { url = "https://files.pythonhosted.org/packages/43/06/8b8ec6e9e6a474fcd5d772453f627ad4549dfe3ab8c0bf70af5afcde551b/onnxruntime-1.24.4-cp312-cp312-win_arm64.whl", hash = "sha256:e6214096e14b7b52e3bee1903dc12dc7ca09cb65e26664668a4620cc5e6f9a90", size = 12270275, upload-time = "2026-03-17T22:05:31.132Z" }, - { url = "https://files.pythonhosted.org/packages/e9/f0/8a21ec0a97e40abb7d8da1e8b20fb9e1af509cc6d191f6faa75f73622fb2/onnxruntime-1.24.4-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:e99a48078baaefa2b50fe5836c319499f71f13f76ed32d0211f39109147a49e0", size = 17341922, upload-time = "2026-03-17T22:03:56.364Z" }, - { url = "https://files.pythonhosted.org/packages/8b/25/d7908de8e08cee9abfa15b8aa82349b79733ae5865162a3609c11598805d/onnxruntime-1.24.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc4aaed1e5e1aaacf2343c838a30a7c3ade78f13eeb16817411f929d04040a13", size = 15172290, upload-time = "2026-03-17T22:03:37.124Z" }, - { url = "https://files.pythonhosted.org/packages/7f/72/105ec27a78c5aa0154a7c0cd8c41c19a97799c3b12fc30392928997e3be3/onnxruntime-1.24.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e30c972bc02e072911aabb6891453ec73795386c0af2b761b65444b8a4c4745f", size = 17244738, upload-time = "2026-03-17T22:04:40.625Z" }, - { url = "https://files.pythonhosted.org/packages/05/fb/a592736d968c2f58e12de4d52088dda8e0e724b26ad5c0487263adb45875/onnxruntime-1.24.4-cp313-cp313-win_amd64.whl", hash = "sha256:3b6ba8b0181a3aa88edab00eb01424ffc06f42e71095a91186c2249415fcff93", size = 12597435, upload-time = "2026-03-17T22:05:43.826Z" }, - { url = "https://files.pythonhosted.org/packages/ad/04/ae2479e9841b64bd2eb44f8a64756c62593f896514369a11243b1b86ca5c/onnxruntime-1.24.4-cp313-cp313-win_arm64.whl", hash = "sha256:71d6a5c1821d6e8586a024000ece458db8f2fc0ecd050435d45794827ce81e19", size = 12269852, upload-time = "2026-03-17T22:05:33.353Z" }, - { url = "https://files.pythonhosted.org/packages/b4/af/a479a536c4398ffaf49fbbe755f45d5b8726bdb4335ab31b537f3d7149b8/onnxruntime-1.24.4-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1700f559c8086d06b2a4d5de51e62cb4ff5e2631822f71a36db8c72383db71ee", size = 15176861, upload-time = "2026-03-17T22:03:40.143Z" }, - { url = "https://files.pythonhosted.org/packages/be/13/19f5da70c346a76037da2c2851ecbf1266e61d7f0dcdb887c667210d4608/onnxruntime-1.24.4-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c74e268dc808e61e63784d43f9ddcdaf50a776c2819e8bd1d1b11ef64bf7e36", size = 17247454, upload-time = "2026-03-17T22:04:46.643Z" }, - { url = "https://files.pythonhosted.org/packages/89/db/b30dbbd6037847b205ab75d962bc349bf1e46d02a65b30d7047a6893ffd6/onnxruntime-1.24.4-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:fbff2a248940e3398ae78374c5a839e49a2f39079b488bc64439fa0ec327a3e4", size = 17343300, upload-time = "2026-03-17T22:03:59.223Z" }, - { url = "https://files.pythonhosted.org/packages/61/88/1746c0e7959961475b84c776d35601a21d445f463c93b1433a409ec3e188/onnxruntime-1.24.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e2b7969e72d8cb53ffc88ab6d49dd5e75c1c663bda7be7eb0ece192f127343d1", size = 15175936, upload-time = "2026-03-17T22:03:43.671Z" }, - { url = "https://files.pythonhosted.org/packages/5f/ba/4699cde04a52cece66cbebc85bd8335a0d3b9ad485abc9a2e15946a1349d/onnxruntime-1.24.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14ed1f197fab812b695a5eaddb536c635e58a2fbbe50a517c78f082cc6ce9177", size = 17246432, upload-time = "2026-03-17T22:04:49.58Z" }, - { url = "https://files.pythonhosted.org/packages/ef/60/4590910841bb28bd3b4b388a9efbedf4e2d2cca99ddf0c863642b4e87814/onnxruntime-1.24.4-cp314-cp314-win_amd64.whl", hash = "sha256:311e309f573bf3c12aa5723e23823077f83d5e412a18499d4485c7eb41040858", size = 12903276, upload-time = "2026-03-17T22:05:46.349Z" }, - { url = "https://files.pythonhosted.org/packages/7f/6f/60e2c0acea1e1ac09b3e794b5a19c166eebf91c0b860b3e6db8e74983fda/onnxruntime-1.24.4-cp314-cp314-win_arm64.whl", hash = "sha256:3f0b910e86b759a4732663ec61fd57ac42ee1b0066f68299de164220b660546d", size = 12594365, upload-time = "2026-03-17T22:05:35.795Z" }, - { url = "https://files.pythonhosted.org/packages/cf/68/0c05d10f8f6c40fe0912ebec0d5a33884aaa2af2053507e864dab0883208/onnxruntime-1.24.4-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa12ddc54c9c4594073abcaa265cd9681e95fb89dae982a6f508a794ca42e661", size = 15176889, upload-time = "2026-03-17T22:03:48.021Z" }, - { url = "https://files.pythonhosted.org/packages/6c/1d/1666dc64e78d8587d168fec4e3b7922b92eb286a2ddeebcf6acb55c7dc82/onnxruntime-1.24.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1cc6a518255f012134bc791975a6294806be9a3b20c4a54cca25194c90cf731", size = 17247021, upload-time = "2026-03-17T22:04:52.377Z" }, + { url = "https://files.pythonhosted.org/packages/d4/81/29a9eb470994a75eb7b3ccf32be314d7c66675a00ac7b50294816cc2db27/onnxruntime-1.26.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:ee1109ef4ef27cad90e823399e61e03b3c6c7bfe0fb820b4baf3678c15be8b3c", size = 18005108, upload-time = "2026-05-08T19:08:11.728Z" }, + { url = "https://files.pythonhosted.org/packages/66/c7/73efa6c8a4000c38fcc14947d84f234a17e5d66f203b37b7f1ad4a7b46eb/onnxruntime-1.26.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:35c7c7b0ac2e02001d28fab6c9fc24e9abc5e6faa35e6e19c63cecf1406ba89f", size = 16043752, upload-time = "2026-05-08T19:07:10.707Z" }, + { url = "https://files.pythonhosted.org/packages/b6/3f/8de630f595daf6ce884d4dd95afd2a60e70ec6572e52bfee3aa2229befab/onnxruntime-1.26.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11a8df4dcfe9ad5ff0bd71a7571dbed019fabc7594676c89fe8b86ea029c246f", size = 18176043, upload-time = "2026-05-08T19:07:33.735Z" }, + { url = "https://files.pythonhosted.org/packages/9c/21/9f041de20787cd85498bd48e0ec4d098bf2a6c486e25b24b8dae1bf492b2/onnxruntime-1.26.0-cp311-cp311-win_amd64.whl", hash = "sha256:e6456718125fd777c673f3b78d4a9ab58d6adea641e9afae85ee6444f0e0e9a9", size = 13023165, upload-time = "2026-05-08T19:08:00.633Z" }, + { url = "https://files.pythonhosted.org/packages/0e/82/3b9fe0ead2557cc3adf74c74c141bd1c7c4c6a9548c610af37df199f4512/onnxruntime-1.26.0-cp311-cp311-win_arm64.whl", hash = "sha256:cd920e45b730e4a87833e2910d8ca375aaca9da6ccc09e24bce463b3356d637f", size = 12789514, upload-time = "2026-05-08T19:07:49.433Z" }, + { url = "https://files.pythonhosted.org/packages/81/b1/d111b1df656761f980d9e298a60039a9cb66036b1d039e777537743d0ac3/onnxruntime-1.26.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:05b028781b322ad74b57ce5b50aa5280bb1fe96ceec334628ade681e0b24c1ac", size = 18016624, upload-time = "2026-05-12T00:41:01.735Z" }, + { url = "https://files.pythonhosted.org/packages/f6/a0/3f9d896a0385a36bd04345d6d0b802821a5782adde562e7e135f6bb71c73/onnxruntime-1.26.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:91f2bb870a4b9224eba0a6728c1fa7a9e552b8e59e1083c51fbbc3d013f2b5c0", size = 16052692, upload-time = "2026-05-08T19:07:13.829Z" }, + { url = "https://files.pythonhosted.org/packages/7c/43/2a4e04f8dbeffad19bbcced4bcd4289bf478921518437404d6b92bdf213b/onnxruntime-1.26.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9b6dd70599005bd1bf29779f04a91978b92b5e719c11a20068a8f8e535f725b6", size = 18185439, upload-time = "2026-05-08T19:07:36.299Z" }, + { url = "https://files.pythonhosted.org/packages/44/fc/026d0a7162b9c2153dac292baea9e027c42304dc1d9dc6f8ff5b4cfbaedd/onnxruntime-1.26.0-cp312-cp312-win_amd64.whl", hash = "sha256:a26374dc7fbcaae593601086b242120e13f2310558df0991da6dd8b8fac00414", size = 13026427, upload-time = "2026-05-08T19:08:03.503Z" }, + { url = "https://files.pythonhosted.org/packages/3e/27/1dcf88e45e4c69db5f7b106f2dacc3801ba98994e082ca03e1dfdf7bfe57/onnxruntime-1.26.0-cp312-cp312-win_arm64.whl", hash = "sha256:54a8053410fd31fd66469bd754fcfe8a4df9f7eb44756b4b5479bf50c842d948", size = 12796647, upload-time = "2026-05-08T19:07:52.108Z" }, + { url = "https://files.pythonhosted.org/packages/cf/a2/c801242685e0ce48a4ca51dfafbb588765e0446397e123be53ba5598f3f5/onnxruntime-1.26.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:ccce19c5f771b8268902f77d9fed9e88f9499465d6780808faa6611a789d33f0", size = 18016563, upload-time = "2026-05-08T19:07:28.081Z" }, + { url = "https://files.pythonhosted.org/packages/e2/64/0492c0b1db04e29b2630c87cfa36f9d6872b1ca8614b90c5cad58fac7d76/onnxruntime-1.26.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bdbed8cf3b672b66acb032f33a253bc27f42bce6ece48ae3fab4fa483a5e96e0", size = 16052634, upload-time = "2026-05-08T19:07:16.885Z" }, + { url = "https://files.pythonhosted.org/packages/3d/26/4d09ddc755a84fc8d5e192991626b0e0680e8f6c5d58f4f1d05c42bc48cf/onnxruntime-1.26.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c07af6fc6d5557835f2b6ee7a96d8b3235d0c57a8e230efdedaee106a8a3cbc6", size = 18185632, upload-time = "2026-05-08T19:07:38.756Z" }, + { url = "https://files.pythonhosted.org/packages/77/89/3e52249aa08fa301e217ecba07b5246a8338fa2b401e109326e3fc5be0f9/onnxruntime-1.26.0-cp313-cp313-win_amd64.whl", hash = "sha256:61bec80655efa460591c2bc655392d57d2650ce85533a6b9b3b7a790d7ea7916", size = 13026751, upload-time = "2026-05-08T19:08:06.2Z" }, + { url = "https://files.pythonhosted.org/packages/06/b3/c1c8782b14af6797c303de132d6eef26a9fb80dfacd3750ce57911d11c6b/onnxruntime-1.26.0-cp313-cp313-win_arm64.whl", hash = "sha256:a6677545ff451e3539a02746d2f207d8c5baa4a0a818886bb9d6a6eb9511ee89", size = 12796807, upload-time = "2026-05-08T19:07:54.879Z" }, + { url = "https://files.pythonhosted.org/packages/c3/f5/47b0676408abec652c14b84d7173e389837832d850c24f87184277313e8d/onnxruntime-1.26.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5e016edc15d3c19f36807e1c6b10be5b27807688c32720f91b5ae480a95215d0", size = 16057265, upload-time = "2026-05-08T19:07:19.603Z" }, + { url = "https://files.pythonhosted.org/packages/3b/45/33ab6deeef010ca844c877dd618cebc079590bbe52d2a3678e7223b1b908/onnxruntime-1.26.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f5fc48a91a046a6a5c9b147f83fb41d65d24d24923373b222cdd248f0f4f4aac", size = 18197590, upload-time = "2026-05-08T19:07:41.422Z" }, + { url = "https://files.pythonhosted.org/packages/40/89/17546c1c20f6bfc3ae41c22152378a26edfea918af3129e2139dcd7c99f3/onnxruntime-1.26.0-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:33a791f31432a3af1a96db5e54818b37aba5e5eefc2e6af5794c10a9118a9993", size = 18019724, upload-time = "2026-05-08T19:07:30.723Z" }, + { url = "https://files.pythonhosted.org/packages/bb/24/89457a35f6af29538a76647f2c18c3a28277e6c19234c847e7b4b7c19860/onnxruntime-1.26.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e90c00732c4553618103149d93f688e8c3063017938f8983e21a71d9f3b6d22e", size = 16054821, upload-time = "2026-05-08T19:07:22.348Z" }, + { url = "https://files.pythonhosted.org/packages/12/f9/15b2e1815cf570d238e0135529f80d2dce64e8e8818a1489cae83823c5c6/onnxruntime-1.26.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01498e80ba8988428d08c2d51b1338f89e3de2a93e6ffe555f79c68f26a5c06b", size = 18185815, upload-time = "2026-05-08T19:07:44.179Z" }, + { url = "https://files.pythonhosted.org/packages/d7/65/2e11055faf015e4b07f45b513fa49b391baf2e19d92d77d73ebee13c1004/onnxruntime-1.26.0-cp314-cp314-win_amd64.whl", hash = "sha256:7ead61450d8405167c87dd3a31d8da1d576b490a57dab1aa8b82a7da6825f5aa", size = 13349887, upload-time = "2026-05-08T19:08:08.671Z" }, + { url = "https://files.pythonhosted.org/packages/19/e4/0f9d1a5718b1781c610c1e354765a3820597081754277a6a9a2b50705702/onnxruntime-1.26.0-cp314-cp314-win_arm64.whl", hash = "sha256:31d71a53490e46910877d0902b5ad99c69a5955e5c7ea6c82863519410e1ba7c", size = 13140121, upload-time = "2026-05-08T19:07:57.804Z" }, + { url = "https://files.pythonhosted.org/packages/1c/42/3b8e635f067d06d9f45bede470b8d539d101a4166c272213158dfd08b6ce/onnxruntime-1.26.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d7b6d258fb78fdfcf049795bcfaa74dcb90ae7baa277afd21e6fd28b83f2c496", size = 16057240, upload-time = "2026-05-08T19:07:25.163Z" }, + { url = "https://files.pythonhosted.org/packages/93/99/f2be40a31b908d96b861ae0ce98582fa376c18a7f816b9d5eb4cd6aa0a4c/onnxruntime-1.26.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4eefd386a45202aefb7a5132b94f32df9d506c9edcc7faf2fc60d65183f4b183", size = 18197382, upload-time = "2026-05-08T19:07:46.965Z" }, ] [[package]] name = "onnxruntime-gpu" -version = "1.24.4" +version = "1.26.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "flatbuffers" }, { name = "numpy" }, { name = "packaging" }, { name = "protobuf" }, - { name = "sympy" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/9f/13/e080d758f2b60f71abe518c707135fb121d6a3019e0761ead89b5283ac3d/onnxruntime_gpu-1.24.4-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c2a698659271c28220b3f56fe9b63f70eae3b3c36afa544201bf750b929a36dc", size = 252761835, upload-time = "2026-03-17T22:03:45.584Z" }, - { url = "https://files.pythonhosted.org/packages/d2/07/036825cbe30f91ea8574a18a759beccd0ea31b7b71e17f6a9ee9304b51d2/onnxruntime_gpu-1.24.4-cp311-cp311-win_amd64.whl", hash = "sha256:1a799a16e5f1ff4d6a9e5f72d750849ab0fe534da8d323ae4a5d8d8bb7daeca8", size = 207193563, upload-time = "2026-03-17T21:58:28.097Z" }, - { url = "https://files.pythonhosted.org/packages/d0/2c/5b3fd4748cf7ed291eae541a37e426efc20ea04cb6e6a05768304ab0aa41/onnxruntime_gpu-1.24.4-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eb0e38f0c1ef3b76ae0081c8e51eed20dd8925aa916f0fc6f9b8b17d05610e99", size = 252765531, upload-time = "2026-03-17T22:03:57.528Z" }, - { url = "https://files.pythonhosted.org/packages/f2/86/70cecfdab1e963cc7f8c11e72040dfcd5cff85b1de2de74deba9611e0059/onnxruntime_gpu-1.24.4-cp312-cp312-win_amd64.whl", hash = "sha256:da5c1e327d8e119a831be2790e69f93cf6daab9145ed0aca7577f412a620f709", size = 207197978, upload-time = "2026-03-17T21:58:38.43Z" }, - { url = "https://files.pythonhosted.org/packages/be/4e/56d11203d7a35e7d6a5ea735f5fecb8673537038c07323e8d3090a896547/onnxruntime_gpu-1.24.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bbdaa73f9055fb2a177425edbed651a1843a6239f9d5430e284f4e5f65440a33", size = 252763446, upload-time = "2026-03-17T22:04:09.515Z" }, - { url = "https://files.pythonhosted.org/packages/fa/bc/35f3a37226d7a28c84b8b456f52237ccd39eb7111114bcf9ac340178e1ec/onnxruntime_gpu-1.24.4-cp313-cp313-win_amd64.whl", hash = "sha256:6be8bf2048777c517fca33eb61e114969fa326619feaa789d8c75f24337ea762", size = 207198775, upload-time = "2026-03-17T21:58:48.768Z" }, - { url = "https://files.pythonhosted.org/packages/37/83/0c851882051b38f245f44b4a51d6232b95b8cd5d334b2c1260f2d796834f/onnxruntime_gpu-1.24.4-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e4b348a078ced73fc577d21b83992fd2187edd10c233729c8d01b000b8543525", size = 252774594, upload-time = "2026-03-17T22:04:24.957Z" }, - { url = "https://files.pythonhosted.org/packages/3e/5b/82b27f766b64f97c9a98b772dc07b608e900bd2faafdfa176b86d20be7f8/onnxruntime_gpu-1.24.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:af9dd7ef92d94c75e5523cf070e180f3d8cdbb2fc007dcea97ba71b03e3b96d6", size = 252765395, upload-time = "2026-03-17T22:04:37.305Z" }, - { url = "https://files.pythonhosted.org/packages/5d/95/fa8c48e03790c979167d08164b34a8442c7074bca4c7253b4455497025de/onnxruntime_gpu-1.24.4-cp314-cp314-win_amd64.whl", hash = "sha256:4dde3d2f1039060c42b12fd446fc0da5b836cc65dceb4020ca60a04cffa1d90d", size = 209597109, upload-time = "2026-03-17T21:58:58.136Z" }, - { url = "https://files.pythonhosted.org/packages/1a/98/7707edefcecf69d6c45b83a83f13ac58257017b4eaf58772668d302f849f/onnxruntime_gpu-1.24.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:097c6f53e99ee35f21d0fdba76ca283b92465a0e364c6f0209cb9653c424e2a4", size = 252776951, upload-time = "2026-03-17T22:04:49.715Z" }, + { url = "https://files.pythonhosted.org/packages/dc/0f/696b4f94a282952239ffed39db78cb17a00ad993acd929cfac010a09759b/onnxruntime_gpu-1.26.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4fa231294c2643911d2a7d16469c4808b0bdcdc4b5f4063d3a53744ce25b683a", size = 276956564, upload-time = "2026-05-08T19:15:35.133Z" }, + { url = "https://files.pythonhosted.org/packages/ef/26/a417b7a1cdbbf56a389bfcd399255be23f30e5721e3e519472fe8dde9c99/onnxruntime_gpu-1.26.0-cp311-cp311-win_amd64.whl", hash = "sha256:cc5329aad02d9745cc3ae9cdb185bfa1aad242a7bf89b8c471280002ec40f98a", size = 226539455, upload-time = "2026-05-08T19:09:24.631Z" }, + { url = "https://files.pythonhosted.org/packages/94/fd/59bee7cffaa435da44fefdeb63e29c61de4dbfa4b279852f59cd02c042ae/onnxruntime_gpu-1.26.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3c01119ed4d9449d60367fa8ccffcd02bd3fe736754284e4b198d131f54edad6", size = 276971796, upload-time = "2026-05-08T19:15:46.192Z" }, + { url = "https://files.pythonhosted.org/packages/a4/e4/9b378a5466ea0bed65e5beb8e09254973c580a6522810a38afbcc45e5105/onnxruntime_gpu-1.26.0-cp312-cp312-win_amd64.whl", hash = "sha256:5f49c44689894650990e4c8a857d2edafc276fbd79bba57ceb224bd18d25d491", size = 226548963, upload-time = "2026-05-08T19:09:34.925Z" }, + { url = "https://files.pythonhosted.org/packages/dd/97/fe8979f44b9275654b42f7bb556e30789b71a1b22998c83b540df2b1b774/onnxruntime_gpu-1.26.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cfda2fad535595bfc3e570eb588092717711dcb2957656d814695e0c9ceb1508", size = 276974871, upload-time = "2026-05-08T19:15:58.052Z" }, + { url = "https://files.pythonhosted.org/packages/67/3f/59f1777a394625ecc9a85636de57dc47c25dbb5f888da050f1463955a0ce/onnxruntime_gpu-1.26.0-cp313-cp313-win_amd64.whl", hash = "sha256:6ab9f9c741d2e239b2e321ab0d389c04329d4ab7f11e3b92dd3aa7db1c59dee4", size = 226548083, upload-time = "2026-05-08T19:09:44.408Z" }, + { url = "https://files.pythonhosted.org/packages/89/96/360328e3c463f7ea08e853c4239c397e83363dd0204de71a710dc1a544bd/onnxruntime_gpu-1.26.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bcf6f347cad9f88a9a625c2b352cf9de927528aedb627ebbc089a201f1990b94", size = 276992052, upload-time = "2026-05-08T19:16:09.892Z" }, + { url = "https://files.pythonhosted.org/packages/fd/c8/aa2dc0e79bba577f37d5448bcb32fea79977e07506684d8138c19a0f1077/onnxruntime_gpu-1.26.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e6e4fb1ec9ae1cf456534d9115f106ab2a1ae96fa513b4ed0f4795302b4a2c6", size = 276978254, upload-time = "2026-05-08T19:16:22.096Z" }, + { url = "https://files.pythonhosted.org/packages/41/e7/923298431e669567d7ccc2a4c898b6534a47641a051569fd97165fe6d9b8/onnxruntime_gpu-1.26.0-cp314-cp314-win_amd64.whl", hash = "sha256:3e592439b0183d303c2374517b5b392599a3d50b2dc9de949b9b15731ac921c9", size = 229142768, upload-time = "2026-05-08T19:09:54.589Z" }, + { url = "https://files.pythonhosted.org/packages/97/91/93ffe5431d154989f5e04864a25a97eea480997d771232bcbbc538188241/onnxruntime_gpu-1.26.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:56dc7b73954ff4bdc71f5b8ab306b6f61be5d007881b6ef423a609e2b9cd088b", size = 276991545, upload-time = "2026-05-08T19:16:33.347Z" }, ] [[package]] name = "onnxruntime-migraphx" -version = "1.24.2" +version = "1.25.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "flatbuffers" }, { name = "numpy" }, { name = "packaging" }, { name = "protobuf" }, - { name = "sympy" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/dd/da/ca7ebc1a8d1193c97ceb9a05fad50f675eb955dc51beb7eb9ba89c8e7db0/onnxruntime_migraphx-1.24.2-cp311-cp311-manylinux_2_34_x86_64.whl", hash = "sha256:a2b434fb8880cac2b268950bdf279f33741d29c1f1c5461d27af835e8e288043", size = 20339710, upload-time = "2026-02-21T07:25:13.17Z" }, - { url = "https://files.pythonhosted.org/packages/fa/2e/8c83ec45a9365b4256495ca55eea30da7f03b02177b6da423c7da1ff5f6a/onnxruntime_migraphx-1.24.2-cp312-cp312-manylinux_2_34_x86_64.whl", hash = "sha256:ec814818da952bda3062e26f56c88bb713c00491ef91f86716c8d7346f9bc31b", size = 20341883, upload-time = "2026-02-21T07:25:17.86Z" }, - { url = "https://files.pythonhosted.org/packages/9f/52/4776ac68dbc46ca02c9a14cc9e5c496017f47a18cedf606cc38f4911b96a/onnxruntime_migraphx-1.24.2-cp313-cp313-manylinux_2_34_x86_64.whl", hash = "sha256:20e497538362170af639b03a40249d7ed61b873ac354f20d732b90252206e320", size = 20342422, upload-time = "2026-02-21T07:25:22.526Z" }, - { url = "https://files.pythonhosted.org/packages/76/44/db9035204a3363f9c0a4822c68e9a7520c13ef8d261f96b89b1375106dab/onnxruntime_migraphx-1.24.2-cp314-cp314-manylinux_2_34_x86_64.whl", hash = "sha256:9d7f1b1a2b9651143a2080b4f42ee99eead02023de1855d1b8a02199a9c179aa", size = 20343783, upload-time = "2026-02-21T07:25:29.155Z" }, + { url = "https://files.pythonhosted.org/packages/a5/3c/8c142017df701c38c4f5d6cae0294a2891bdd0f719eaff42e87564ac1480/onnxruntime_migraphx-1.25.0-cp311-cp311-manylinux_2_34_x86_64.whl", hash = "sha256:c7f28ddb8eef13fd5e9002d5162bfe848abfd02dcf13ab6a6b8c00380c014f33", size = 21467402, upload-time = "2026-04-21T05:23:52.026Z" }, + { url = "https://files.pythonhosted.org/packages/1c/0d/21730262547fae9557cb3fc85d891d600158c92399f68e4a760aed59bffc/onnxruntime_migraphx-1.25.0-cp312-cp312-manylinux_2_34_x86_64.whl", hash = "sha256:5c1dac156f98a9a20556bbc1e1c69d5ca1f78bad14d4953aa2b3cb2711507166", size = 21477899, upload-time = "2026-04-21T05:24:31.072Z" }, + { url = "https://files.pythonhosted.org/packages/09/b5/39764a6c87e9f97c0891f194100883192a0ce88725aba6215d499cd714b8/onnxruntime_migraphx-1.25.0-cp313-cp313-manylinux_2_34_x86_64.whl", hash = "sha256:d778d88bfdd8818817618ff3e4d973ed861428f61ba7c8b841690c699cb29ed4", size = 21477896, upload-time = "2026-04-21T05:25:11.969Z" }, + { url = "https://files.pythonhosted.org/packages/35/97/f8d8f3141c606e14a23e52a035c123524f5b714e15a1f9425a940906dfce/onnxruntime_migraphx-1.25.0-cp314-cp314-manylinux_2_34_x86_64.whl", hash = "sha256:5558a05f4cc1975c9974090337bbc460aae72b29ae20afb42bec93b74df0eae8", size = 21478521, upload-time = "2026-04-21T05:25:52.121Z" }, ] [[package]] @@ -1822,70 +1864,70 @@ wheels = [ [[package]] name = "orjson" -version = "3.11.8" +version = "3.11.9" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9d/1b/2024d06792d0779f9dbc51531b61c24f76c75b9f4ce05e6f3377a1814cea/orjson-3.11.8.tar.gz", hash = "sha256:96163d9cdc5a202703e9ad1b9ae757d5f0ca62f4fa0cc93d1f27b0e180cc404e", size = 5603832, upload-time = "2026-03-31T16:16:27.878Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/0c/964746fcafbd16f8ff53219ad9f6b412b34f345c75f384ad434ceaadb538/orjson-3.11.9.tar.gz", hash = "sha256:4fef17e1f8722c11587a6ef18e35902450221da0028e65dbaaa543619e68e48f", size = 5599163, upload-time = "2026-05-06T15:11:08.309Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/67/41/5aa7fa3b0f4dc6b47dcafc3cea909299c37e40e9972feabc8b6a74e2730d/orjson-3.11.8-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:003646067cc48b7fcab2ae0c562491c9b5d2cbd43f1e5f16d98fd118c5522d34", size = 229229, upload-time = "2026-03-31T16:14:50.424Z" }, - { url = "https://files.pythonhosted.org/packages/0a/d7/57e7f2458e0a2c41694f39fc830030a13053a84f837a5b73423dca1f0938/orjson-3.11.8-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:ed193ce51d77a3830cad399a529cd4ef029968761f43ddc549e1bc62b40d88f8", size = 128871, upload-time = "2026-03-31T16:14:51.888Z" }, - { url = "https://files.pythonhosted.org/packages/53/4a/e0fdb9430983e6c46e0299559275025075568aad5d21dd606faee3703924/orjson-3.11.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f30491bc4f862aa15744b9738517454f1e46e56c972a2be87d70d727d5b2a8f8", size = 132104, upload-time = "2026-03-31T16:14:53.142Z" }, - { url = "https://files.pythonhosted.org/packages/08/4a/2025a60ff3f5c8522060cda46612d9b1efa653de66ed2908591d8d82f22d/orjson-3.11.8-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6eda5b8b6be91d3f26efb7dc6e5e68ee805bc5617f65a328587b35255f138bf4", size = 130483, upload-time = "2026-03-31T16:14:54.605Z" }, - { url = "https://files.pythonhosted.org/packages/2d/3c/b9cde05bdc7b2385c66014e0620627da638d3d04e4954416ab48c31196c5/orjson-3.11.8-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee8db7bfb6fe03581bbab54d7c4124a6dd6a7f4273a38f7267197890f094675f", size = 135481, upload-time = "2026-03-31T16:14:55.901Z" }, - { url = "https://files.pythonhosted.org/packages/ff/f2/a8238e7734de7cb589fed319857a8025d509c89dc52fdcc88f39c6d03d5a/orjson-3.11.8-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5d8b5231de76c528a46b57010bbd83fb51e056aa0220a372fd5065e978406f1c", size = 146819, upload-time = "2026-03-31T16:14:57.548Z" }, - { url = "https://files.pythonhosted.org/packages/db/10/dbf1e2a3cafea673b1b4350e371877b759060d6018a998643b7040e5de48/orjson-3.11.8-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:58a4a208a6fbfdb7a7327b8f201c6014f189f721fd55d047cafc4157af1bc62a", size = 132846, upload-time = "2026-03-31T16:14:58.91Z" }, - { url = "https://files.pythonhosted.org/packages/f8/fc/55e667ec9c85694038fcff00573d221b085d50777368ee3d77f38668bf3c/orjson-3.11.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f8952d6d2505c003e8f0224ff7858d341fa4e33fef82b91c4ff0ef070f2393c", size = 133580, upload-time = "2026-03-31T16:15:00.519Z" }, - { url = "https://files.pythonhosted.org/packages/7e/a6/c08c589a9aad0cb46c4831d17de212a2b6901f9d976814321ff8e69e8785/orjson-3.11.8-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0022bb50f90da04b009ce32c512dc1885910daa7cb10b7b0cba4505b16db82a8", size = 142042, upload-time = "2026-03-31T16:15:01.906Z" }, - { url = "https://files.pythonhosted.org/packages/5c/cc/2f78ea241d52b717d2efc38878615fe80425bf2beb6e68c984dde257a766/orjson-3.11.8-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:ff51f9d657d1afb6f410cb435792ce4e1fe427aab23d2fcd727a2876e21d4cb6", size = 423845, upload-time = "2026-03-31T16:15:03.703Z" }, - { url = "https://files.pythonhosted.org/packages/70/07/c17dcf05dd8045457538428a983bf1f1127928df5bf328cb24d2b7cddacb/orjson-3.11.8-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6dbe9a97bdb4d8d9d5367b52a7c32549bba70b2739c58ef74a6964a6d05ae054", size = 147729, upload-time = "2026-03-31T16:15:05.203Z" }, - { url = "https://files.pythonhosted.org/packages/90/6c/0fb6e8a24e682e0958d71711ae6f39110e4b9cd8cab1357e2a89cb8e1951/orjson-3.11.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a5c370674ebabe16c6ccac33ff80c62bf8a6e59439f5e9d40c1f5ab8fd2215b7", size = 136425, upload-time = "2026-03-31T16:15:07.052Z" }, - { url = "https://files.pythonhosted.org/packages/b2/35/4d3cc3a3d616035beb51b24a09bb872942dc452cf2df0c1d11ab35046d9f/orjson-3.11.8-cp311-cp311-win32.whl", hash = "sha256:0e32f7154299f42ae66f13488963269e5eccb8d588a65bc839ed986919fc9fac", size = 131870, upload-time = "2026-03-31T16:15:08.678Z" }, - { url = "https://files.pythonhosted.org/packages/13/26/9fe70f81d16b702f8c3a775e8731b50ad91d22dacd14c7599b60a0941cd1/orjson-3.11.8-cp311-cp311-win_amd64.whl", hash = "sha256:25e0c672a2e32348d2eb33057b41e754091f2835f87222e4675b796b92264f06", size = 127440, upload-time = "2026-03-31T16:15:09.994Z" }, - { url = "https://files.pythonhosted.org/packages/e8/c6/b038339f4145efd2859c1ca53097a52c0bb9cbdd24f947ebe146da1ad067/orjson-3.11.8-cp311-cp311-win_arm64.whl", hash = "sha256:9185589c1f2a944c17e26c9925dcdbc2df061cc4a145395c57f0c51f9b5dbfcd", size = 127399, upload-time = "2026-03-31T16:15:11.412Z" }, - { url = "https://files.pythonhosted.org/packages/01/f6/8d58b32ab32d9215973a1688aebd098252ee8af1766c0e4e36e7831f0295/orjson-3.11.8-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:1cd0b77e77c95758f8e1100139844e99f3ccc87e71e6fc8e1c027e55807c549f", size = 229233, upload-time = "2026-03-31T16:15:12.762Z" }, - { url = "https://files.pythonhosted.org/packages/a9/8b/2ffe35e71f6b92622e8ea4607bf33ecf7dfb51b3619dcfabfd36cbe2d0a5/orjson-3.11.8-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:6a3d159d5ffa0e3961f353c4b036540996bf8b9697ccc38261c0eac1fd3347a6", size = 128772, upload-time = "2026-03-31T16:15:14.237Z" }, - { url = "https://files.pythonhosted.org/packages/27/d2/1f8682ae50d5c6897a563cb96bc106da8c9cb5b7b6e81a52e4cc086679b9/orjson-3.11.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76070a76e9c5ae661e2d9848f216980d8d533e0f8143e6ed462807b242e3c5e8", size = 131946, upload-time = "2026-03-31T16:15:15.607Z" }, - { url = "https://files.pythonhosted.org/packages/52/4b/5500f76f0eece84226e0689cb48dcde081104c2fa6e2483d17ca13685ffb/orjson-3.11.8-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:54153d21520a71a4c82a0dbb4523e468941d549d221dc173de0f019678cf3813", size = 130368, upload-time = "2026-03-31T16:15:17.066Z" }, - { url = "https://files.pythonhosted.org/packages/da/4e/58b927e08fbe9840e6c920d9e299b051ea667463b1f39a56e668669f8508/orjson-3.11.8-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:469ac2125611b7c5741a0b3798cd9e5786cbad6345f9f400c77212be89563bec", size = 135540, upload-time = "2026-03-31T16:15:18.404Z" }, - { url = "https://files.pythonhosted.org/packages/56/7c/ba7cb871cba1bcd5cd02ee34f98d894c6cea96353ad87466e5aef2429c60/orjson-3.11.8-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:14778ffd0f6896aa613951a7fbf4690229aa7a543cb2bfbe9f358e08aafa9546", size = 146877, upload-time = "2026-03-31T16:15:19.833Z" }, - { url = "https://files.pythonhosted.org/packages/0b/5d/eb9c25fc1386696c6a342cd361c306452c75e0b55e86ad602dd4827a7fd7/orjson-3.11.8-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea56a955056a6d6c550cf18b3348656a9d9a4f02e2d0c02cabf3c73f1055d506", size = 132837, upload-time = "2026-03-31T16:15:21.282Z" }, - { url = "https://files.pythonhosted.org/packages/37/87/5ddeb7fc1fbd9004aeccab08426f34c81a5b4c25c7061281862b015fce2b/orjson-3.11.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53a0f57e59a530d18a142f4d4ba6dfc708dc5fdedce45e98ff06b44930a2a48f", size = 133624, upload-time = "2026-03-31T16:15:22.641Z" }, - { url = "https://files.pythonhosted.org/packages/22/09/90048793db94ee4b2fcec4ac8e5ddb077367637d6650be896b3494b79bb7/orjson-3.11.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9b48e274f8824567d74e2158199e269597edf00823a1b12b63d48462bbf5123e", size = 141904, upload-time = "2026-03-31T16:15:24.435Z" }, - { url = "https://files.pythonhosted.org/packages/c0/cf/eb284847487821a5d415e54149a6449ba9bfc5872ce63ab7be41b8ec401c/orjson-3.11.8-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:3f262401086a3960586af06c054609365e98407151f5ea24a62893a40d80dbbb", size = 423742, upload-time = "2026-03-31T16:15:26.155Z" }, - { url = "https://files.pythonhosted.org/packages/44/09/e12423d327071c851c13e76936f144a96adacfc037394dec35ac3fc8d1e8/orjson-3.11.8-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:8e8c6218b614badf8e229b697865df4301afa74b791b6c9ade01d19a9953a942", size = 147806, upload-time = "2026-03-31T16:15:27.909Z" }, - { url = "https://files.pythonhosted.org/packages/b3/6d/37c2589ba864e582ffe7611643314785c6afb1f83c701654ef05daa8fcc7/orjson-3.11.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:093d489fa039ddade2db541097dbb484999fcc65fc2b0ff9819141e2ab364f25", size = 136485, upload-time = "2026-03-31T16:15:29.749Z" }, - { url = "https://files.pythonhosted.org/packages/be/c9/135194a02ab76b04ed9a10f68624b7ebd238bbe55548878b11ff15a0f352/orjson-3.11.8-cp312-cp312-win32.whl", hash = "sha256:e0950ed1bcb9893f4293fd5c5a7ee10934fbf82c4101c70be360db23ce24b7d2", size = 131966, upload-time = "2026-03-31T16:15:31.687Z" }, - { url = "https://files.pythonhosted.org/packages/ed/9a/9796f8fbe3cf30ce9cb696748dbb535e5c87be4bf4fe2e9ca498ef1fa8cf/orjson-3.11.8-cp312-cp312-win_amd64.whl", hash = "sha256:3cf17c141617b88ced4536b2135c552490f07799f6ad565948ea07bef0dcb9a6", size = 127441, upload-time = "2026-03-31T16:15:33.333Z" }, - { url = "https://files.pythonhosted.org/packages/cc/47/5aaf54524a7a4a0dd09dd778f3fa65dd2108290615b652e23d944152bc8e/orjson-3.11.8-cp312-cp312-win_arm64.whl", hash = "sha256:48854463b0572cc87dac7d981aa72ed8bf6deedc0511853dc76b8bbd5482d36d", size = 127364, upload-time = "2026-03-31T16:15:34.748Z" }, - { url = "https://files.pythonhosted.org/packages/66/7f/95fba509bb2305fab0073558f1e8c3a2ec4b2afe58ed9fcb7d3b8beafe94/orjson-3.11.8-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:3f23426851d98478c8970da5991f84784a76682213cd50eb73a1da56b95239dc", size = 229180, upload-time = "2026-03-31T16:15:36.426Z" }, - { url = "https://files.pythonhosted.org/packages/f6/9d/b237215c743ca073697d759b5503abd2cb8a0d7b9c9e21f524bcf176ab66/orjson-3.11.8-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:ebaed4cef74a045b83e23537b52ef19a367c7e3f536751e355a2a394f8648559", size = 128754, upload-time = "2026-03-31T16:15:38.049Z" }, - { url = "https://files.pythonhosted.org/packages/42/3d/27d65b6d11e63f133781425f132807aef793ed25075fec686fc8e46dd528/orjson-3.11.8-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97c8f5d3b62380b70c36ffacb2a356b7c6becec86099b177f73851ba095ef623", size = 131877, upload-time = "2026-03-31T16:15:39.484Z" }, - { url = "https://files.pythonhosted.org/packages/dd/cc/faee30cd8f00421999e40ef0eba7332e3a625ce91a58200a2f52c7fef235/orjson-3.11.8-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:436c4922968a619fb7fef1ccd4b8b3a76c13b67d607073914d675026e911a65c", size = 130361, upload-time = "2026-03-31T16:15:41.274Z" }, - { url = "https://files.pythonhosted.org/packages/5c/bb/a6c55896197f97b6d4b4e7c7fd77e7235517c34f5d6ad5aadd43c54c6d7c/orjson-3.11.8-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1ab359aff0436d80bfe8a23b46b5fea69f1e18aaf1760a709b4787f1318b317f", size = 135521, upload-time = "2026-03-31T16:15:42.758Z" }, - { url = "https://files.pythonhosted.org/packages/9c/7c/ca3a3525aa32ff636ebb1778e77e3587b016ab2edb1b618b36ba96f8f2c0/orjson-3.11.8-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f89b6d0b3a8d81e1929d3ab3d92bbc225688bd80a770c49432543928fe09ac55", size = 146862, upload-time = "2026-03-31T16:15:44.341Z" }, - { url = "https://files.pythonhosted.org/packages/3c/0c/18a9d7f18b5edd37344d1fd5be17e94dc652c67826ab749c6e5948a78112/orjson-3.11.8-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:29c009e7a2ca9ad0ed1376ce20dd692146a5d9fe4310848904b6b4fee5c5c137", size = 132847, upload-time = "2026-03-31T16:15:46.368Z" }, - { url = "https://files.pythonhosted.org/packages/23/91/7e722f352ad67ca573cee44de2a58fb810d0f4eb4e33276c6a557979fd8a/orjson-3.11.8-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:705b895b781b3e395c067129d8551655642dfe9437273211d5404e87ac752b53", size = 133637, upload-time = "2026-03-31T16:15:48.123Z" }, - { url = "https://files.pythonhosted.org/packages/af/04/32845ce13ac5bd1046ddb02ac9432ba856cc35f6d74dde95864fe0ad5523/orjson-3.11.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:88006eda83858a9fdf73985ce3804e885c2befb2f506c9a3723cdeb5a2880e3e", size = 141906, upload-time = "2026-03-31T16:15:49.626Z" }, - { url = "https://files.pythonhosted.org/packages/02/5e/c551387ddf2d7106d9039369862245c85738b828844d13b99ccb8d61fd06/orjson-3.11.8-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:55120759e61309af7fcf9e961c6f6af3dde5921cdb3ee863ef63fd9db126cae6", size = 423722, upload-time = "2026-03-31T16:15:51.176Z" }, - { url = "https://files.pythonhosted.org/packages/00/a3/ecfe62434096f8a794d4976728cb59bcfc4a643977f21c2040545d37eb4c/orjson-3.11.8-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:98bdc6cb889d19bed01de46e67574a2eab61f5cc6b768ed50e8ac68e9d6ffab6", size = 147801, upload-time = "2026-03-31T16:15:52.939Z" }, - { url = "https://files.pythonhosted.org/packages/18/6d/0dce10b9f6643fdc59d99333871a38fa5a769d8e2fc34a18e5d2bfdee900/orjson-3.11.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:708c95f925a43ab9f34625e45dcdadf09ec8a6e7b664a938f2f8d5650f6c090b", size = 136460, upload-time = "2026-03-31T16:15:54.431Z" }, - { url = "https://files.pythonhosted.org/packages/01/d6/6dde4f31842d87099238f1f07b459d24edc1a774d20687187443ab044191/orjson-3.11.8-cp313-cp313-win32.whl", hash = "sha256:01c4e5a6695dc09098f2e6468a251bc4671c50922d4d745aff1a0a33a0cf5b8d", size = 131956, upload-time = "2026-03-31T16:15:56.081Z" }, - { url = "https://files.pythonhosted.org/packages/c1/f9/4e494a56e013db957fb77186b818b916d4695b8fa2aa612364974160e91b/orjson-3.11.8-cp313-cp313-win_amd64.whl", hash = "sha256:c154a35dd1330707450bb4d4e7dd1f17fa6f42267a40c1e8a1daa5e13719b4b8", size = 127410, upload-time = "2026-03-31T16:15:57.54Z" }, - { url = "https://files.pythonhosted.org/packages/57/7f/803203d00d6edb6e9e7eef421d4e1adbb5ea973e40b3533f3cfd9aeb374e/orjson-3.11.8-cp313-cp313-win_arm64.whl", hash = "sha256:4861bde57f4d253ab041e374f44023460e60e71efaa121f3c5f0ed457c3a701e", size = 127338, upload-time = "2026-03-31T16:15:59.106Z" }, - { url = "https://files.pythonhosted.org/packages/6d/35/b01910c3d6b85dc882442afe5060cbf719c7d1fc85749294beda23d17873/orjson-3.11.8-cp314-cp314-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:ec795530a73c269a55130498842aaa762e4a939f6ce481a7e986eeaa790e9da4", size = 229171, upload-time = "2026-03-31T16:16:00.651Z" }, - { url = "https://files.pythonhosted.org/packages/c2/56/c9ec97bd11240abef39b9e5d99a15462809c45f677420fd148a6c5e6295e/orjson-3.11.8-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:c492a0e011c0f9066e9ceaa896fbc5b068c54d365fea5f3444b697ee01bc8625", size = 128746, upload-time = "2026-03-31T16:16:02.673Z" }, - { url = "https://files.pythonhosted.org/packages/3b/e4/66d4f30a90de45e2f0cbd9623588e8ae71eef7679dbe2ae954ed6d66a41f/orjson-3.11.8-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:883206d55b1bd5f5679ad5e6ddd3d1a5e3cac5190482927fdb8c78fb699193b5", size = 131867, upload-time = "2026-03-31T16:16:04.342Z" }, - { url = "https://files.pythonhosted.org/packages/19/30/2a645fc9286b928675e43fa2a3a16fb7b6764aa78cc719dc82141e00f30b/orjson-3.11.8-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5774c1fdcc98b2259800b683b19599c133baeb11d60033e2095fd9d4667b82db", size = 124664, upload-time = "2026-03-31T16:16:05.837Z" }, - { url = "https://files.pythonhosted.org/packages/db/44/77b9a86d84a28d52ba3316d77737f6514e17118119ade3f91b639e859029/orjson-3.11.8-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ac7381c83dd3d4a6347e6635950aa448f54e7b8406a27c7ecb4a37e9f1ae08b", size = 129701, upload-time = "2026-03-31T16:16:07.407Z" }, - { url = "https://files.pythonhosted.org/packages/b3/ea/eff3d9bfe47e9bc6969c9181c58d9f71237f923f9c86a2d2f490cd898c82/orjson-3.11.8-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:14439063aebcb92401c11afc68ee4e407258d2752e62d748b6942dad20d2a70d", size = 141202, upload-time = "2026-03-31T16:16:09.48Z" }, - { url = "https://files.pythonhosted.org/packages/52/c8/90d4b4c60c84d62068d0cf9e4d8f0a4e05e76971d133ac0c60d818d4db20/orjson-3.11.8-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fa72e71977bff96567b0f500fc5bfd2fdf915f34052c782a4c6ebbdaa97aa858", size = 127194, upload-time = "2026-03-31T16:16:11.02Z" }, - { url = "https://files.pythonhosted.org/packages/8d/c7/ea9e08d1f0ba981adffb629811148b44774d935171e7b3d780ae43c4c254/orjson-3.11.8-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7679bc2f01bb0d219758f1a5f87bb7c8a81c0a186824a393b366876b4948e14f", size = 133639, upload-time = "2026-03-31T16:16:13.434Z" }, - { url = "https://files.pythonhosted.org/packages/6c/8c/ddbbfd6ba59453c8fc7fe1d0e5983895864e264c37481b2a791db635f046/orjson-3.11.8-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:14f7b8fcb35ef403b42fa5ecfa4ed032332a91f3dc7368fbce4184d59e1eae0d", size = 141914, upload-time = "2026-03-31T16:16:14.955Z" }, - { url = "https://files.pythonhosted.org/packages/4e/31/dbfbefec9df060d34ef4962cd0afcb6fa7a9ec65884cb78f04a7859526c3/orjson-3.11.8-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:c2bdf7b2facc80b5e34f48a2d557727d5c5c57a8a450de122ae81fa26a81c1bc", size = 423800, upload-time = "2026-03-31T16:16:16.594Z" }, - { url = "https://files.pythonhosted.org/packages/87/cf/f74e9ae9803d4ab46b163494adba636c6d7ea955af5cc23b8aaa94cfd528/orjson-3.11.8-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ccd7ba1b0605813a0715171d39ec4c314cb97a9c85893c2c5c0c3a3729df38bf", size = 147837, upload-time = "2026-03-31T16:16:18.585Z" }, - { url = "https://files.pythonhosted.org/packages/64/e6/9214f017b5db85e84e68602792f742e5dc5249e963503d1b356bee611e01/orjson-3.11.8-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:cdbc8c9c02463fef4d3c53a9ba3336d05496ec8e1f1c53326a1e4acc11f5c600", size = 136441, upload-time = "2026-03-31T16:16:20.151Z" }, - { url = "https://files.pythonhosted.org/packages/24/dd/3590348818f58f837a75fb969b04cdf187ae197e14d60b5e5a794a38b79d/orjson-3.11.8-cp314-cp314-win32.whl", hash = "sha256:0b57f67710a8cd459e4e54eb96d5f77f3624eba0c661ba19a525807e42eccade", size = 131983, upload-time = "2026-03-31T16:16:21.823Z" }, - { url = "https://files.pythonhosted.org/packages/3f/0f/b6cb692116e05d058f31ceee819c70f097fa9167c82f67fabe7516289abc/orjson-3.11.8-cp314-cp314-win_amd64.whl", hash = "sha256:735e2262363dcbe05c35e3a8869898022af78f89dde9e256924dc02e99fe69ca", size = 127396, upload-time = "2026-03-31T16:16:23.685Z" }, - { url = "https://files.pythonhosted.org/packages/c0/d1/facb5b5051fabb0ef9d26c6544d87ef19a939a9a001198655d0d891062dd/orjson-3.11.8-cp314-cp314-win_arm64.whl", hash = "sha256:6ccdea2c213cf9f3d9490cbd5d427693c870753df41e6cb375bd79bcbafc8817", size = 127330, upload-time = "2026-03-31T16:16:25.496Z" }, + { url = "https://files.pythonhosted.org/packages/1e/51/3fb9e65ae76ee97bd611869a503fa3fc0a6e81dd8b737cf3003f682df7ff/orjson-3.11.9-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:f01c4818b3fc9b0da8e096722a84318071eaa118df35f6ed2344da0e73a5444f", size = 228522, upload-time = "2026-05-06T15:09:35.362Z" }, + { url = "https://files.pythonhosted.org/packages/16/fa/9d54b07cb3f3b0bfd57841478e42d7a0ece4a9f49f9907eecf5a45461687/orjson-3.11.9-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:3ebca4179031ee716ed076ffadc29428e900512f6fccee8614c9983157fcf19c", size = 128463, upload-time = "2026-05-06T15:09:37.063Z" }, + { url = "https://files.pythonhosted.org/packages/88/b1/6ceafc2eefd0a553e3be77ce6c49d107e772485d9568629376171c50e634/orjson-3.11.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48ee05097750de0ff69ed5b7bbcf0732182fd57a24043dcc2a1da780a5ead3a5", size = 132306, upload-time = "2026-05-06T15:09:38.299Z" }, + { url = "https://files.pythonhosted.org/packages/ea/76/f11311285324a40aab1e3031385c50b635a7cd0734fdaf60c7e89a696f60/orjson-3.11.9-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a6082706765a95a6680d812e1daf1c0cfe8adec7831b3ff3b625693f3b461b1c", size = 127988, upload-time = "2026-05-06T15:09:39.597Z" }, + { url = "https://files.pythonhosted.org/packages/9e/85/0ef63bcf1337f44031ce9b91b1919563f62a37527b3ea4368bb15a22e5d7/orjson-3.11.9-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:277fefe9d76ee17eb14debf399e3533d4d63b5f677a4d3719eb763536af1f4bd", size = 135188, upload-time = "2026-05-06T15:09:40.957Z" }, + { url = "https://files.pythonhosted.org/packages/05/94/b0d27090ea8a2095db3c2bd1b1c96f96f19bbb494d7fef33130e846e613d/orjson-3.11.9-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:03db380e3780fa0015ed776a90f20e8e20bb11dde13b216ce19e5718e3dfba62", size = 145937, upload-time = "2026-05-06T15:09:42.249Z" }, + { url = "https://files.pythonhosted.org/packages/09/eb/75d50c29c05b8054013e221e598820a365c8e64065312e75e202ed880709/orjson-3.11.9-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33d7d766701847dc6729846362dc27895d2f2d2251264f9d10e7cb9878194877", size = 132758, upload-time = "2026-05-06T15:09:43.945Z" }, + { url = "https://files.pythonhosted.org/packages/49/bd/360686f39348aa88827cb6fbf7dc606fd41c831a35235e1abf1db8e3a9e6/orjson-3.11.9-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:147302878da387104b66bb4a8b0227d1d487e976ce41a8501916161072ed87b1", size = 133971, upload-time = "2026-05-06T15:09:45.239Z" }, + { url = "https://files.pythonhosted.org/packages/0e/30/3178eb16f3221aeef068b6f1f1ebe05f656ea5c6dffe9f6c917329fe17a3/orjson-3.11.9-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3513550321f8c8c811a7c3297b8a630e82dc08e4c10216d07703c997776236cd", size = 141685, upload-time = "2026-05-06T15:09:46.858Z" }, + { url = "https://files.pythonhosted.org/packages/5f/f1/ff2f19ed0225f9680fafa42febca3570dd59444ebf190980738d376214c2/orjson-3.11.9-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:c5d001196b89fa9cf0a4ab79766cd835b991a166e4b621ba95089edc50c429ff", size = 415167, upload-time = "2026-05-06T15:09:48.312Z" }, + { url = "https://files.pythonhosted.org/packages/9b/61/863bddf0da6e9e586765414debd54b4e58db05f560902b6d00658cb88636/orjson-3.11.9-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:16969c9d369c98eb084889c6e4d2d39b77c7eb38ceccf8da2a9fff62ae908980", size = 147913, upload-time = "2026-05-06T15:09:49.733Z" }, + { url = "https://files.pythonhosted.org/packages/b6/8a/4081492586d75b073d60c5271a8d0f05a0955cabf1e34c8473f6fcd84235/orjson-3.11.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:63e0efbc991250c0b3143488fa57d95affcabbfc63c99c48d625dd37779aafe2", size = 136959, upload-time = "2026-05-06T15:09:51.311Z" }, + { url = "https://files.pythonhosted.org/packages/0d/bd/70b6ab193594d7abb875320c0a7c8335e846f28968c432c31042409c3c8d/orjson-3.11.9-cp311-cp311-win32.whl", hash = "sha256:14ed654580c1ed2bc217352ec82f91b047aef82951aa71c7f64e0dcb03c0e180", size = 131533, upload-time = "2026-05-06T15:09:52.637Z" }, + { url = "https://files.pythonhosted.org/packages/3f/17/1a1a228183d62d1b77e2c30d210f47dd4768b310ebe1607c63e3c0e3a71e/orjson-3.11.9-cp311-cp311-win_amd64.whl", hash = "sha256:57ea77fb70a448ce87d18fca050193202a3da5e54598f6501ca5476fb66cfe02", size = 127106, upload-time = "2026-05-06T15:09:54.204Z" }, + { url = "https://files.pythonhosted.org/packages/b8/95/285de5fa296d09681ee9c546cd4a8aeb773b701cf343dc125994f4d52953/orjson-3.11.9-cp311-cp311-win_arm64.whl", hash = "sha256:19b72ed11572a2ee51a67a903afbe5af504f84ed6f529c0fe44b0ab3fb5cc697", size = 126848, upload-time = "2026-05-06T15:09:55.551Z" }, + { url = "https://files.pythonhosted.org/packages/16/6d/11867a3ffa3a3608d84a4de51ef4dd0896d6b5cc9132fbe1daf593e677bc/orjson-3.11.9-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9ef6fe90aadef185c7b128859f40beb24720b4ecea95379fc9000931179c3a49", size = 228515, upload-time = "2026-05-06T15:09:57.265Z" }, + { url = "https://files.pythonhosted.org/packages/24/75/05912954c8b288f34fcf5cd4b9b071cb4f6e77b9961e175e56ebb258089f/orjson-3.11.9-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:e5c9b8f28e726e97d97696c826bc7bea5d71cecd63576dba92924a32c1961291", size = 128409, upload-time = "2026-05-06T15:09:59.063Z" }, + { url = "https://files.pythonhosted.org/packages/ab/86/1c3a47df3bc8191ea9ac51603bbb872a95167a364320c269f2557911f406/orjson-3.11.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26a473dbb4162108b27901492546f83c76fdcea3d0eadff00ae7a07e18dcce09", size = 132106, upload-time = "2026-05-06T15:10:00.798Z" }, + { url = "https://files.pythonhosted.org/packages/d7/cf/b33b5f3e695ae7d63feef9d915c37cc3b8f465493dcd4f8e0b4c697a2366/orjson-3.11.9-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:011382e2a60fda9d46f1cdee31068cfc52ffe952b587d683ec0463002802a0f4", size = 127864, upload-time = "2026-05-06T15:10:02.15Z" }, + { url = "https://files.pythonhosted.org/packages/31/6a/6cf69385a58208024fcb8c014e2141b8ce838aba6492b589f8acfff97fab/orjson-3.11.9-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c2d3dc759490128c5c1711a53eeaa8ee1d437fd0038ffd2b6008abf46db3f882", size = 135213, upload-time = "2026-05-06T15:10:03.515Z" }, + { url = "https://files.pythonhosted.org/packages/e8/f8/0b1bd3e8f2efcdd376af5c8cfd79eaf13f018080c0089c80ebd724e3c7fb/orjson-3.11.9-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d8ea516b3726d190e1b4297e6f4e7a8650347ae053868a18163b4dd3641d1fff", size = 145994, upload-time = "2026-05-06T15:10:05.083Z" }, + { url = "https://files.pythonhosted.org/packages/f3/59/dab79f61044c529d2c81aecdc589b1f833a1c8dec11ba3b1c2498a02ca7e/orjson-3.11.9-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:380cdce7ba24989af81d0a7013d0aaec5d0e2a21734c0e2681b1bc4f141957fe", size = 132744, upload-time = "2026-05-06T15:10:06.853Z" }, + { url = "https://files.pythonhosted.org/packages/0e/a4/82b7a2fe5d8a67a59ed831b24d59a3d46ea7d207b66e1602d376541d94a6/orjson-3.11.9-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be4fa4f0af7fa18951f7ab3fc2148e223af211bf03f59e1c6034ec3f97f21d61", size = 134014, upload-time = "2026-05-06T15:10:08.213Z" }, + { url = "https://files.pythonhosted.org/packages/50/c7/375e83a76851b73b2e39f3bcf0e5a19e2b89bad13e5bca97d0b293d27f24/orjson-3.11.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a8f5f8bc7ce7d59f08d9f99fa510c06496164a24cb5f3d34537dbd9ca30132e2", size = 141509, upload-time = "2026-05-06T15:10:09.595Z" }, + { url = "https://files.pythonhosted.org/packages/7f/7c/49d5d82a3d3097f641f094f552131f1e2723b0b8cb0fa2874ab65ecfffa6/orjson-3.11.9-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:4d7fde5501b944f83b3e665e1b31343ff6e154b15560a16b7130ea1e594a4206", size = 415127, upload-time = "2026-05-06T15:10:11.049Z" }, + { url = "https://files.pythonhosted.org/packages/3a/dc/7446c538590d55f455647e5f3c61fc33f7108714e7afcffa6a2a033f8350/orjson-3.11.9-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:cde1a448023ba7d5bb4c01c5afb48894380b5e4956e0627266526587ef4e535f", size = 148025, upload-time = "2026-05-06T15:10:12.842Z" }, + { url = "https://files.pythonhosted.org/packages/df/e5/4d2d8af06f788329b4f78f8cc3679bb395392fcaa1e4d8d3c33e85308fa4/orjson-3.11.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:71e63adb0e1f1ed5d9e168f50a91ceb93ae6420731d222dc7da5c69409aa47aa", size = 136943, upload-time = "2026-05-06T15:10:14.405Z" }, + { url = "https://files.pythonhosted.org/packages/06/69/850264ccf6d80f6b174620d30a87f65c9b1490aba33fe6b62798e618cad3/orjson-3.11.9-cp312-cp312-win32.whl", hash = "sha256:2d057a602cdd19a0ad680417527c45b6961a095081c0f46fe0e03e304aac6470", size = 131606, upload-time = "2026-05-06T15:10:15.791Z" }, + { url = "https://files.pythonhosted.org/packages/b9/d5/973a43fc9c55e20f2051e9830997649f669be0cb3ca52192087c0143f118/orjson-3.11.9-cp312-cp312-win_amd64.whl", hash = "sha256:59e403b1cc5a676da8eaf31f6254801b7341b3e29efa85f92b48d272637e77be", size = 127101, upload-time = "2026-05-06T15:10:17.129Z" }, + { url = "https://files.pythonhosted.org/packages/fe/ae/495470f0e4a18f73fa10b7f6b84b464ec4cc5291c4e0c7c2a6c400bef006/orjson-3.11.9-cp312-cp312-win_arm64.whl", hash = "sha256:9af678d6488357948f1f84c6cd1c1d397c014e1ae2f98ae082a44eb48f602624", size = 126736, upload-time = "2026-05-06T15:10:18.645Z" }, + { url = "https://files.pythonhosted.org/packages/32/33/93fcc25907235c344ae73122f8a4e01d2d393ef062b4af7d2e2487a32c37/orjson-3.11.9-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:4bab1b2d6141fe7b32ae71dac905666ece4f94936efbfb13d55bb7739a3a6021", size = 228458, upload-time = "2026-05-06T15:10:20.079Z" }, + { url = "https://files.pythonhosted.org/packages/8f/27/b1e6dadb3c080313c03fdd8067b85e6a0460c7d8d6a1c3984ef77b904e4d/orjson-3.11.9-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:844417969855fc7a41be124aafe83dc424592a7f77cd4501900c67307122b92c", size = 128368, upload-time = "2026-05-06T15:10:21.549Z" }, + { url = "https://files.pythonhosted.org/packages/21/0f/c9ede0bf052f6b4051e64a7d4fa91b725cccf8321a6a786e86eb03519f00/orjson-3.11.9-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffe02797b5e9f3a9d8292ddcd289b474ad13e81ad83cd1891a240811f1d2cb81", size = 132070, upload-time = "2026-05-06T15:10:23.371Z" }, + { url = "https://files.pythonhosted.org/packages/fd/26/d398e28048dc18205bbe812f2c88cb9b40313db2470778e25964796458fe/orjson-3.11.9-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0e4eed3b200023042814d2fc8a5d2e880f13b52e1ed2485e83da4f3962f7dc1a", size = 127892, upload-time = "2026-05-06T15:10:24.714Z" }, + { url = "https://files.pythonhosted.org/packages/66/60/52b0054c4c700d5aa7fc5b7ca96917400d8f061307778578e67a10e25852/orjson-3.11.9-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8aff7da9952a5ad1cef8e68017724d96c7b9a66e99e91d6252e1b133d67a7b10", size = 135217, upload-time = "2026-05-06T15:10:26.084Z" }, + { url = "https://files.pythonhosted.org/packages/d5/97/1e3dc2b2a28b7b2528f403d2fc1d79ec5f39af3bc143ab65d3ec26426385/orjson-3.11.9-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4d4e98d6f3b8afed8bc8cd9718ec0cdf46661826beefb53fe8eafb37f2bf0362", size = 145980, upload-time = "2026-05-06T15:10:28.062Z" }, + { url = "https://files.pythonhosted.org/packages/fc/39/31fbfe7850f2de32dee7e7e5c09f26d403ab01e440ac96001c6b01ad3c99/orjson-3.11.9-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3a81d52442a7c99b3662333235b3adf96a1715864658b35bb797212be7bddb97", size = 132738, upload-time = "2026-05-06T15:10:29.727Z" }, + { url = "https://files.pythonhosted.org/packages/a1/08/dca0082dd2a194acb93e5457e73455388e2e2ca464a2672449a9ddbb679d/orjson-3.11.9-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e39364e726a8fff737309aff059ff67d8a8c8d5b677be7bb49a8b3e84b7e218", size = 134033, upload-time = "2026-05-06T15:10:31.152Z" }, + { url = "https://files.pythonhosted.org/packages/11/d4/5bdb0626801230139987385554c5d4c42255218ac906525bf4347f22cd95/orjson-3.11.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4fd66214623f1b17501df9f0543bef0b833979ab5b6ded1e1d123222866aa8c9", size = 141492, upload-time = "2026-05-06T15:10:32.641Z" }, + { url = "https://files.pythonhosted.org/packages/fa/88/a21fb53b3ede6703aede6dce4710ed4111e5b201cfa6bbff5e544f9d47d7/orjson-3.11.9-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:8ecc30f10465fa1e0ce13fd01d9e22c316e5053a719a8d915d4545a09a5ff677", size = 415087, upload-time = "2026-05-06T15:10:34.438Z" }, + { url = "https://files.pythonhosted.org/packages/3d/57/1b30daf70f0d8180e9a73cefbfbdd99e4bf19eb020466502b01fba7e0e50/orjson-3.11.9-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:97db4c94a7db398a5bd636273324f0b3fd58b350bbbac8bb380ceb825a9b40f4", size = 148031, upload-time = "2026-05-06T15:10:36.358Z" }, + { url = "https://files.pythonhosted.org/packages/04/83/45fbb6d962e260807f99441db9613cee868ceda4baceda59b3720a563f97/orjson-3.11.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9f78cf8fec5bd627f4082b8dfeac7871b43d7f3274904492a43dab39f18a19a0", size = 136915, upload-time = "2026-05-06T15:10:38.013Z" }, + { url = "https://files.pythonhosted.org/packages/5f/cc/2d10025f9056d376e4127ec05a5808b218d46f035fdc08178a5411b34250/orjson-3.11.9-cp313-cp313-win32.whl", hash = "sha256:d4087e5c0209a0a8efe4de3303c234b9c44d1174161dcd851e8eea07c7560b32", size = 131613, upload-time = "2026-05-06T15:10:39.569Z" }, + { url = "https://files.pythonhosted.org/packages/67/bd/2775ff28bfe883b9aa1ff348300542eb2ef1ee18d8ae0e3a49846817a865/orjson-3.11.9-cp313-cp313-win_amd64.whl", hash = "sha256:051b102c93b4f634e89f3866b07b9a9a98915ada541f4ec30f177067b2694979", size = 127086, upload-time = "2026-05-06T15:10:41.262Z" }, + { url = "https://files.pythonhosted.org/packages/91/2b/d26799e580939e32a7da9a39531bc9e58e15ca32ffaa6a8cb3e9bb0d22cd/orjson-3.11.9-cp313-cp313-win_arm64.whl", hash = "sha256:cce9127885941bd28f080cecf1f1d288336b7e0d812c345b08be88b572796254", size = 126696, upload-time = "2026-05-06T15:10:42.651Z" }, + { url = "https://files.pythonhosted.org/packages/8e/eb/5da01e356015aee6ecfa1187ced87aef51364e306f5e695dd52719bf0e78/orjson-3.11.9-cp314-cp314-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:b6ef1979adc4bc243523f1a2ba91418030a8e29b0a99cbe7e0e2d6807d4dce6e", size = 228465, upload-time = "2026-05-06T15:10:44.097Z" }, + { url = "https://files.pythonhosted.org/packages/64/62/3e0e0c14c957133bcd855395c62b55ed4e3b0af23ffea11b032cb1dcbdb1/orjson-3.11.9-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:f36b7f32c7c0db4a719f1fc5824db4a9c6f8bd1a354debb91faf26ebf3a4c71e", size = 128364, upload-time = "2026-05-06T15:10:45.839Z" }, + { url = "https://files.pythonhosted.org/packages/5a/5a/07d8aa117211a8ed7630bda80c8c0b14d04e0f8dcf99bcf49656e4a710eb/orjson-3.11.9-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08f4d8ebb44925c794e535b2bebc507cebf32209df81de22ae285fb0d8d66de0", size = 132063, upload-time = "2026-05-06T15:10:47.267Z" }, + { url = "https://files.pythonhosted.org/packages/d6/ec/4acaf21483e18aa945be74a474c74b434f284b549f275a0a39b9f98956e9/orjson-3.11.9-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6cc7923789694fd58f001cbcac7e47abc13af4d560ebbfcf3b41a8b1a0748124", size = 122356, upload-time = "2026-05-06T15:10:48.765Z" }, + { url = "https://files.pythonhosted.org/packages/13/d8/5f0555e7638801323b7a75850f92e7dfa891bc84fe27a1ba4449170d1200/orjson-3.11.9-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea5c46eb2d3af39e806b986f4b09d5c2706a1f5afde3cbf7544ce6616127173c", size = 129592, upload-time = "2026-05-06T15:10:50.13Z" }, + { url = "https://files.pythonhosted.org/packages/b6/30/ed9860412a3603ceb3c5955bfd72d28b9d0e7ba6ed81add14f83d7114236/orjson-3.11.9-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f5d89a2ed90731df3be64bab0aa44f78bff39fdc9d71c291f4a8023aa46425b7", size = 140491, upload-time = "2026-05-06T15:10:51.582Z" }, + { url = "https://files.pythonhosted.org/packages/d0/17/adc514dea7ac7c505527febf884934b815d34f0c7b8693c1a8b39c5c4a57/orjson-3.11.9-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:25e4aed0312d292c09f61af25bba34e0b2c88546041472b09088c39a4d828af1", size = 127309, upload-time = "2026-05-06T15:10:53.329Z" }, + { url = "https://files.pythonhosted.org/packages/76/3e/c0b690253f0b82d86e99949af13533363acfb5432ecb5d53dd5b3bce9c34/orjson-3.11.9-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aaea64f3f467d22e70eeed68bdccb3bc4f83f650446c4a03c59f2cba28a108db", size = 134030, upload-time = "2026-05-06T15:10:54.988Z" }, + { url = "https://files.pythonhosted.org/packages/c1/7a/bc82a0bb25e9faaf92dc4d9ef002732efc09737706af83e346788641d4a7/orjson-3.11.9-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a028425d1b440c5d92a6be1e1a020739dfe67ea87d96c6dbe828c1b30041728b", size = 141482, upload-time = "2026-05-06T15:10:56.663Z" }, + { url = "https://files.pythonhosted.org/packages/01/55/e69188b939f77d5d32a9833745ace31ea5ccae3ab613a1ec185d3cd2c4fb/orjson-3.11.9-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:5b192c6cf397e4455b11523c5cf2b18ed084c1bbd61b6c0926344d2129481972", size = 415178, upload-time = "2026-05-06T15:10:58.446Z" }, + { url = "https://files.pythonhosted.org/packages/2e/1a/b8a5a7ac527e80b9cb11d51e3f6689b709279183264b9ec5c7bc680bb8b5/orjson-3.11.9-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ea407d4ccf5891d667d045fecae97a7a1e5e87b3b97f97ae1803c2e741130be0", size = 148089, upload-time = "2026-05-06T15:11:00.441Z" }, + { url = "https://files.pythonhosted.org/packages/97/4e/00503f64204bf859b37213a63927028f30fb6268cd8677fb0a5ad48155e1/orjson-3.11.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5f63aaf97afd9f6dec5b1a68e1b8da12bfccb4cb9a9a65c3e0b6c847849e7586", size = 136921, upload-time = "2026-05-06T15:11:02.176Z" }, + { url = "https://files.pythonhosted.org/packages/0d/ba/a23b82a0a8d0ed7bed4e5f5035aae751cad4ff6a1e8d2ecd14d8860f5929/orjson-3.11.9-cp314-cp314-win32.whl", hash = "sha256:e30ab17845bb9fa54ccf67fa4f9f5282652d54faa6d17452f47d0f369d038673", size = 131638, upload-time = "2026-05-06T15:11:03.696Z" }, + { url = "https://files.pythonhosted.org/packages/f3/c3/0c6798456bade745c75c452342dabacce5798196483e77e643be1f53877d/orjson-3.11.9-cp314-cp314-win_amd64.whl", hash = "sha256:32ef5f4283a3be81913947d19608eacb7c6608026851123790cd9cc8982af34b", size = 127078, upload-time = "2026-05-06T15:11:05.123Z" }, + { url = "https://files.pythonhosted.org/packages/16/21/5a3f1e8913103b703a436a5664238e5b965ec392b555fe68943ea3691e6b/orjson-3.11.9-cp314-cp314-win_arm64.whl", hash = "sha256:eebdbdeef0094e4f5aefa20dcd4eb2368ab5e7a3b4edea27f1e7b2892e009cf9", size = 126687, upload-time = "2026-05-06T15:11:06.602Z" }, ] [[package]] @@ -2080,7 +2122,7 @@ wheels = [ [[package]] name = "pydantic" -version = "2.12.5" +version = "2.13.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-types" }, @@ -2088,120 +2130,125 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591, upload-time = "2025-11-26T15:11:46.471Z" } +sdist = { url = "https://files.pythonhosted.org/packages/18/a5/b60d21ac674192f8ab0ba4e9fd860690f9b4a6e51ca5df118733b487d8d6/pydantic-2.13.4.tar.gz", hash = "sha256:c40756b57adaa8b1efeeced5c196f3f3b7c435f90e84ea7f443901bec8099ef6", size = 844775, upload-time = "2026-05-06T13:43:05.343Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580, upload-time = "2025-11-26T15:11:44.605Z" }, + { url = "https://files.pythonhosted.org/packages/fd/7b/122376b1fd3c62c1ed9dc80c931ace4844b3c55407b6fb2d199377c9736f/pydantic-2.13.4-py3-none-any.whl", hash = "sha256:45a282cde31d808236fd7ea9d919b128653c8b38b393d1c4ab335c62924d9aba", size = 472262, upload-time = "2026-05-06T13:43:02.641Z" }, ] [[package]] name = "pydantic-core" -version = "2.41.5" +version = "2.46.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9d/56/921726b776ace8d8f5db44c4ef961006580d91dc52b803c489fafd1aa249/pydantic_core-2.46.4.tar.gz", hash = "sha256:62f875393d7f270851f20523dd2e29f082bcc82292d66db2b64ea71f64b6e1c1", size = 471464, upload-time = "2026-05-06T13:37:06.98Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e8/72/74a989dd9f2084b3d9530b0915fdda64ac48831c30dbf7c72a41a5232db8/pydantic_core-2.41.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6", size = 2105873, upload-time = "2025-11-04T13:39:31.373Z" }, - { url = "https://files.pythonhosted.org/packages/12/44/37e403fd9455708b3b942949e1d7febc02167662bf1a7da5b78ee1ea2842/pydantic_core-2.41.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b", size = 1899826, upload-time = "2025-11-04T13:39:32.897Z" }, - { url = "https://files.pythonhosted.org/packages/33/7f/1d5cab3ccf44c1935a359d51a8a2a9e1a654b744b5e7f80d41b88d501eec/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a", size = 1917869, upload-time = "2025-11-04T13:39:34.469Z" }, - { url = "https://files.pythonhosted.org/packages/6e/6a/30d94a9674a7fe4f4744052ed6c5e083424510be1e93da5bc47569d11810/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8", size = 2063890, upload-time = "2025-11-04T13:39:36.053Z" }, - { url = "https://files.pythonhosted.org/packages/50/be/76e5d46203fcb2750e542f32e6c371ffa9b8ad17364cf94bb0818dbfb50c/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e", size = 2229740, upload-time = "2025-11-04T13:39:37.753Z" }, - { url = "https://files.pythonhosted.org/packages/d3/ee/fed784df0144793489f87db310a6bbf8118d7b630ed07aa180d6067e653a/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1", size = 2350021, upload-time = "2025-11-04T13:39:40.94Z" }, - { url = "https://files.pythonhosted.org/packages/c8/be/8fed28dd0a180dca19e72c233cbf58efa36df055e5b9d90d64fd1740b828/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b", size = 2066378, upload-time = "2025-11-04T13:39:42.523Z" }, - { url = "https://files.pythonhosted.org/packages/b0/3b/698cf8ae1d536a010e05121b4958b1257f0b5522085e335360e53a6b1c8b/pydantic_core-2.41.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b", size = 2175761, upload-time = "2025-11-04T13:39:44.553Z" }, - { url = "https://files.pythonhosted.org/packages/b8/ba/15d537423939553116dea94ce02f9c31be0fa9d0b806d427e0308ec17145/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284", size = 2146303, upload-time = "2025-11-04T13:39:46.238Z" }, - { url = "https://files.pythonhosted.org/packages/58/7f/0de669bf37d206723795f9c90c82966726a2ab06c336deba4735b55af431/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594", size = 2340355, upload-time = "2025-11-04T13:39:48.002Z" }, - { url = "https://files.pythonhosted.org/packages/e5/de/e7482c435b83d7e3c3ee5ee4451f6e8973cff0eb6007d2872ce6383f6398/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e", size = 2319875, upload-time = "2025-11-04T13:39:49.705Z" }, - { url = "https://files.pythonhosted.org/packages/fe/e6/8c9e81bb6dd7560e33b9053351c29f30c8194b72f2d6932888581f503482/pydantic_core-2.41.5-cp311-cp311-win32.whl", hash = "sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b", size = 1987549, upload-time = "2025-11-04T13:39:51.842Z" }, - { url = "https://files.pythonhosted.org/packages/11/66/f14d1d978ea94d1bc21fc98fcf570f9542fe55bfcc40269d4e1a21c19bf7/pydantic_core-2.41.5-cp311-cp311-win_amd64.whl", hash = "sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe", size = 2011305, upload-time = "2025-11-04T13:39:53.485Z" }, - { url = "https://files.pythonhosted.org/packages/56/d8/0e271434e8efd03186c5386671328154ee349ff0354d83c74f5caaf096ed/pydantic_core-2.41.5-cp311-cp311-win_arm64.whl", hash = "sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f", size = 1972902, upload-time = "2025-11-04T13:39:56.488Z" }, - { url = "https://files.pythonhosted.org/packages/5f/5d/5f6c63eebb5afee93bcaae4ce9a898f3373ca23df3ccaef086d0233a35a7/pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7", size = 2110990, upload-time = "2025-11-04T13:39:58.079Z" }, - { url = "https://files.pythonhosted.org/packages/aa/32/9c2e8ccb57c01111e0fd091f236c7b371c1bccea0fa85247ac55b1e2b6b6/pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0", size = 1896003, upload-time = "2025-11-04T13:39:59.956Z" }, - { url = "https://files.pythonhosted.org/packages/68/b8/a01b53cb0e59139fbc9e4fda3e9724ede8de279097179be4ff31f1abb65a/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69", size = 1919200, upload-time = "2025-11-04T13:40:02.241Z" }, - { url = "https://files.pythonhosted.org/packages/38/de/8c36b5198a29bdaade07b5985e80a233a5ac27137846f3bc2d3b40a47360/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75", size = 2052578, upload-time = "2025-11-04T13:40:04.401Z" }, - { url = "https://files.pythonhosted.org/packages/00/b5/0e8e4b5b081eac6cb3dbb7e60a65907549a1ce035a724368c330112adfdd/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05", size = 2208504, upload-time = "2025-11-04T13:40:06.072Z" }, - { url = "https://files.pythonhosted.org/packages/77/56/87a61aad59c7c5b9dc8caad5a41a5545cba3810c3e828708b3d7404f6cef/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc", size = 2335816, upload-time = "2025-11-04T13:40:07.835Z" }, - { url = "https://files.pythonhosted.org/packages/0d/76/941cc9f73529988688a665a5c0ecff1112b3d95ab48f81db5f7606f522d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c", size = 2075366, upload-time = "2025-11-04T13:40:09.804Z" }, - { url = "https://files.pythonhosted.org/packages/d3/43/ebef01f69baa07a482844faaa0a591bad1ef129253ffd0cdaa9d8a7f72d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5", size = 2171698, upload-time = "2025-11-04T13:40:12.004Z" }, - { url = "https://files.pythonhosted.org/packages/b1/87/41f3202e4193e3bacfc2c065fab7706ebe81af46a83d3e27605029c1f5a6/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c", size = 2132603, upload-time = "2025-11-04T13:40:13.868Z" }, - { url = "https://files.pythonhosted.org/packages/49/7d/4c00df99cb12070b6bccdef4a195255e6020a550d572768d92cc54dba91a/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294", size = 2329591, upload-time = "2025-11-04T13:40:15.672Z" }, - { url = "https://files.pythonhosted.org/packages/cc/6a/ebf4b1d65d458f3cda6a7335d141305dfa19bdc61140a884d165a8a1bbc7/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1", size = 2319068, upload-time = "2025-11-04T13:40:17.532Z" }, - { url = "https://files.pythonhosted.org/packages/49/3b/774f2b5cd4192d5ab75870ce4381fd89cf218af999515baf07e7206753f0/pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d", size = 1985908, upload-time = "2025-11-04T13:40:19.309Z" }, - { url = "https://files.pythonhosted.org/packages/86/45/00173a033c801cacf67c190fef088789394feaf88a98a7035b0e40d53dc9/pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815", size = 2020145, upload-time = "2025-11-04T13:40:21.548Z" }, - { url = "https://files.pythonhosted.org/packages/f9/22/91fbc821fa6d261b376a3f73809f907cec5ca6025642c463d3488aad22fb/pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3", size = 1976179, upload-time = "2025-11-04T13:40:23.393Z" }, - { url = "https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9", size = 2120403, upload-time = "2025-11-04T13:40:25.248Z" }, - { url = "https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34", size = 1896206, upload-time = "2025-11-04T13:40:27.099Z" }, - { url = "https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0", size = 1919307, upload-time = "2025-11-04T13:40:29.806Z" }, - { url = "https://files.pythonhosted.org/packages/9a/e3/6324802931ae1d123528988e0e86587c2072ac2e5394b4bc2bc34b61ff6e/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33", size = 2063258, upload-time = "2025-11-04T13:40:33.544Z" }, - { url = "https://files.pythonhosted.org/packages/c9/d4/2230d7151d4957dd79c3044ea26346c148c98fbf0ee6ebd41056f2d62ab5/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e", size = 2214917, upload-time = "2025-11-04T13:40:35.479Z" }, - { url = "https://files.pythonhosted.org/packages/e6/9f/eaac5df17a3672fef0081b6c1bb0b82b33ee89aa5cec0d7b05f52fd4a1fa/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2", size = 2332186, upload-time = "2025-11-04T13:40:37.436Z" }, - { url = "https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586", size = 2073164, upload-time = "2025-11-04T13:40:40.289Z" }, - { url = "https://files.pythonhosted.org/packages/bf/e3/f6e262673c6140dd3305d144d032f7bd5f7497d3871c1428521f19f9efa2/pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d", size = 2179146, upload-time = "2025-11-04T13:40:42.809Z" }, - { url = "https://files.pythonhosted.org/packages/75/c7/20bd7fc05f0c6ea2056a4565c6f36f8968c0924f19b7d97bbfea55780e73/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740", size = 2137788, upload-time = "2025-11-04T13:40:44.752Z" }, - { url = "https://files.pythonhosted.org/packages/3a/8d/34318ef985c45196e004bc46c6eab2eda437e744c124ef0dbe1ff2c9d06b/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e", size = 2340133, upload-time = "2025-11-04T13:40:46.66Z" }, - { url = "https://files.pythonhosted.org/packages/9c/59/013626bf8c78a5a5d9350d12e7697d3d4de951a75565496abd40ccd46bee/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858", size = 2324852, upload-time = "2025-11-04T13:40:48.575Z" }, - { url = "https://files.pythonhosted.org/packages/1a/d9/c248c103856f807ef70c18a4f986693a46a8ffe1602e5d361485da502d20/pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36", size = 1994679, upload-time = "2025-11-04T13:40:50.619Z" }, - { url = "https://files.pythonhosted.org/packages/9e/8b/341991b158ddab181cff136acd2552c9f35bd30380422a639c0671e99a91/pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11", size = 2019766, upload-time = "2025-11-04T13:40:52.631Z" }, - { url = "https://files.pythonhosted.org/packages/73/7d/f2f9db34af103bea3e09735bb40b021788a5e834c81eedb541991badf8f5/pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd", size = 1981005, upload-time = "2025-11-04T13:40:54.734Z" }, - { url = "https://files.pythonhosted.org/packages/ea/28/46b7c5c9635ae96ea0fbb779e271a38129df2550f763937659ee6c5dbc65/pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a", size = 2119622, upload-time = "2025-11-04T13:40:56.68Z" }, - { url = "https://files.pythonhosted.org/packages/74/1a/145646e5687e8d9a1e8d09acb278c8535ebe9e972e1f162ed338a622f193/pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14", size = 1891725, upload-time = "2025-11-04T13:40:58.807Z" }, - { url = "https://files.pythonhosted.org/packages/23/04/e89c29e267b8060b40dca97bfc64a19b2a3cf99018167ea1677d96368273/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1", size = 1915040, upload-time = "2025-11-04T13:41:00.853Z" }, - { url = "https://files.pythonhosted.org/packages/84/a3/15a82ac7bd97992a82257f777b3583d3e84bdb06ba6858f745daa2ec8a85/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66", size = 2063691, upload-time = "2025-11-04T13:41:03.504Z" }, - { url = "https://files.pythonhosted.org/packages/74/9b/0046701313c6ef08c0c1cf0e028c67c770a4e1275ca73131563c5f2a310a/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869", size = 2213897, upload-time = "2025-11-04T13:41:05.804Z" }, - { url = "https://files.pythonhosted.org/packages/8a/cd/6bac76ecd1b27e75a95ca3a9a559c643b3afcd2dd62086d4b7a32a18b169/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2", size = 2333302, upload-time = "2025-11-04T13:41:07.809Z" }, - { url = "https://files.pythonhosted.org/packages/4c/d2/ef2074dc020dd6e109611a8be4449b98cd25e1b9b8a303c2f0fca2f2bcf7/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375", size = 2064877, upload-time = "2025-11-04T13:41:09.827Z" }, - { url = "https://files.pythonhosted.org/packages/18/66/e9db17a9a763d72f03de903883c057b2592c09509ccfe468187f2a2eef29/pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553", size = 2180680, upload-time = "2025-11-04T13:41:12.379Z" }, - { url = "https://files.pythonhosted.org/packages/d3/9e/3ce66cebb929f3ced22be85d4c2399b8e85b622db77dad36b73c5387f8f8/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90", size = 2138960, upload-time = "2025-11-04T13:41:14.627Z" }, - { url = "https://files.pythonhosted.org/packages/a6/62/205a998f4327d2079326b01abee48e502ea739d174f0a89295c481a2272e/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07", size = 2339102, upload-time = "2025-11-04T13:41:16.868Z" }, - { url = "https://files.pythonhosted.org/packages/3c/0d/f05e79471e889d74d3d88f5bd20d0ed189ad94c2423d81ff8d0000aab4ff/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb", size = 2326039, upload-time = "2025-11-04T13:41:18.934Z" }, - { url = "https://files.pythonhosted.org/packages/ec/e1/e08a6208bb100da7e0c4b288eed624a703f4d129bde2da475721a80cab32/pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23", size = 1995126, upload-time = "2025-11-04T13:41:21.418Z" }, - { url = "https://files.pythonhosted.org/packages/48/5d/56ba7b24e9557f99c9237e29f5c09913c81eeb2f3217e40e922353668092/pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf", size = 2015489, upload-time = "2025-11-04T13:41:24.076Z" }, - { url = "https://files.pythonhosted.org/packages/4e/bb/f7a190991ec9e3e0ba22e4993d8755bbc4a32925c0b5b42775c03e8148f9/pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0", size = 1977288, upload-time = "2025-11-04T13:41:26.33Z" }, - { url = "https://files.pythonhosted.org/packages/92/ed/77542d0c51538e32e15afe7899d79efce4b81eee631d99850edc2f5e9349/pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a", size = 2120255, upload-time = "2025-11-04T13:41:28.569Z" }, - { url = "https://files.pythonhosted.org/packages/bb/3d/6913dde84d5be21e284439676168b28d8bbba5600d838b9dca99de0fad71/pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3", size = 1863760, upload-time = "2025-11-04T13:41:31.055Z" }, - { url = "https://files.pythonhosted.org/packages/5a/f0/e5e6b99d4191da102f2b0eb9687aaa7f5bea5d9964071a84effc3e40f997/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c", size = 1878092, upload-time = "2025-11-04T13:41:33.21Z" }, - { url = "https://files.pythonhosted.org/packages/71/48/36fb760642d568925953bcc8116455513d6e34c4beaa37544118c36aba6d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612", size = 2053385, upload-time = "2025-11-04T13:41:35.508Z" }, - { url = "https://files.pythonhosted.org/packages/20/25/92dc684dd8eb75a234bc1c764b4210cf2646479d54b47bf46061657292a8/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d", size = 2218832, upload-time = "2025-11-04T13:41:37.732Z" }, - { url = "https://files.pythonhosted.org/packages/e2/09/f53e0b05023d3e30357d82eb35835d0f6340ca344720a4599cd663dca599/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9", size = 2327585, upload-time = "2025-11-04T13:41:40Z" }, - { url = "https://files.pythonhosted.org/packages/aa/4e/2ae1aa85d6af35a39b236b1b1641de73f5a6ac4d5a7509f77b814885760c/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660", size = 2041078, upload-time = "2025-11-04T13:41:42.323Z" }, - { url = "https://files.pythonhosted.org/packages/cd/13/2e215f17f0ef326fc72afe94776edb77525142c693767fc347ed6288728d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9", size = 2173914, upload-time = "2025-11-04T13:41:45.221Z" }, - { url = "https://files.pythonhosted.org/packages/02/7a/f999a6dcbcd0e5660bc348a3991c8915ce6599f4f2c6ac22f01d7a10816c/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3", size = 2129560, upload-time = "2025-11-04T13:41:47.474Z" }, - { url = "https://files.pythonhosted.org/packages/3a/b1/6c990ac65e3b4c079a4fb9f5b05f5b013afa0f4ed6780a3dd236d2cbdc64/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf", size = 2329244, upload-time = "2025-11-04T13:41:49.992Z" }, - { url = "https://files.pythonhosted.org/packages/d9/02/3c562f3a51afd4d88fff8dffb1771b30cfdfd79befd9883ee094f5b6c0d8/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470", size = 2331955, upload-time = "2025-11-04T13:41:54.079Z" }, - { url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" }, - { url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" }, - { url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" }, - { url = "https://files.pythonhosted.org/packages/11/72/90fda5ee3b97e51c494938a4a44c3a35a9c96c19bba12372fb9c634d6f57/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034", size = 2115441, upload-time = "2025-11-04T13:42:39.557Z" }, - { url = "https://files.pythonhosted.org/packages/1f/53/8942f884fa33f50794f119012dc6a1a02ac43a56407adaac20463df8e98f/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c", size = 1930291, upload-time = "2025-11-04T13:42:42.169Z" }, - { url = "https://files.pythonhosted.org/packages/79/c8/ecb9ed9cd942bce09fc888ee960b52654fbdbede4ba6c2d6e0d3b1d8b49c/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2", size = 1948632, upload-time = "2025-11-04T13:42:44.564Z" }, - { url = "https://files.pythonhosted.org/packages/2e/1b/687711069de7efa6af934e74f601e2a4307365e8fdc404703afc453eab26/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad", size = 2138905, upload-time = "2025-11-04T13:42:47.156Z" }, - { url = "https://files.pythonhosted.org/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", size = 2110495, upload-time = "2025-11-04T13:42:49.689Z" }, - { url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388, upload-time = "2025-11-04T13:42:52.215Z" }, - { url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879, upload-time = "2025-11-04T13:42:56.483Z" }, - { url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017, upload-time = "2025-11-04T13:42:59.471Z" }, - { url = "https://files.pythonhosted.org/packages/5f/9b/1b3f0e9f9305839d7e84912f9e8bfbd191ed1b1ef48083609f0dabde978c/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26", size = 2101980, upload-time = "2025-11-04T13:43:25.97Z" }, - { url = "https://files.pythonhosted.org/packages/a4/ed/d71fefcb4263df0da6a85b5d8a7508360f2f2e9b3bf5814be9c8bccdccc1/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808", size = 1923865, upload-time = "2025-11-04T13:43:28.763Z" }, - { url = "https://files.pythonhosted.org/packages/ce/3a/626b38db460d675f873e4444b4bb030453bbe7b4ba55df821d026a0493c4/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc", size = 2134256, upload-time = "2025-11-04T13:43:31.71Z" }, - { url = "https://files.pythonhosted.org/packages/83/d9/8412d7f06f616bbc053d30cb4e5f76786af3221462ad5eee1f202021eb4e/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1", size = 2174762, upload-time = "2025-11-04T13:43:34.744Z" }, - { url = "https://files.pythonhosted.org/packages/55/4c/162d906b8e3ba3a99354e20faa1b49a85206c47de97a639510a0e673f5da/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84", size = 2143141, upload-time = "2025-11-04T13:43:37.701Z" }, - { url = "https://files.pythonhosted.org/packages/1f/f2/f11dd73284122713f5f89fc940f370d035fa8e1e078d446b3313955157fe/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770", size = 2330317, upload-time = "2025-11-04T13:43:40.406Z" }, - { url = "https://files.pythonhosted.org/packages/88/9d/b06ca6acfe4abb296110fb1273a4d848a0bfb2ff65f3ee92127b3244e16b/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f", size = 2316992, upload-time = "2025-11-04T13:43:43.602Z" }, - { url = "https://files.pythonhosted.org/packages/36/c7/cfc8e811f061c841d7990b0201912c3556bfeb99cdcb7ed24adc8d6f8704/pydantic_core-2.41.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51", size = 2145302, upload-time = "2025-11-04T13:43:46.64Z" }, + { url = "https://files.pythonhosted.org/packages/5c/fa/6d7708d2cfc1a832acb6aeb0cd16e801902df8a0f583bb3b4b527fde022e/pydantic_core-2.46.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:0e96592440881c74a213e5ad528e2b24d3d4f940de2766bed9010ab1d9e51594", size = 2111872, upload-time = "2026-05-06T13:40:27.596Z" }, + { url = "https://files.pythonhosted.org/packages/ae/6f/aa064a3e74b5745afbdf250594f38e7ead05e2d651bcb35994b9417a0d4d/pydantic_core-2.46.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e0d65b8c354be7fb5f720c3caa8bc940bc2d20ce749c8e06135f07f8ed95dd7c", size = 1948255, upload-time = "2026-05-06T13:39:12.574Z" }, + { url = "https://files.pythonhosted.org/packages/43/3a/41114a9f7569b84b4d84e7a018c57c56347dac30c0d4a872946ec4e36c46/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bfb192b3f4b9e8a89b6277b6ce787564f62cfd272055f6e685726b111dc7826", size = 1972827, upload-time = "2026-05-06T13:38:19.841Z" }, + { url = "https://files.pythonhosted.org/packages/ef/25/1ab42e8048fe551934d9884e8d64daa7e990ad386f310a15981aeb6a5b08/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9037063db01f09b09e237c282b6792bd4da634b5402c4e7f0c61effed7701a04", size = 2041051, upload-time = "2026-05-06T13:38:10.447Z" }, + { url = "https://files.pythonhosted.org/packages/94/c2/1a934597ddf08da410385b3b7aae91956a5a76c635effef456074fad7e88/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc010ab034c8c7452522748bf937df58020d256ccae0874463d1f4d01758af8e", size = 2221314, upload-time = "2026-05-06T13:40:13.089Z" }, + { url = "https://files.pythonhosted.org/packages/02/6d/9e8ad178c9c4df27ad3c8f25d1fe2a7ab0d2ba0559fad4aee5d3d1f16771/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c5dac79fa1614d1e06ca695109c6105923bd9c7d1d6c918d4e637b7e6b32fd3", size = 2285146, upload-time = "2026-05-06T13:38:59.224Z" }, + { url = "https://files.pythonhosted.org/packages/80/50/540cd3aeefc041beb111125c4bff779831a2111fc6b15a9138cda277d32c/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9fa868638bf362d3d138ea55829cefb3d5f4b0d7f142234382a15e2485dbec4", size = 2089685, upload-time = "2026-05-06T13:38:17.762Z" }, + { url = "https://files.pythonhosted.org/packages/6b/a4/b440ad35f05f6a38f89fa0f149accb3f0e02be94ca5e15f3c449a61b4bc9/pydantic_core-2.46.4-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:17299feefe090f2caa5b8e37222bb5f663e4935a8bfa6931d4102e5df1a9f398", size = 2115420, upload-time = "2026-05-06T13:37:58.195Z" }, + { url = "https://files.pythonhosted.org/packages/99/61/de4f55db8dfd57bfdfa9a12ec90fe1b57c4f41062f7ca86f08586b3e0ac0/pydantic_core-2.46.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4c63ebc82684aa89d9a3bcbd13d515b3be44250dc68dd3bd81526c1cb31286c3", size = 2165122, upload-time = "2026-05-06T13:37:01.167Z" }, + { url = "https://files.pythonhosted.org/packages/f7/52/7c529d7bdb2d1068bd52f51fe32572c8301f9a4febf1948f10639f1436f5/pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:aaa2a54443eff1950ba5ddc6b6ccda0d9c84a364276a62f969bdf2a390650848", size = 2182573, upload-time = "2026-05-06T13:38:45.04Z" }, + { url = "https://files.pythonhosted.org/packages/37/b3/7c40325848ba78247f2812dcf9c7274e38cd801820ca6dd9fe63bcfb0eb4/pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:18e5ceec2ab67e6d5f1a9085e5a24c9c4e2ac4545730bfe668680bca05e555f3", size = 2317139, upload-time = "2026-05-06T13:37:15.539Z" }, + { url = "https://files.pythonhosted.org/packages/d9/37/f913f81a657c865b75da6c0dbed79876073c2a43b5bd9edbe8da785e4d49/pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a0f62d0a58f4e7da165457e995725421e0064f2255d8eccebc49f41bbc23b109", size = 2360433, upload-time = "2026-05-06T13:37:30.099Z" }, + { url = "https://files.pythonhosted.org/packages/c4/67/6acaa1be2567f9256b056d8477158cac7240813956ce86e49deae8e173b4/pydantic_core-2.46.4-cp311-cp311-win32.whl", hash = "sha256:041bde0a48fd37cf71cab1c9d56d3e8625a3793fef1f7dd232b3ff37e978ecda", size = 1985513, upload-time = "2026-05-06T13:38:15.669Z" }, + { url = "https://files.pythonhosted.org/packages/aa/e6/c505f83dfeda9a2e5c995cfd872949e4d05e12f7feb3dca72f633daefa94/pydantic_core-2.46.4-cp311-cp311-win_amd64.whl", hash = "sha256:6f2eeda33a839975441c86a4119e1383c50b47faf0cbb5176985565c6bb02c33", size = 2071114, upload-time = "2026-05-06T13:40:35.416Z" }, + { url = "https://files.pythonhosted.org/packages/0f/da/7a263a96d965d9d0df5e8de8a475f33495451117035b09acb110288c381f/pydantic_core-2.46.4-cp311-cp311-win_arm64.whl", hash = "sha256:14f4c5d6db102bd796a627bbb3a17b4cf4574b9ae861d8b7c9a9661c6dd3362d", size = 2044298, upload-time = "2026-05-06T13:38:29.754Z" }, + { url = "https://files.pythonhosted.org/packages/ce/8c/af022f0af448d7747c5154288d46b5f2bc5f17366eaa0e23e9aa04d59f3b/pydantic_core-2.46.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3245406455a5d98187ec35530fd772b1d799b26667980872c8d4614991e2c4a2", size = 2106158, upload-time = "2026-05-06T13:38:57.215Z" }, + { url = "https://files.pythonhosted.org/packages/19/95/6195171e385007300f0f5574592e467c568becce2d937a0b6804f218bc49/pydantic_core-2.46.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:962ccbab7b642487b1d8b7df90ef677e03134cf1fd8880bf698649b22a69371f", size = 1951724, upload-time = "2026-05-06T13:37:02.697Z" }, + { url = "https://files.pythonhosted.org/packages/8e/bc/f47d1ff9cbb1620e1b5b697eef06010035735f07820180e74178226b27b3/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8233f2947cf85404441fd7e0085f53b10c93e0ee78611099b5c7237e36aacbf7", size = 1975742, upload-time = "2026-05-06T13:37:09.448Z" }, + { url = "https://files.pythonhosted.org/packages/5b/11/9b9a5b0306345664a2da6410877af6e8082481b5884b3ddd78d47c6013ce/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3a233125ac121aa3ffba9a2b59edfc4a985a76092dc8279586ab4b71390875e7", size = 2052418, upload-time = "2026-05-06T13:37:38.234Z" }, + { url = "https://files.pythonhosted.org/packages/f1/b7/a65fec226f5d78fc39f4a13c4cc0c768c22b113438f60c14adc9d2865038/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b712b53160b79a5850310b912a5ef8e57e56947c8ad690c227f5c9d7e561712", size = 2232274, upload-time = "2026-05-06T13:38:27.753Z" }, + { url = "https://files.pythonhosted.org/packages/68/f0/92039db98b907ef49269a8271f67db9cb78ae2fc68062ef7e4e77adb5f61/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9401557acd873c3a7f3eb9383edef8ac4968f9510e340f4808d427e75667e7b4", size = 2309940, upload-time = "2026-05-06T13:38:05.353Z" }, + { url = "https://files.pythonhosted.org/packages/5f/97/2aab507d3d00ca626e8e57c1eac6a79e4e5fbcc63eb99733ff55d1717f65/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:926c9541b14b12b1681dca8a0b75feb510b06c6341b70a8e500c2fdcff837cce", size = 2094516, upload-time = "2026-05-06T13:39:10.577Z" }, + { url = "https://files.pythonhosted.org/packages/22/37/a8aca44d40d737dde2bc05b3c6c07dff0de07ce6f82e9f3167aeaf4d5dea/pydantic_core-2.46.4-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:56cb4851bcaf3d117eddcef4fe66afd750a50274b0da8e22be256d10e5611987", size = 2136854, upload-time = "2026-05-06T13:40:22.59Z" }, + { url = "https://files.pythonhosted.org/packages/24/99/fcef1b79238c06a8cbec70819ac722ba76e02bc8ada9b0fd66eba40da01b/pydantic_core-2.46.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c68fcd102d71ea85c5b2dfac3f4f8476eff42a9e078fd5faefff6d145063536b", size = 2180306, upload-time = "2026-05-06T13:40:10.666Z" }, + { url = "https://files.pythonhosted.org/packages/ae/6c/fc44000918855b42779d007ae63b0532794739027b2f417321cddbc44f6a/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b2f69dec1725e79a012d920df1707de5caf7ed5e08f3be4435e25803efc47458", size = 2190044, upload-time = "2026-05-06T13:40:43.231Z" }, + { url = "https://files.pythonhosted.org/packages/6b/65/d9cadc9f1920d7a127ad2edba16c1db7916e59719285cd6c94600b0080ba/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:8d0820e8192167f80d88d64038e609c31452eeca865b4e1d9950a27a4609b00b", size = 2329133, upload-time = "2026-05-06T13:39:57.365Z" }, + { url = "https://files.pythonhosted.org/packages/d0/cf/c873d91679f3a30bcf5e7ac280ce5573483e72295307685120d0d5ad3416/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fbdb89b3e1c94a30cc5edfce477c6e6a5dc4d8f84665b455c27582f211a1c72c", size = 2374464, upload-time = "2026-05-06T13:38:06.976Z" }, + { url = "https://files.pythonhosted.org/packages/47/bd/6f2fc8188f31bf10590f1e98e7b306336161fac930a8c514cd7bd828c7dc/pydantic_core-2.46.4-cp312-cp312-win32.whl", hash = "sha256:9aa768456404a8bf48a4406685ac2bec8e72b62c69313734fa3b73cf33b3a894", size = 1974823, upload-time = "2026-05-06T13:40:47.985Z" }, + { url = "https://files.pythonhosted.org/packages/40/8c/985c1d41ea1107c2534abd9870e4ed5c8e7669b5c308297835c001e7a1c4/pydantic_core-2.46.4-cp312-cp312-win_amd64.whl", hash = "sha256:e9c26f834c65f5752f3f06cb08cb86a913ceb7274d0db6e267808a708b46bc89", size = 2072919, upload-time = "2026-05-06T13:39:21.153Z" }, + { url = "https://files.pythonhosted.org/packages/c4/ba/f463d006e0c47373ca7ec5e1a261c59dc01ef4d62b2657af925fb0deee3a/pydantic_core-2.46.4-cp312-cp312-win_arm64.whl", hash = "sha256:4fc73cb559bdb54b1134a706a2802a4cddd27a0633f5abb7e53056268751ac6a", size = 2027604, upload-time = "2026-05-06T13:39:03.753Z" }, + { url = "https://files.pythonhosted.org/packages/51/a2/5d30b469c5267a17b39dec53208222f76a8d351dfac4af661888c5aee77d/pydantic_core-2.46.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5d5902252db0d3cedf8d4a1bc68f70eeb430f7e4c7104c8c476753519b423008", size = 2106306, upload-time = "2026-05-06T13:37:48.029Z" }, + { url = "https://files.pythonhosted.org/packages/c1/81/4fa520eaffa8bd7d1525e644cd6d39e7d60b1592bc5b516693c7340b50f1/pydantic_core-2.46.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c94f0688e7b8d0a67abf40e57a7eaaecd17cc9586706a31b76c031f63df052b4", size = 1951906, upload-time = "2026-05-06T13:37:17.012Z" }, + { url = "https://files.pythonhosted.org/packages/03/d5/fd02da45b659668b05923b17ba3a0100a0a3d5541e3bd8fcc4ecb711309e/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f027324c56cd5406ca49c124b0db10e56c69064fec039acc571c29020cc87c76", size = 1976802, upload-time = "2026-05-06T13:37:35.113Z" }, + { url = "https://files.pythonhosted.org/packages/21/f2/95727e1368be3d3ed485eaab7adbd7dda408f33f7a36e8b48e0144002b91/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e739fee756ba1010f8bcccb534252e85a35fe45ae92c295a06059ce58b74ccd3", size = 2052446, upload-time = "2026-05-06T13:37:12.313Z" }, + { url = "https://files.pythonhosted.org/packages/9c/86/5d99feea3f77c7234b8718075b23db11532773c1a0dbd9b9490215dc2eeb/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d56801be94b86a9da183e5f3766e6310752b99ff647e38b09a9500d88e46e76", size = 2232757, upload-time = "2026-05-06T13:39:01.149Z" }, + { url = "https://files.pythonhosted.org/packages/d2/3a/508ac615935ef7588cf6d9e9b91309fdc2da751af865e02a9098de88258c/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2412e734dcb48da14d4e4006b82b46b74f2518b8a26ee7e58c6844a6cd6d03c4", size = 2309275, upload-time = "2026-05-06T13:37:41.406Z" }, + { url = "https://files.pythonhosted.org/packages/07/f8/41db9de19d7987d6b04715a02b3b40aea467000275d9d758ffaa31af7d50/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9551187363ffc0de2a00b2e47c25aeaeb1020b69b668762966df15fc5659dd5a", size = 2094467, upload-time = "2026-05-06T13:39:18.847Z" }, + { url = "https://files.pythonhosted.org/packages/2c/e2/f35033184cb11d0052daf4416e8e10a502ea2ac006fc4f459aee872727d1/pydantic_core-2.46.4-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:0186750b482eefa11d7f435892b09c5c606193ef3375bcf94aa00ae6bfb66262", size = 2134417, upload-time = "2026-05-06T13:40:17.944Z" }, + { url = "https://files.pythonhosted.org/packages/7e/7b/6ceeb1cc90e193862f444ebe373d8fdf613f0a82572dde03fb10734c6c71/pydantic_core-2.46.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5855698a4856556d86e8e6cd8434bc3ac0314ee8e12089ae0e143f64c6256e4e", size = 2179782, upload-time = "2026-05-06T13:40:32.618Z" }, + { url = "https://files.pythonhosted.org/packages/5a/f2/c8d7773ede6af08036423a00ae0ceffce266c3c52a096c435d68c896083f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:cbaf13819775b7f769bf4a1f066cb6df7a28d4480081a589828ef190226881cd", size = 2188782, upload-time = "2026-05-06T13:36:51.018Z" }, + { url = "https://files.pythonhosted.org/packages/59/31/0c864784e31f09f05cdd87606f08923b9c9e7f6e51dd27f20f62f975ce9f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:633147d34cf4550417f12e2b1a0383973bdf5cdfde212cb09e9a581cf10820be", size = 2328334, upload-time = "2026-05-06T13:40:37.764Z" }, + { url = "https://files.pythonhosted.org/packages/c2/eb/4f6c8a41efa30baa755590f4141abf3a8c370fab610915733e74134a7270/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:82cf5301172168103724d49a1444d3378cb20cdee30b116a1bd6031236298a5d", size = 2372986, upload-time = "2026-05-06T13:39:34.152Z" }, + { url = "https://files.pythonhosted.org/packages/5b/24/b375a480d53113860c299764bfe9f349a3dc9108b3adc0d7f0d786492ebf/pydantic_core-2.46.4-cp313-cp313-win32.whl", hash = "sha256:9fa8ae11da9e2b3126c6426f147e0fba88d96d65921799bb30c6abd1cb2c97fb", size = 1973693, upload-time = "2026-05-06T13:37:55.072Z" }, + { url = "https://files.pythonhosted.org/packages/7e/e8/cff247591966f2d22ec8c003cd7587e27b7ba7b81ab2fb888e3ab75dc285/pydantic_core-2.46.4-cp313-cp313-win_amd64.whl", hash = "sha256:6b3ace8194b0e5204818c92802dcdca7fc6d88aabbb799d7c795540d9cd6d292", size = 2071819, upload-time = "2026-05-06T13:38:49.139Z" }, + { url = "https://files.pythonhosted.org/packages/c6/1a/f4aee670d5670e9e148e0c82c7db98d780be566c6e6a97ee8035528ca0b3/pydantic_core-2.46.4-cp313-cp313-win_arm64.whl", hash = "sha256:184c081504d17f1c1066e430e117142b2c77d9448a97f7b65c6ac9fd9aee238d", size = 2027411, upload-time = "2026-05-06T13:40:45.796Z" }, + { url = "https://files.pythonhosted.org/packages/8d/74/228a26ddad29c6672b805d9fd78e8d251cd04004fa7eed0e622096cd0250/pydantic_core-2.46.4-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:428e04521a40150c85216fc8b85e8d39fece235a9cf5e383761238c7fa9b96fb", size = 2102079, upload-time = "2026-05-06T13:38:41.019Z" }, + { url = "https://files.pythonhosted.org/packages/ad/1f/8970b150a4b4365623ae00fc88603491f763c627311ae8031e3111356d6e/pydantic_core-2.46.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:23ace664830ee0bfe014a0c7bc248b1f7f25ed7ad103852c317624a1083af462", size = 1952179, upload-time = "2026-05-06T13:36:59.812Z" }, + { url = "https://files.pythonhosted.org/packages/95/30/5211a831ae054928054b2f79731661087a2bc5c01e825c672b3a4a8f1b3e/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce5c1d2a8b27468f433ca974829c44060b8097eedc39933e3c206a90ee49c4a9", size = 1978926, upload-time = "2026-05-06T13:37:39.933Z" }, + { url = "https://files.pythonhosted.org/packages/57/e9/689668733b1eb67adeef047db3c2e8788fcf65a7fd9c9e2b46b7744fe245/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7283d57845ecf5a163403eb0702dfc220cc4fbdd18919cb5ccea4f95ee1cdab4", size = 2046785, upload-time = "2026-05-06T13:38:01.995Z" }, + { url = "https://files.pythonhosted.org/packages/60/d9/6715260422ff50a2109878fd24d948a6c3446bb2664f34ee78cd972b3acd/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8daafc69c93ee8a0204506a3b6b30f586ef54028f52aeeeb5c4cfc5184fd5914", size = 2228733, upload-time = "2026-05-06T13:40:50.371Z" }, + { url = "https://files.pythonhosted.org/packages/18/ae/fdb2f64316afca925640f8e70bb1a564b0ec2721c1389e25b8eb4bf9a299/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd2213145bcc2ba85884d0ac63d222fece9209678f77b9b4d76f054c561adb28", size = 2307534, upload-time = "2026-05-06T13:37:21.531Z" }, + { url = "https://files.pythonhosted.org/packages/89/1d/8eff589b45bb8190a9d12c49cfad0f176a5cbd1534908a6b5125e2886239/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a5f930472650a82629163023e630d160863fce524c616f4e5186e5de9d9a49b", size = 2099732, upload-time = "2026-05-06T13:39:31.942Z" }, + { url = "https://files.pythonhosted.org/packages/06/d5/ee5a3366637fee41dee51a1fc91562dcf12ddbc68fda34e6b253da2324bb/pydantic_core-2.46.4-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:c1b3f518abeca3aa13c712fd202306e145abf59a18b094a6bafb2d2bbf59192c", size = 2129627, upload-time = "2026-05-06T13:37:25.033Z" }, + { url = "https://files.pythonhosted.org/packages/94/33/2414be571d2c6a6c4d08be21f9292b6d3fdb08949a97b6dfe985017821db/pydantic_core-2.46.4-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a7dd0b3ee80d90150e3495a3a13ac34dbcbfd4f012996a6a1d8900e91b5c0fb", size = 2179141, upload-time = "2026-05-06T13:37:14.046Z" }, + { url = "https://files.pythonhosted.org/packages/7b/79/7daa95be995be0eecc4cf75064cb33f9bbbfe3fe0158caf2f0d4a996a5c7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:3fb702cd90b0446a3a1c5e470bfa0dd23c0233b676a9099ddcc964fa6ca13898", size = 2184325, upload-time = "2026-05-06T13:36:53.615Z" }, + { url = "https://files.pythonhosted.org/packages/9f/cb/d0a382f5c0de8a222dc61c65348e0ce831b1f68e0a018450d31c2cace3a5/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:b8458003118a712e66286df6a707db01c52c0f52f7db8e4a38f0da1d3b94fc4e", size = 2323990, upload-time = "2026-05-06T13:40:29.971Z" }, + { url = "https://files.pythonhosted.org/packages/05/db/d9ba624cc4a5aced1598e88c04fdbd8310c8a69b9d38b9a3d39ce3a61ed7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:372429a130e469c9cd698925ce5fc50940b7a1336b0d82038e63d5bbc4edc519", size = 2369978, upload-time = "2026-05-06T13:37:23.027Z" }, + { url = "https://files.pythonhosted.org/packages/f2/20/d15df15ba918c423461905802bfd2981c3af0bfa0e40d05e13edbfa48bc3/pydantic_core-2.46.4-cp314-cp314-win32.whl", hash = "sha256:85bb3611ff1802f3ee7fdd7dbff26b56f343fb432d57a4728fdd49b6ef35e2f4", size = 1966354, upload-time = "2026-05-06T13:38:03.499Z" }, + { url = "https://files.pythonhosted.org/packages/fc/b6/6b8de4c0a7d7ab3004c439c80c5c1e0a3e8d78bbae19379b01960383d9e5/pydantic_core-2.46.4-cp314-cp314-win_amd64.whl", hash = "sha256:811ff8e9c313ab425368bcbb36e5c4ebd7108c2bbf4e4089cfbb0b01eff63fac", size = 2072238, upload-time = "2026-05-06T13:39:40.807Z" }, + { url = "https://files.pythonhosted.org/packages/32/36/51eb763beec1f4cf59b1db243a7dcc39cbb41230f050a09b9d69faaf0a48/pydantic_core-2.46.4-cp314-cp314-win_arm64.whl", hash = "sha256:bfec22eab3c8cc2ceec0248aec886624116dc079afa027ecc8ad4a7e62010f8a", size = 2018251, upload-time = "2026-05-06T13:37:26.72Z" }, + { url = "https://files.pythonhosted.org/packages/e8/91/855af51d625b23aa987116a19e231d2aaef9c4a415273ddc189b79a45fee/pydantic_core-2.46.4-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:af8244b2bef6aaad6d92cda81372de7f8c8d36c9f0c3ea36e827c60e7d9467a0", size = 2099593, upload-time = "2026-05-06T13:39:47.682Z" }, + { url = "https://files.pythonhosted.org/packages/fb/1b/8784a54c65edb5f49f0a14d6977cf1b209bba85a4c77445b255c2de58ab3/pydantic_core-2.46.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a4330cdbc57162e4b3aa303f588ba752257694c9c9be3e7ebb11b4aca659b5d", size = 1935226, upload-time = "2026-05-06T13:40:40.428Z" }, + { url = "https://files.pythonhosted.org/packages/e8/e7/1955d28d1afc56dd4b3ad7cc0cf39df1b9852964cf16e5d13912756d6d6b/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29c61fc04a3d840155ff08e475a04809278972fe6aef51e2720554e96367e34b", size = 1974605, upload-time = "2026-05-06T13:37:32.029Z" }, + { url = "https://files.pythonhosted.org/packages/93/e2/3fedbf0ba7a22850e6e9fd78117f1c0f10f950182344d8a6c535d468fdd8/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c50f2528cf200c5eed56faf3f4e22fcd5f38c157a8b78576e6ba3168ec35f000", size = 2030777, upload-time = "2026-05-06T13:38:55.239Z" }, + { url = "https://files.pythonhosted.org/packages/f8/61/46be275fcaaba0b4f5b9669dd852267ce1ff616592dccf7a7845588df091/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0cbe8b01f948de4286c74cdd6c667aceb38f5c1e26f0693b3983d9d74887c65e", size = 2236641, upload-time = "2026-05-06T13:37:08.096Z" }, + { url = "https://files.pythonhosted.org/packages/60/db/12e93e46a8bac9988be3c016860f83293daea8c716c029c9ace279036f2f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:617d7e2ca7dcb8c5cf6bcb8c59b8832c94b36196bbf1cbd1bfb56ed341905edd", size = 2286404, upload-time = "2026-05-06T13:40:20.221Z" }, + { url = "https://files.pythonhosted.org/packages/e2/4a/4d8b19008f38d31c53b8219cfedc2e3d5de5fe99d90076b7e767de29274f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7027560ee92211647d0d34e3f7cd6f50da56399d26a9c8ad0da286d3869a53f3", size = 2109219, upload-time = "2026-05-06T13:38:12.153Z" }, + { url = "https://files.pythonhosted.org/packages/88/70/3cbc40978fefb7bb09c6708d40d4ad1a5d70fd7213c3d17f971de868ec1f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:f99626688942fb746e545232e7726926f3be91b5975f8b55327665fafda991c7", size = 2110594, upload-time = "2026-05-06T13:40:02.971Z" }, + { url = "https://files.pythonhosted.org/packages/9d/20/b8d36736216e29491125531685b2f9e61aa5b4b2599893f8268551da3338/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fc3e9034a63de20e15e8ade85358bc6efc614008cab72898b4b4952bea0509ff", size = 2159542, upload-time = "2026-05-06T13:39:27.506Z" }, + { url = "https://files.pythonhosted.org/packages/1d/a2/367df868eb584dacf6bf82a389272406d7178e301c4ac82545ab98bc2dd9/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:97e7cf2be5c77b7d1a9713a05605d49460d02c6078d38d8bef3cbe323c548424", size = 2168146, upload-time = "2026-05-06T13:38:31.93Z" }, + { url = "https://files.pythonhosted.org/packages/c1/b8/4460f77f7e201893f649a29ab355dddd3beee8a97bcb1a320db414f9a06e/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:3bf92c5d0e00fefaab325a4d27828fe6b6e2a21848686b5b60d2d9eeb09d76c6", size = 2306309, upload-time = "2026-05-06T13:37:44.717Z" }, + { url = "https://files.pythonhosted.org/packages/64/c4/be2639293acd87dc8ddbcec41a73cee9b2ebf996fe6d892a1a74e88ad3f7/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:3ecbc122d18468d06ca279dc26a8c2e2d5acb10943bb35e36ae92096dc3b5565", size = 2369736, upload-time = "2026-05-06T13:37:05.645Z" }, + { url = "https://files.pythonhosted.org/packages/30/a6/9f9f380dbb301f67023bf8f707aaa75daadf84f7152d95c410fd7e81d994/pydantic_core-2.46.4-cp314-cp314t-win32.whl", hash = "sha256:e846ae7835bf0703ae43f534ab79a867146dadd59dc9ca5c8b53d5c8f7c9ef02", size = 1955575, upload-time = "2026-05-06T13:38:51.116Z" }, + { url = "https://files.pythonhosted.org/packages/40/1f/f1eb9eb350e795d1af8586289746f5c5677d16043040d63710e22abc43c9/pydantic_core-2.46.4-cp314-cp314t-win_amd64.whl", hash = "sha256:2108ba5c1c1eca18030634489dc544844144ee36357f2f9f780b93e7ddbb44b5", size = 2051624, upload-time = "2026-05-06T13:38:21.672Z" }, + { url = "https://files.pythonhosted.org/packages/f6/d2/42dd53d0a85c27606f316d3aa5d2869c4e8470a5ed6dec30e4a1abe19192/pydantic_core-2.46.4-cp314-cp314t-win_arm64.whl", hash = "sha256:4fcbe087dbc2068af7eda3aa87634eba216dbda64d1ae73c8684b621d33f6596", size = 2017325, upload-time = "2026-05-06T13:40:52.723Z" }, + { url = "https://files.pythonhosted.org/packages/ee/a4/73995fd4ebbb46ba0ee51e6fa049b8f02c40daebb762208feda8a6b7894d/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:14d4edf427bdcf950a8a02d7cb44a08614388dd6e1bdcbf4f67504fa7887da9c", size = 2111589, upload-time = "2026-05-06T13:37:10.817Z" }, + { url = "https://files.pythonhosted.org/packages/fb/7f/f37d3a5e8bfcc2e403f5c57a730f2d815693fb42119e8ea48b3789335af1/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:0ce40cd7b21210e99342afafbd4d0f76d784eb5b1d60f3bdc566be4983c6c73b", size = 1944552, upload-time = "2026-05-06T13:36:56.717Z" }, + { url = "https://files.pythonhosted.org/packages/15/3c/d7eb777b3ff43e8433a4efb39a17aa8fd98a4ee8561a24a67ef5db07b2d6/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90884113d8b48f760e9587002789ddd741e76ab9f89518cd1e43b1f1a52ec44b", size = 1982984, upload-time = "2026-05-06T13:39:06.207Z" }, + { url = "https://files.pythonhosted.org/packages/63/87/70b9f40170a81afd55ca26c9b2acb25c20d64bcfbf888fafecb3ba077d4c/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66ce7632c22d837c95301830e111ad0128a32b8207533b60896a96c4915192ea", size = 2138417, upload-time = "2026-05-06T13:39:45.476Z" }, + { url = "https://files.pythonhosted.org/packages/9d/1d/8987ad40f65ae1432753072f214fb5c74fe47ffbd0698bb9cbbb585664f8/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:1d8ba486450b14f3b1d63bc521d410ec7565e52f887b9fb671791886436a42f7", size = 2095527, upload-time = "2026-05-06T13:39:52.283Z" }, + { url = "https://files.pythonhosted.org/packages/64/d3/84c282a7eee1d3ac4c0377546ef5a1ea436ce26840d9ac3b7ed54a377507/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:3009f12e4e90b7f88b4f9adb1b0c4a3d58fe7820f3238c190047209d148026df", size = 1936024, upload-time = "2026-05-06T13:40:15.671Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ca/eac61596cdeb4d7e174d3dc0bd8a6238f14f75f97a24e7b7db4c7e7340a0/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad785e92e6dc634c21555edc8bd6b64957ab844541bcb96a1366c202951ae526", size = 1990696, upload-time = "2026-05-06T13:38:34.717Z" }, + { url = "https://files.pythonhosted.org/packages/fa/c3/7c8b240552251faf6b3a957db200fcfbbcec36763c050428b601e0c9b83b/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00c603d540afdd6b80eb39f078f33ebd46211f02f33e34a32d9f053bba711de0", size = 2147590, upload-time = "2026-05-06T13:39:29.883Z" }, + { url = "https://files.pythonhosted.org/packages/11/cb/428de0385b6c8d44b716feba566abfacfbd23ee3c4439faa789a1456242f/pydantic_core-2.46.4-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:0c563b08bca408dc7f65f700633d8442fffb2421fc47b8101377e9fd65051ff0", size = 2112782, upload-time = "2026-05-06T13:37:04.016Z" }, + { url = "https://files.pythonhosted.org/packages/0b/b5/6a17bdadd0fc1f170adfd05a20d37c832f52b117b4d9131da1f41bb097ce/pydantic_core-2.46.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:db06ffe51636ffe9ca531fe9023dd64bdd794be8754cb5df57c5498ae5b518a7", size = 1952146, upload-time = "2026-05-06T13:39:43.092Z" }, + { url = "https://files.pythonhosted.org/packages/2a/dc/03734d80e362cd43ef65428e9de77c730ce7f2f11c60d2b1e1b39f0fbf99/pydantic_core-2.46.4-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:133878133d271ade3d41d1bfb2a45ec38dbdbda40bc065921c6b04e4630127e2", size = 2134492, upload-time = "2026-05-06T13:36:58.124Z" }, + { url = "https://files.pythonhosted.org/packages/de/df/5e5ffc085ed07cc22d298134d3d911c63e91f6a0eb91fe646750a3209910/pydantic_core-2.46.4-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9bc519fbf2b7578398853d815009ae5e4d4603d12f4e3f91da8c06852d3da3e9", size = 2156604, upload-time = "2026-05-06T13:37:49.88Z" }, + { url = "https://files.pythonhosted.org/packages/81/44/6e112a4253e56f5705467cbab7ab5e91ee7398ba3d56d358635958893d3e/pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:c7a7bd4e39e8e4c12c39cd480356842b6a8a06e41b23a55a5e3e191718838ddf", size = 2183828, upload-time = "2026-05-06T13:37:43.053Z" }, + { url = "https://files.pythonhosted.org/packages/ac/ad/5565071e937d8e752842ac241463944c9eb14c87e2d269f2658a5bd05e98/pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:d396ec2b979760aaf3218e76c24e65bd0aca24983298653b3a9d7a45f9e47b30", size = 2310000, upload-time = "2026-05-06T13:37:56.694Z" }, + { url = "https://files.pythonhosted.org/packages/4f/c3/66883a5cec183e7fba4d024b4cbbe61851a63750ef606b0afecc46d1f2bf/pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:86e1a4418c6cd97d60c95c71164158eaf7324fae7b0923264016baa993eba6fc", size = 2361286, upload-time = "2026-05-06T13:40:05.667Z" }, + { url = "https://files.pythonhosted.org/packages/4b/2d/69abac8f838090bbecd5df894befb2c2619e7996a98ddb949db9f3b93225/pydantic_core-2.46.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:d51026d73fcfd93610abc7b27789c26b313920fcfb20e27462d74a7f8b06e983", size = 2193071, upload-time = "2026-05-06T13:38:08.682Z" }, ] [[package]] name = "pydantic-settings" -version = "2.13.1" +version = "2.14.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic" }, { name = "python-dotenv" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/52/6d/fffca34caecc4a3f97bda81b2098da5e8ab7efc9a66e819074a11955d87e/pydantic_settings-2.13.1.tar.gz", hash = "sha256:b4c11847b15237fb0171e1462bf540e294affb9b86db4d9aa5c01730bdbe4025", size = 223826, upload-time = "2026-02-19T13:45:08.055Z" } +sdist = { url = "https://files.pythonhosted.org/packages/07/60/1d1e59c9c90d54591469ada7d268251f71c24bdb765f1a8a832cee8c6653/pydantic_settings-2.14.1.tar.gz", hash = "sha256:e874d3bec7e787b0c9958277956ed9b4dd5de6a80e162188fdaff7c5e26fd5fa", size = 235551, upload-time = "2026-05-08T13:40:06.542Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/00/4b/ccc026168948fec4f7555b9164c724cf4125eac006e176541483d2c959be/pydantic_settings-2.13.1-py3-none-any.whl", hash = "sha256:d56fd801823dbeae7f0975e1f8c8e25c258eb75d278ea7abb5d9cebb01b56237", size = 58929, upload-time = "2026-02-19T13:45:06.034Z" }, + { url = "https://files.pythonhosted.org/packages/ae/8d/f1af3832f5e6eb13ba94ee809e72b8ecb5eef226d27ee0bef7d963d943c7/pydantic_settings-2.14.1-py3-none-any.whl", hash = "sha256:6e3c7edfd8277687cdc598f56e5cff0e9bfff0910a3749deaa8d4401c3a2b9de", size = 60964, upload-time = "2026-05-08T13:40:04.958Z" }, ] [[package]] @@ -2240,15 +2287,15 @@ wheels = [ [[package]] name = "pytest-asyncio" -version = "1.3.0" +version = "1.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/90/2c/8af215c0f776415f3590cac4f9086ccefd6fd463befeae41cd4d3f193e5a/pytest_asyncio-1.3.0.tar.gz", hash = "sha256:d7f52f36d231b80ee124cd216ffb19369aa168fc10095013c6b014a34d3ee9e5", size = 50087, upload-time = "2025-11-10T16:07:47.256Z" } +sdist = { url = "https://files.pythonhosted.org/packages/43/7c/d36d04db312ecf4298932ef77e6e4a9e8ad017906e24e34f0b0c361a2473/pytest_asyncio-1.4.0.tar.gz", hash = "sha256:c6c0d2259945122819f171a32ecea2c349ead889ee28176caaf492143424be42", size = 58514, upload-time = "2026-05-26T09:56:04.083Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/35/f8b19922b6a25bc0880171a2f1a003eaeb93657475193ab516fd87cac9da/pytest_asyncio-1.3.0-py3-none-any.whl", hash = "sha256:611e26147c7f77640e6d0a92a38ed17c3e9848063698d5c93d5aa7aa11cebff5", size = 15075, upload-time = "2025-11-10T16:07:45.537Z" }, + { url = "https://files.pythonhosted.org/packages/03/e2/08a497ef684b88559c9cc5f4ad53a37e7b99e727094a86d6ea32536d5d3c/pytest_asyncio-1.4.0-py3-none-any.whl", hash = "sha256:933ca923a23075a87fb7070c0ec272a6848489824d887c85c812670932835aa1", size = 16930, upload-time = "2026-05-26T09:56:02.576Z" }, ] [[package]] @@ -2312,11 +2359,11 @@ wheels = [ [[package]] name = "python-multipart" -version = "0.0.27" +version = "0.0.30" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/69/9b/f23807317a113dc36e74e75eb265a02dd1a4d9082abc3c1064acd22997c4/python_multipart-0.0.27.tar.gz", hash = "sha256:9870a6a8c5a20a5bf4f07c017bd1489006ff8836cff097b6933355ee2b49b602", size = 44043, upload-time = "2026-04-27T10:51:26.649Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4b/82/c8cd43a6e0719bf5a3b034f6726dd701f75829c08944c83d4b95d02ed0e8/python_multipart-0.0.30.tar.gz", hash = "sha256:0edfe0475c1f46ddd3ff7785a626f6118af32bdcf359bb21260367313bb32118", size = 46316, upload-time = "2026-05-31T19:24:55.198Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/99/78/4126abcbdbd3c559d43e0db7f7b9173fc6befe45d39a2856cc0b8ec2a5a6/python_multipart-0.0.27-py3-none-any.whl", hash = "sha256:6fccfad17a27334bd0193681b369f476eda3409f17381a2d65aa7df3f7275645", size = 29254, upload-time = "2026-04-27T10:51:24.997Z" }, + { url = "https://files.pythonhosted.org/packages/1c/fd/0318007beb234790993d3ec5afd051d1dbceb733e81e3afe2b981ece3f37/python_multipart-0.0.30-py3-none-any.whl", hash = "sha256:830964def8c90607ac5daa00514e3987815865713ade8d20febc9177ac0c3c5b", size = 29730, upload-time = "2026-05-31T19:24:53.814Z" }, ] [[package]] @@ -2577,27 +2624,27 @@ wheels = [ [[package]] name = "ruff" -version = "0.15.10" +version = "0.15.15" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e7/d9/aa3f7d59a10ef6b14fe3431706f854dbf03c5976be614a9796d36326810c/ruff-0.15.10.tar.gz", hash = "sha256:d1f86e67ebfdef88e00faefa1552b5e510e1d35f3be7d423dc7e84e63788c94e", size = 4631728, upload-time = "2026-04-09T14:06:09.884Z" } +sdist = { url = "https://files.pythonhosted.org/packages/84/6f/a76f7d96e5c962f5b69cee865e49c15c1116897c01990faa8a57edb62e7f/ruff-0.15.15.tar.gz", hash = "sha256:b8dff018130b46d8e5bf0f926ef6b60cf871d6d5ae45fc9334e09632daa741d6", size = 4706985, upload-time = "2026-05-28T14:16:57.784Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/eb/00/a1c2fdc9939b2c03691edbda290afcd297f1f389196172826b03d6b6a595/ruff-0.15.10-py3-none-linux_armv6l.whl", hash = "sha256:0744e31482f8f7d0d10a11fcbf897af272fefdfcb10f5af907b18c2813ff4d5f", size = 10563362, upload-time = "2026-04-09T14:06:21.189Z" }, - { url = "https://files.pythonhosted.org/packages/5c/15/006990029aea0bebe9d33c73c3e28c80c391ebdba408d1b08496f00d422d/ruff-0.15.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b1e7c16ea0ff5a53b7c2df52d947e685973049be1cdfe2b59a9c43601897b22e", size = 10951122, upload-time = "2026-04-09T14:06:02.236Z" }, - { url = "https://files.pythonhosted.org/packages/f2/c0/4ac978fe874d0618c7da647862afe697b281c2806f13ce904ad652fa87e4/ruff-0.15.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:93cc06a19e5155b4441dd72808fdf84290d84ad8a39ca3b0f994363ade4cebb1", size = 10314005, upload-time = "2026-04-09T14:06:00.026Z" }, - { url = "https://files.pythonhosted.org/packages/da/73/c209138a5c98c0d321266372fc4e33ad43d506d7e5dd817dd89b60a8548f/ruff-0.15.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:83e1dd04312997c99ea6965df66a14fb4f03ba978564574ffc68b0d61fd3989e", size = 10643450, upload-time = "2026-04-09T14:05:42.137Z" }, - { url = "https://files.pythonhosted.org/packages/ec/76/0deec355d8ec10709653635b1f90856735302cb8e149acfdf6f82a5feb70/ruff-0.15.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8154d43684e4333360fedd11aaa40b1b08a4e37d8ffa9d95fee6fa5b37b6fab1", size = 10379597, upload-time = "2026-04-09T14:05:49.984Z" }, - { url = "https://files.pythonhosted.org/packages/dc/be/86bba8fc8798c081e28a4b3bb6d143ccad3fd5f6f024f02002b8f08a9fa3/ruff-0.15.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ab88715f3a6deb6bde6c227f3a123410bec7b855c3ae331b4c006189e895cef", size = 11146645, upload-time = "2026-04-09T14:06:12.246Z" }, - { url = "https://files.pythonhosted.org/packages/a8/89/140025e65911b281c57be1d385ba1d932c2366ca88ae6663685aed8d4881/ruff-0.15.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a768ff5969b4f44c349d48edf4ab4f91eddb27fd9d77799598e130fb628aa158", size = 12030289, upload-time = "2026-04-09T14:06:04.776Z" }, - { url = "https://files.pythonhosted.org/packages/88/de/ddacca9545a5e01332567db01d44bd8cf725f2db3b3d61a80550b48308ea/ruff-0.15.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ee3ef42dab7078bda5ff6a1bcba8539e9857deb447132ad5566a038674540d0", size = 11496266, upload-time = "2026-04-09T14:05:55.485Z" }, - { url = "https://files.pythonhosted.org/packages/bc/bb/7ddb00a83760ff4a83c4e2fc231fd63937cc7317c10c82f583302e0f6586/ruff-0.15.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51cb8cc943e891ba99989dd92d61e29b1d231e14811db9be6440ecf25d5c1609", size = 11256418, upload-time = "2026-04-09T14:05:57.69Z" }, - { url = "https://files.pythonhosted.org/packages/dc/8d/55de0d35aacf6cd50b6ee91ee0f291672080021896543776f4170fc5c454/ruff-0.15.10-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:e59c9bdc056a320fb9ea1700a8d591718b8faf78af065484e801258d3a76bc3f", size = 11288416, upload-time = "2026-04-09T14:05:44.695Z" }, - { url = "https://files.pythonhosted.org/packages/68/cf/9438b1a27426ec46a80e0a718093c7f958ef72f43eb3111862949ead3cc1/ruff-0.15.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:136c00ca2f47b0018b073f28cb5c1506642a830ea941a60354b0e8bc8076b151", size = 10621053, upload-time = "2026-04-09T14:05:52.782Z" }, - { url = "https://files.pythonhosted.org/packages/4c/50/e29be6e2c135e9cd4cb15fbade49d6a2717e009dff3766dd080fcb82e251/ruff-0.15.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8b80a2f3c9c8a950d6237f2ca12b206bccff626139be9fa005f14feb881a1ae8", size = 10378302, upload-time = "2026-04-09T14:06:14.361Z" }, - { url = "https://files.pythonhosted.org/packages/18/2f/e0b36a6f99c51bb89f3a30239bc7bf97e87a37ae80aa2d6542d6e5150364/ruff-0.15.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:e3e53c588164dc025b671c9df2462429d60357ea91af7e92e9d56c565a9f1b07", size = 10850074, upload-time = "2026-04-09T14:06:16.581Z" }, - { url = "https://files.pythonhosted.org/packages/11/08/874da392558ce087a0f9b709dc6ec0d60cbc694c1c772dab8d5f31efe8cb/ruff-0.15.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:b0c52744cf9f143a393e284125d2576140b68264a93c6716464e129a3e9adb48", size = 11358051, upload-time = "2026-04-09T14:06:18.948Z" }, - { url = "https://files.pythonhosted.org/packages/e4/46/602938f030adfa043e67112b73821024dc79f3ab4df5474c25fa4c1d2d14/ruff-0.15.10-py3-none-win32.whl", hash = "sha256:d4272e87e801e9a27a2e8df7b21011c909d9ddd82f4f3281d269b6ba19789ca5", size = 10588964, upload-time = "2026-04-09T14:06:07.14Z" }, - { url = "https://files.pythonhosted.org/packages/25/b6/261225b875d7a13b33a6d02508c39c28450b2041bb01d0f7f1a83d569512/ruff-0.15.10-py3-none-win_amd64.whl", hash = "sha256:28cb32d53203242d403d819fd6983152489b12e4a3ae44993543d6fe62ab42ed", size = 11745044, upload-time = "2026-04-09T14:05:39.473Z" }, - { url = "https://files.pythonhosted.org/packages/58/ed/dea90a65b7d9e69888890fb14c90d7f51bf0c1e82ad800aeb0160e4bacfd/ruff-0.15.10-py3-none-win_arm64.whl", hash = "sha256:601d1610a9e1f1c2165a4f561eeaa2e2ea1e97f3287c5aa258d3dab8b57c6188", size = 11035607, upload-time = "2026-04-09T14:05:47.593Z" }, + { url = "https://files.pythonhosted.org/packages/fa/9d/3a45c05b8ab04b4705989de70a79008e27c8003296a0feaee9edc18dd7e9/ruff-0.15.15-py3-none-linux_armv6l.whl", hash = "sha256:cf93e5388f412e1b108b1f8b34a6e036b70fe8aff89393befad96fe48670311b", size = 10710652, upload-time = "2026-05-28T14:16:06.701Z" }, + { url = "https://files.pythonhosted.org/packages/05/66/da974431624bf3b49f6ee1f9543c02d929ff1cba78b0d5a79c38cf21f744/ruff-0.15.15-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ac5a646d1f6a7dadd5d50842dae2c1f9862ac887ef5d1b1375e02def791fde6e", size = 11096615, upload-time = "2026-05-28T14:16:23.313Z" }, + { url = "https://files.pythonhosted.org/packages/8c/09/7443452e5d290230a712103f2fdceeef7184f3ec99a2bd01c8be78aaceb5/ruff-0.15.15-py3-none-macosx_11_0_arm64.whl", hash = "sha256:77d955a431430c66f72dd94e379ad38a16daea3d25094872ac4edf9e797be530", size = 10436683, upload-time = "2026-05-28T14:16:40.974Z" }, + { url = "https://files.pythonhosted.org/packages/53/01/d330c26a57fa4f3943a14424904027428315b700fe4d14a84bb123a649e5/ruff-0.15.15-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7614ee79c69788cf6cedd568069ade9cecc22a1ad20494efe8d0c9ebb4b622d4", size = 10769064, upload-time = "2026-05-28T14:16:28.905Z" }, + { url = "https://files.pythonhosted.org/packages/1d/85/cc8770f8bdff541b1da8392d1634141fe4a0e3f4ee596605959b7906c27f/ruff-0.15.15-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3cdb1679e06a1f6b47bc384714ae96f6e2fb65ca441eb78c43d2ca554176ce1f", size = 10511987, upload-time = "2026-05-28T14:16:43.732Z" }, + { url = "https://files.pythonhosted.org/packages/7c/29/8c190c1472b63013583ba391f3342036e02010544c1270455ed8e519bdf3/ruff-0.15.15-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2728b93d7b23a603ea2c0ac6eb73d760bd38ec9de35f35fb41e18f7a3fee7622", size = 11275100, upload-time = "2026-05-28T14:16:55.244Z" }, + { url = "https://files.pythonhosted.org/packages/9f/6b/7e145ce2cc8e63d6834eca03d83a0e18d121def5c69f91b4cf4011ed4879/ruff-0.15.15-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be582fcc0db438902c7792b08d6ddf6c9b9e21addaa10092c2c741cfb09e5a45", size = 12176903, upload-time = "2026-05-28T14:16:14.368Z" }, + { url = "https://files.pythonhosted.org/packages/80/a3/d5974637f68e451f7fadf015cf3101d1cd7d8ba5027cffe0b9e3826ebe6b/ruff-0.15.15-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7aa77465b8ecaf1a27bea098d696f7fed5e1eccbd10b321b682d6de586ae5627", size = 11404550, upload-time = "2026-05-28T14:16:20.138Z" }, + { url = "https://files.pythonhosted.org/packages/fe/1c/e6e5e568f22be4fb05d6244234aba384c06b451252453b821e1a529263cf/ruff-0.15.15-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48decfa11d740de4889de623be1463308346312f2409a56e24aa280c86162dc4", size = 11382027, upload-time = "2026-05-28T14:16:46.615Z" }, + { url = "https://files.pythonhosted.org/packages/1d/01/170921b49fcd2e8858825593f91cf7146c3e40a5c3e6df763e4bb0484dde/ruff-0.15.15-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:a5015088452ca0081387063649ec67f06d3d1d6b8b936a1f836b5e9657ecd48c", size = 11366041, upload-time = "2026-05-28T14:16:26.247Z" }, + { url = "https://files.pythonhosted.org/packages/87/54/a7bad711d7de93254e15e06a4c375b89a03d18de45d3e5dcc86a4472fb1a/ruff-0.15.15-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:f5294aab6356c81600fcdea3a62bb1b924dfd5e91767c12318d3f68f86af57cd", size = 10741795, upload-time = "2026-05-28T14:16:17.11Z" }, + { url = "https://files.pythonhosted.org/packages/c9/31/38c075963668f8b41c6914ee0f6f318727fbe30ab9145cb29e6df464c5fa/ruff-0.15.15-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:db5bd4d802415cca656dc1616070b725952d6ae95eb5d4831e49fbd94a38f75f", size = 10511117, upload-time = "2026-05-28T14:16:31.767Z" }, + { url = "https://files.pythonhosted.org/packages/9d/96/6ff689e1f7e375d1d97075eca022f74c2bab59554a432fe4d2e6f091986a/ruff-0.15.15-py3-none-musllinux_1_2_i686.whl", hash = "sha256:587a6278ed42059191c1a466e490bd7930fb50bd2e255398bc29616c895a61cb", size = 10994867, upload-time = "2026-05-28T14:16:35.149Z" }, + { url = "https://files.pythonhosted.org/packages/c3/c2/5dce0ab9f92a8d534fa62b9bf9caca3eddb8c1a81b616f5e195ada4f0d6e/ruff-0.15.15-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:df0c1c084f5f4be9812f61518a45c440d3c30d69ce4bf6c5270e66d38338f02a", size = 11482101, upload-time = "2026-05-28T14:16:49.598Z" }, + { url = "https://files.pythonhosted.org/packages/b1/c0/1003b60edd697c649faf61f1a34094b1abb38fb3d1181e3f895781250a08/ruff-0.15.15-py3-none-win32.whl", hash = "sha256:29428ea79694afbe756d45fd59b36f22b6b020dc0443cf7de0173046236964b9", size = 10716774, upload-time = "2026-05-28T14:16:52.337Z" }, + { url = "https://files.pythonhosted.org/packages/02/a8/1269eddd6945a06c23f055ef7848886e37cf9d6a8bebb386a3115f01470c/ruff-0.15.15-py3-none-win_amd64.whl", hash = "sha256:8df0323902e15e24bc4bf246da830573d3cf3352bd0b9a164eab335d111ff4a4", size = 11868463, upload-time = "2026-05-28T14:16:11.333Z" }, + { url = "https://files.pythonhosted.org/packages/4e/b2/920464c907b191e37469d477a1aa8bc048b8f36c4c1610dfa4ab87b39e18/ruff-0.15.15-py3-none-win_arm64.whl", hash = "sha256:3c8ceca6792f38196b8f589bc92eccd03eef286602da92e5dc05cc42ef6441b7", size = 11138498, upload-time = "2026-05-28T14:16:38.425Z" }, ] [[package]] @@ -2872,28 +2919,29 @@ wheels = [ [[package]] name = "tokenizers" -version = "0.22.2" +version = "0.23.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "huggingface-hub" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/73/6f/f80cfef4a312e1fb34baf7d85c72d4411afde10978d4657f8cdd811d3ccc/tokenizers-0.22.2.tar.gz", hash = "sha256:473b83b915e547aa366d1eee11806deaf419e17be16310ac0a14077f1e28f917", size = 372115, upload-time = "2026-01-05T10:45:15.988Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c1/60/21f715d9faba5f5407ff759472ade058ec4a507ad62bcea47cb847239a73/tokenizers-0.23.1.tar.gz", hash = "sha256:1feeeadf865a7915adc25445dea30e9933e593c31bb96c277cee36de227c8bfa", size = 365748, upload-time = "2026-04-27T14:43:25.606Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/92/97/5dbfabf04c7e348e655e907ed27913e03db0923abb5dfdd120d7b25630e1/tokenizers-0.22.2-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:544dd704ae7238755d790de45ba8da072e9af3eea688f698b137915ae959281c", size = 3100275, upload-time = "2026-01-05T10:41:02.158Z" }, - { url = "https://files.pythonhosted.org/packages/2e/47/174dca0502ef88b28f1c9e06b73ce33500eedfac7a7692108aec220464e7/tokenizers-0.22.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:1e418a55456beedca4621dbab65a318981467a2b188e982a23e117f115ce5001", size = 2981472, upload-time = "2026-01-05T10:41:00.276Z" }, - { url = "https://files.pythonhosted.org/packages/d6/84/7990e799f1309a8b87af6b948f31edaa12a3ed22d11b352eaf4f4b2e5753/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2249487018adec45d6e3554c71d46eb39fa8ea67156c640f7513eb26f318cec7", size = 3290736, upload-time = "2026-01-05T10:40:32.165Z" }, - { url = "https://files.pythonhosted.org/packages/78/59/09d0d9ba94dcd5f4f1368d4858d24546b4bdc0231c2354aa31d6199f0399/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25b85325d0815e86e0bac263506dd114578953b7b53d7de09a6485e4a160a7dd", size = 3168835, upload-time = "2026-01-05T10:40:38.847Z" }, - { url = "https://files.pythonhosted.org/packages/47/50/b3ebb4243e7160bda8d34b731e54dd8ab8b133e50775872e7a434e524c28/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfb88f22a209ff7b40a576d5324bf8286b519d7358663db21d6246fb17eea2d5", size = 3521673, upload-time = "2026-01-05T10:40:56.614Z" }, - { url = "https://files.pythonhosted.org/packages/e0/fa/89f4cb9e08df770b57adb96f8cbb7e22695a4cb6c2bd5f0c4f0ebcf33b66/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c774b1276f71e1ef716e5486f21e76333464f47bece56bbd554485982a9e03e", size = 3724818, upload-time = "2026-01-05T10:40:44.507Z" }, - { url = "https://files.pythonhosted.org/packages/64/04/ca2363f0bfbe3b3d36e95bf67e56a4c88c8e3362b658e616d1ac185d47f2/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df6c4265b289083bf710dff49bc51ef252f9d5be33a45ee2bed151114a56207b", size = 3379195, upload-time = "2026-01-05T10:40:51.139Z" }, - { url = "https://files.pythonhosted.org/packages/2e/76/932be4b50ef6ccedf9d3c6639b056a967a86258c6d9200643f01269211ca/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:369cc9fc8cc10cb24143873a0d95438bb8ee257bb80c71989e3ee290e8d72c67", size = 3274982, upload-time = "2026-01-05T10:40:58.331Z" }, - { url = "https://files.pythonhosted.org/packages/1d/28/5f9f5a4cc211b69e89420980e483831bcc29dade307955cc9dc858a40f01/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:29c30b83d8dcd061078b05ae0cb94d3c710555fbb44861139f9f83dcca3dc3e4", size = 9478245, upload-time = "2026-01-05T10:41:04.053Z" }, - { url = "https://files.pythonhosted.org/packages/6c/fb/66e2da4704d6aadebf8cb39f1d6d1957df667ab24cff2326b77cda0dcb85/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:37ae80a28c1d3265bb1f22464c856bd23c02a05bb211e56d0c5301a435be6c1a", size = 9560069, upload-time = "2026-01-05T10:45:10.673Z" }, - { url = "https://files.pythonhosted.org/packages/16/04/fed398b05caa87ce9b1a1bb5166645e38196081b225059a6edaff6440fac/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:791135ee325f2336f498590eb2f11dc5c295232f288e75c99a36c5dbce63088a", size = 9899263, upload-time = "2026-01-05T10:45:12.559Z" }, - { url = "https://files.pythonhosted.org/packages/05/a1/d62dfe7376beaaf1394917e0f8e93ee5f67fea8fcf4107501db35996586b/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:38337540fbbddff8e999d59970f3c6f35a82de10053206a7562f1ea02d046fa5", size = 10033429, upload-time = "2026-01-05T10:45:14.333Z" }, - { url = "https://files.pythonhosted.org/packages/fd/18/a545c4ea42af3df6effd7d13d250ba77a0a86fb20393143bbb9a92e434d4/tokenizers-0.22.2-cp39-abi3-win32.whl", hash = "sha256:a6bf3f88c554a2b653af81f3204491c818ae2ac6fbc09e76ef4773351292bc92", size = 2502363, upload-time = "2026-01-05T10:45:20.593Z" }, - { url = "https://files.pythonhosted.org/packages/65/71/0670843133a43d43070abeb1949abfdef12a86d490bea9cd9e18e37c5ff7/tokenizers-0.22.2-cp39-abi3-win_amd64.whl", hash = "sha256:c9ea31edff2968b44a88f97d784c2f16dc0729b8b143ed004699ebca91f05c48", size = 2747786, upload-time = "2026-01-05T10:45:18.411Z" }, - { url = "https://files.pythonhosted.org/packages/72/f4/0de46cfa12cdcbcd464cc59fde36912af405696f687e53a091fb432f694c/tokenizers-0.22.2-cp39-abi3-win_arm64.whl", hash = "sha256:9ce725d22864a1e965217204946f830c37876eee3b2ba6fc6255e8e903d5fcbc", size = 2612133, upload-time = "2026-01-05T10:45:17.232Z" }, + { url = "https://files.pythonhosted.org/packages/87/39/b87a87d5bb9470610b80a2d31df42fcffeaf35118b8b97952b2aff598cc7/tokenizers-0.23.1-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:e03d6ffcbe0d56ee9c1ccd070e70a13fa750727c0277e138152acbc0252c2224", size = 3146732, upload-time = "2026-04-27T14:43:15.427Z" }, + { url = "https://files.pythonhosted.org/packages/e2/6a/068ed9f6e444c9d7e9d55ce134181325700f3d7f30410721bdc8f848d727/tokenizers-0.23.1-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:e0948bbb1ac1d7cdfc9fb6d62c596e3b7550036ad60ecd654a66ad273326324e", size = 3054954, upload-time = "2026-04-27T14:43:13.745Z" }, + { url = "https://files.pythonhosted.org/packages/6c/36/e006edf031154cba92b8416057d92c3abe3635e4c4b0aa0b5b9bb39dde70/tokenizers-0.23.1-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bf13402aff9bc533c89cb849ec3b412dc3fbeacc9744840e423d7bf3f7dc0e3", size = 3374081, upload-time = "2026-04-27T14:43:01.241Z" }, + { url = "https://files.pythonhosted.org/packages/a2/ef/7735d226f9c7f874a6bee5e3f27fb25ecabdf207d37b8cf45286d0795893/tokenizers-0.23.1-cp310-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f836ca703b89ae07919a309f9651f7a88fd5a33d5f718ba5ad0870ec0256bad6", size = 3247641, upload-time = "2026-04-27T14:43:03.856Z" }, + { url = "https://files.pythonhosted.org/packages/b9/d9/24827036f6e21297bfffda0768e58eb6096a4f411e932964a01707857931/tokenizers-0.23.1-cp310-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae848657742035523fdf261773630cb819a26995fcd3d9ecae0c1daf6e5a4959", size = 3585624, upload-time = "2026-04-27T14:43:10.664Z" }, + { url = "https://files.pythonhosted.org/packages/0c/9a/22f3582b3a4f49358293a5206e25317621ee4526bfe9cdaa0f07a12e770e/tokenizers-0.23.1-cp310-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:53b09e85775d5187941e7bab30e941b4134ab4a7dd8c68e783d231fb7ca27c51", size = 3844062, upload-time = "2026-04-27T14:43:05.643Z" }, + { url = "https://files.pythonhosted.org/packages/7e/65/b8f8814eef95800f20721384136d9a1d22241d50b2874357cb70542c392f/tokenizers-0.23.1-cp310-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea5a0ce170074329faaa8ea3f6400ecde604b6678192688533af80980daae71a", size = 3460098, upload-time = "2026-04-27T14:43:08.854Z" }, + { url = "https://files.pythonhosted.org/packages/0d/d5/1353e5f677ec27c2494fb6a6725e82d56c985f53e90ec511369e7e4f02c6/tokenizers-0.23.1-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5075b405006415ea148a992d093699c66eb01952bf59f4d5727089a98bda45a4", size = 3346235, upload-time = "2026-04-27T14:43:12.377Z" }, + { url = "https://files.pythonhosted.org/packages/71/89/39b6b8fc073fb6d413d0147aa333dc7eff7be65639ac9d19930a0b21bf33/tokenizers-0.23.1-cp310-abi3-manylinux_2_31_riscv64.whl", hash = "sha256:56f3a77de629917652f876294dc9fe6bad4a0c43bc229dc72e59bb23a0f4729a", size = 3426398, upload-time = "2026-04-27T14:43:07.264Z" }, + { url = "https://files.pythonhosted.org/packages/0f/80/127c854da64827e5b79264ce524993a90dddcb320e5cd42412c5c02f9e8a/tokenizers-0.23.1-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9d10a6d957ef01896dc274e890eee27d41bd0e74ef31e60616f0fc311345184e", size = 9823279, upload-time = "2026-04-27T14:43:17.222Z" }, + { url = "https://files.pythonhosted.org/packages/fe/ba/44c2502feb1a058f096ddfb4e0996ef3225a01a388e1a9b094e91689fe93/tokenizers-0.23.1-cp310-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:1974288a609c343774f1b897c8b482c791ab17b75ab5c8c2b1737565c1d82288", size = 9644986, upload-time = "2026-04-27T14:43:19.45Z" }, + { url = "https://files.pythonhosted.org/packages/9e/c1/464019a9fb059870bfe4eebb4ba12208f3042035e258bf5e782906bd3847/tokenizers-0.23.1-cp310-abi3-musllinux_1_2_i686.whl", hash = "sha256:120468fb4c24faf0543c835a4fabafa4deb3f20a035c9b6e83d0b553a97615d4", size = 9976181, upload-time = "2026-04-27T14:43:21.463Z" }, + { url = "https://files.pythonhosted.org/packages/79/94/3ac1432bda31626071e9b6a12709b97ae05131c804b94c8f3ac622c5da32/tokenizers-0.23.1-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e3d8f40ea6268047de7046906326abed5134f27d4e8447b23763afe5808c8a96", size = 10113853, upload-time = "2026-04-27T14:43:23.617Z" }, + { url = "https://files.pythonhosted.org/packages/6a/dd/631b21433c771b1382535326f0eca80b9c9cee2e64961dd993bc9ac4669e/tokenizers-0.23.1-cp310-abi3-win32.whl", hash = "sha256:93120a930b919416da7cd10a2f606ac9919cc69cacae7980fa2140e277660948", size = 2536263, upload-time = "2026-04-27T14:43:29.888Z" }, + { url = "https://files.pythonhosted.org/packages/97/c9/2553f72aaf65a2797d4229e37fa7fbe38ffbf3e32912d31bdd78b3323e59/tokenizers-0.23.1-cp310-abi3-win_amd64.whl", hash = "sha256:e7bfaf995c1bdbbd21d13539decb6650967013759318627d85daeb7881af16b7", size = 2798223, upload-time = "2026-04-27T14:43:28.51Z" }, + { url = "https://files.pythonhosted.org/packages/cd/2b/2be299bab55fc595e3d38567edb1a87f86e594842968fa9515a07bdcf422/tokenizers-0.23.1-cp310-abi3-win_arm64.whl", hash = "sha256:a26197957d8e4425dfba746315f3c425ea00cfa8367c5fbc4ec73447893dcea9", size = 2664127, upload-time = "2026-04-27T14:43:26.949Z" }, ] [[package]] @@ -2974,41 +3022,41 @@ wheels = [ [[package]] name = "types-pyyaml" -version = "6.0.12.20260408" +version = "6.0.12.20260518" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/74/73/b759b1e413c31034cc01ecdfb96b38115d0ab4db55a752a3929f0cd449fd/types_pyyaml-6.0.12.20260408.tar.gz", hash = "sha256:92a73f2b8d7f39ef392a38131f76b970f8c66e4c42b3125ae872b7c93b556307", size = 17735, upload-time = "2026-04-08T04:30:50.974Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b8/83/4a1afc3fbfcf5b8d46fc390cd95ed6b0dc9010a265f4e9f46314efffa37a/types_pyyaml-6.0.12.20260518.tar.gz", hash = "sha256:d917f83fb38462550338c1297faedd860b3ec83912b96b1e3d73255f7473e466", size = 17850, upload-time = "2026-05-18T06:01:58.675Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1c/f0/c391068b86abb708882c6d75a08cd7d25b2c7227dab527b3a3685a3c635b/types_pyyaml-6.0.12.20260408-py3-none-any.whl", hash = "sha256:fbc42037d12159d9c801ebfcc79ebd28335a7c13b08a4cfbc6916df78fee9384", size = 20339, upload-time = "2026-04-08T04:30:50.113Z" }, + { url = "https://files.pythonhosted.org/packages/06/a2/c01db32be2ae7d6a1689972f3c492b149ee4e164b12fdfd9f64b50888215/types_pyyaml-6.0.12.20260518-py3-none-any.whl", hash = "sha256:d2150f75a231c9fe9c7463bd29487d93e60bac90400287351384bc2284eba7cd", size = 20312, upload-time = "2026-05-18T06:01:57.368Z" }, ] [[package]] name = "types-requests" -version = "2.33.0.20260408" +version = "2.33.0.20260518" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/69/6a/749dc53a54a3f35842c1f8197b3ca6b54af6d7458a1bfc75f6629b6da666/types_requests-2.33.0.20260408.tar.gz", hash = "sha256:95b9a86376807a216b2fb412b47617b202091c3ea7c078f47cc358d5528ccb7b", size = 23882, upload-time = "2026-04-08T04:34:49.33Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/01/c5a19253fe1ac159159ddf9a3a07cec8bb5e486ec4d9002ad2821da0e5d2/types_requests-2.33.0.20260518.tar.gz", hash = "sha256:df7bd3bfe0ca8402dfb841e7d9be714bb5578203283d66d7dc4ef69343449a5e", size = 24752, upload-time = "2026-05-18T06:07:37.966Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/90/b8/78fd6c037de4788c040fdd323b3369804400351b7827473920f6c1d03c10/types_requests-2.33.0.20260408-py3-none-any.whl", hash = "sha256:81f31d5ea4acb39f03be7bc8bed569ba6d5a9c5d97e89f45ac43d819b68ca50f", size = 20739, upload-time = "2026-04-08T04:34:48.325Z" }, + { url = "https://files.pythonhosted.org/packages/1c/bc/b139710a3b6018f7fb2b9508b35c8af564e61bf2bf4fa619d088f3e16f85/types_requests-2.33.0.20260518-py3-none-any.whl", hash = "sha256:626d697d1adaaff76e2044dc8c5c051d8f21abc157bdfe204a75558076fe0bf0", size = 21391, upload-time = "2026-05-18T06:07:37.044Z" }, ] [[package]] name = "types-setuptools" -version = "82.0.0.20260408" +version = "82.0.0.20260518" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c3/12/3464b410c50420dd4674fa5fe9d3880711c1dbe1a06f5fe4960ee9067b9e/types_setuptools-82.0.0.20260408.tar.gz", hash = "sha256:036c68caf7e672a699f5ebbf914708d40644c14e05298bc49f7272be91cf43d3", size = 44861, upload-time = "2026-04-08T04:29:33.292Z" } +sdist = { url = "https://files.pythonhosted.org/packages/38/bc/73c2c27e047e42f114ac50fb3bdef986c56cbdb68096f8690eeafb839a93/types_setuptools-82.0.0.20260518.tar.gz", hash = "sha256:3b743cfe63d0981ea4c15b90710fc1ed41e3464a537d51e705be514e891c1d07", size = 44999, upload-time = "2026-05-18T06:02:55.642Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/e1/46a4fc3ef03aabf5d18bac9df5cf37c6b02c3bddf3e05c3533f4b4588331/types_setuptools-82.0.0.20260408-py3-none-any.whl", hash = "sha256:ece0a215cdfa6463a65fd6f68bd940f39e455729300ddfe61cab1147ed1d2462", size = 68428, upload-time = "2026-04-08T04:29:32.175Z" }, + { url = "https://files.pythonhosted.org/packages/32/8f/d5e2d493f09a7a98c95619edda1cb37cee377626c0a869d53274c26f2858/types_setuptools-82.0.0.20260518-py3-none-any.whl", hash = "sha256:31c04a62b57a653a5021caf191be0f10f70df890f813b51f02bab3969d300f20", size = 68444, upload-time = "2026-05-18T06:02:54.582Z" }, ] [[package]] name = "types-simplejson" -version = "3.20.0.20260408" +version = "3.20.0.20260518" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9f/36/e319fd0f6d906dbf7c2c03eef17db77ef461197a75b253fccd9c7c695d3e/types_simplejson-3.20.0.20260408.tar.gz", hash = "sha256:0b0e1bf61e70f81dfe6ef4c2b9c02e39403848c0652df334e7a430c3a26c06b3", size = 10693, upload-time = "2026-04-08T04:28:07.8Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a5/e4/f8ecdc3529688ad058b0496f67a1bdfbd2576c3669e1c478b6347b12cb7e/types_simplejson-3.20.0.20260518.tar.gz", hash = "sha256:298c5b2fc2df3eb128d2077abd5bd1f5b0419dcf72fdf0551b2af2f03b1ff2b3", size = 10777, upload-time = "2026-05-18T06:04:39.57Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/22/c0/01a5a4c3948c2269cf9d727e5e66a8b404e03beb4f9522680a3f71097011/types_simplejson-3.20.0.20260408-py3-none-any.whl", hash = "sha256:f9e542199cb159ed34ad54b6ceb3dc9af890c256b810ad1bd7c69c61db7d2236", size = 10415, upload-time = "2026-04-08T04:28:06.984Z" }, + { url = "https://files.pythonhosted.org/packages/a6/41/16937326596bec6283a51cc901c701a964019569b861ac5f88128bc98b43/types_simplejson-3.20.0.20260518-py3-none-any.whl", hash = "sha256:f4792dbc1ab049fcaee5d99ca950bd6468ea462fe79af25fe8ca08b6bee313ac", size = 10421, upload-time = "2026-05-18T06:04:38.706Z" }, ] [[package]] @@ -3052,15 +3100,15 @@ wheels = [ [[package]] name = "uvicorn" -version = "0.44.0" +version = "0.48.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "h11" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5e/da/6eee1ff8b6cbeed47eeb5229749168e81eb4b7b999a1a15a7176e51410c9/uvicorn-0.44.0.tar.gz", hash = "sha256:6c942071b68f07e178264b9152f1f16dfac5da85880c4ce06366a96d70d4f31e", size = 86947, upload-time = "2026-04-06T09:23:22.826Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e6/bf/f6544ba992ddb9a6077343a576f9844f7f8f06ab819aefd00206e9255f18/uvicorn-0.48.0.tar.gz", hash = "sha256:a5504207195d08c2511bf9125ede5ac4a4b71725d519e758d01dcf0bc2d31c37", size = 91074, upload-time = "2026-05-24T12:08:41.925Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/23/a5bbd9600dd607411fa644c06ff4951bec3a4d82c4b852374024359c19c0/uvicorn-0.44.0-py3-none-any.whl", hash = "sha256:ce937c99a2cc70279556967274414c087888e8cec9f9c94644dfca11bd3ced89", size = 69425, upload-time = "2026-04-06T09:23:21.524Z" }, + { url = "https://files.pythonhosted.org/packages/01/be/72532be3da7acc5fdfbccdb95215cd04f995a0886532a5b423f929cda4cc/uvicorn-0.48.0-py3-none-any.whl", hash = "sha256:48097851328b87ec36117d3d575234519eb58c2b22d79666e9bbc6c49a761dad", size = 71410, upload-time = "2026-05-24T12:08:40.258Z" }, ] [package.optional-dependencies] diff --git a/mise.lock b/mise.lock index 7760585258..fa63fda5e0 100644 --- a/mise.lock +++ b/mise.lock @@ -1,30 +1,30 @@ # @generated - this file is auto-generated by `mise lock` https://mise.en.dev/dev-tools/mise-lock.html [[tools."aqua:flutter/flutter"]] -version = "3.44.0" +version = "3.44.1" backend = "aqua:flutter/flutter" [tools."aqua:flutter/flutter"."platforms.linux-arm64"] -url = "https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_3.44.0-stable.tar.xz" +url = "https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_3.44.1-stable.tar.xz" [tools."aqua:flutter/flutter"."platforms.linux-arm64-musl"] -url = "https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_3.44.0-stable.tar.xz" +url = "https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_3.44.1-stable.tar.xz" [tools."aqua:flutter/flutter"."platforms.linux-x64"] -url = "https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_3.44.0-stable.tar.xz" +url = "https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_3.44.1-stable.tar.xz" [tools."aqua:flutter/flutter"."platforms.linux-x64-musl"] -url = "https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_3.44.0-stable.tar.xz" +url = "https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_3.44.1-stable.tar.xz" [tools."aqua:flutter/flutter"."platforms.macos-arm64"] -checksum = "blake3:fb03aa5d9790205c948922ec3f0751c16e4575b09d6ae9dd4fbeb664a69f0e00" -url = "https://storage.googleapis.com/flutter_infra_release/releases/stable/macos/flutter_macos_arm64_3.44.0-stable.zip" +checksum = "blake3:15069c982a30ca0189a83edb5627b69d91485ad94fb74d2de8585b43364e9e8e" +url = "https://storage.googleapis.com/flutter_infra_release/releases/stable/macos/flutter_macos_arm64_3.44.1-stable.zip" [tools."aqua:flutter/flutter"."platforms.macos-x64"] -url = "https://storage.googleapis.com/flutter_infra_release/releases/stable/macos/flutter_macos_3.44.0-stable.zip" +url = "https://storage.googleapis.com/flutter_infra_release/releases/stable/macos/flutter_macos_3.44.1-stable.zip" [tools."aqua:flutter/flutter"."platforms.windows-x64"] -url = "https://storage.googleapis.com/flutter_infra_release/releases/stable/windows/flutter_windows_3.44.0-stable.zip" +url = "https://storage.googleapis.com/flutter_infra_release/releases/stable/windows/flutter_windows_3.44.1-stable.zip" [[tools.flutter]] version = "3.41.9-stable" diff --git a/mise.toml b/mise.toml index 5659aacb6c..b501f15f0c 100644 --- a/mise.toml +++ b/mise.toml @@ -16,7 +16,7 @@ config_roots = [ [tools] node = "24.15.0" -"aqua:flutter/flutter" = "3.44.0" +"aqua:flutter/flutter" = "3.44.1" pnpm = "10.33.4" terragrunt = "1.0.3" opentofu = "1.11.6" @@ -54,8 +54,8 @@ lockfile = true [tasks.plugins] run = [ - "pnpm --filter @immich/plugin-sdk --filter @immich/plugin-core install --frozen-lockfile", - "pnpm --filter @immich/plugin-sdk --filter @immich/plugin-core build", + "pnpm --filter @immich/sdk --filter @immich/plugin-sdk --filter @immich/plugin-core install --frozen-lockfile", + "pnpm --filter @immich/sdk --filter @immich/plugin-sdk --filter @immich/plugin-core build", ] [tasks.open-api-typescript] @@ -108,7 +108,7 @@ depends = "//:plugins" dir = "docker" interactive = true env = { COMPOSE_BAKE = true } -run = "docker compose -f ./docker-compose.prod.yml up --remove-orphans" +run = "docker compose -f ./docker-compose.prod.yml up --build --remove-orphans" depends_post = "//:prod-down" [tasks.prod-scale] diff --git a/mobile/drift_schemas/main/drift_schema_v27.json b/mobile/drift_schemas/main/drift_schema_v27.json new file mode 100644 index 0000000000..4df6ef8389 --- /dev/null +++ b/mobile/drift_schemas/main/drift_schema_v27.json @@ -0,0 +1,3368 @@ +{ + "_meta": { + "description": "This file contains a serialized version of schema entities for drift.", + "version": "1.3.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_ms", + "getter_name": "durationMs", + "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": [ + { + "foreign_key": { + "to": { + "table": "user_entity", + "column": "id" + }, + "initially_deferred": false, + "on_update": null, + "on_delete": "cascade" + } + } + ] + }, + { + "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": "uploaded_at", + "getter_name": "uploadedAt", + "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": [] + }, + { + "name": "is_edited", + "getter_name": "isEdited", + "moor_type": "bool", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "CHECK (\"is_edited\" IN (0, 1))", + "dialectAwareDefaultConstraints": { + "sqlite": "CHECK (\"is_edited\" IN (0, 1))" + }, + "default_dart": "const CustomExpression('0')", + "default_client_dart": null, + "dsl_features": [] + } + ], + "is_virtual": false, + "without_rowid": true, + "constraints": [], + "strict": true, + "explicit_pk": [ + "id" + ] + } + }, + { + "id": 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": [ + { + "foreign_key": { + "to": { + "table": "user_entity", + "column": "id" + }, + "initially_deferred": false, + "on_update": null, + "on_delete": "cascade" + } + } + ] + }, + { + "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_ms", + "getter_name": "durationMs", + "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": [] + }, + { + "name": "i_cloud_id", + "getter_name": "iCloudId", + "moor_type": "string", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "adjustment_time", + "getter_name": "adjustmentTime", + "moor_type": "dateTime", + "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": "playback_style", + "getter_name": "playbackStyle", + "moor_type": "int", + "nullable": false, + "customConstraints": null, + "default_dart": "const CustomExpression('0')", + "default_client_dart": null, + "dsl_features": [], + "type_converter": { + "dart_expr": "const EnumIndexConverter(AssetPlaybackStyle.values)", + "dart_type_name": "AssetPlaybackStyle" + } + } + ], + "is_virtual": false, + "without_rowid": true, + "constraints": [], + "strict": true, + "explicit_pk": [ + "id" + ] + } + }, + { + "id": 4, + "references": [ + 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": "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": [ + { + "foreign_key": { + "to": { + "table": "remote_asset_entity", + "column": "id" + }, + "initially_deferred": false, + "on_update": null, + "on_delete": "setNull" + } + } + ] + }, + { + "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": [ + { + "foreign_key": { + "to": { + "table": "remote_album_entity", + "column": "id" + }, + "initially_deferred": false, + "on_update": null, + "on_delete": "setNull" + } + } + ] + }, + { + "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": [ + { + "foreign_key": { + "to": { + "table": "local_asset_entity", + "column": "id" + }, + "initially_deferred": false, + "on_update": null, + "on_delete": "cascade" + } + } + ] + }, + { + "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": [ + { + "foreign_key": { + "to": { + "table": "local_album_entity", + "column": "id" + }, + "initially_deferred": false, + "on_update": null, + "on_delete": "cascade" + } + } + ] + }, + { + "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": [ + 6 + ], + "type": "index", + "data": { + "on": 6, + "name": "idx_local_album_asset_album_asset", + "sql": "CREATE INDEX IF NOT EXISTS idx_local_album_asset_album_asset ON local_album_asset_entity (album_id, asset_id)", + "unique": false, + "columns": [] + } + }, + { + "id": 8, + "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": 9, + "references": [ + 3 + ], + "type": "index", + "data": { + "on": 3, + "name": "idx_local_asset_cloud_id", + "sql": "CREATE INDEX IF NOT EXISTS idx_local_asset_cloud_id ON local_asset_entity (i_cloud_id)", + "unique": false, + "columns": [] + } + }, + { + "id": 10, + "references": [ + 2 + ], + "type": "index", + "data": { + "on": 2, + "name": "idx_stack_primary_asset_id", + "sql": "CREATE INDEX IF NOT EXISTS idx_stack_primary_asset_id ON stack_entity (primary_asset_id)", + "unique": false, + "columns": [] + } + }, + { + "id": 11, + "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": 12, + "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": 13, + "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": 14, + "references": [ + 1 + ], + "type": "index", + "data": { + "on": 1, + "name": "idx_remote_asset_stack_id", + "sql": "CREATE INDEX IF NOT EXISTS idx_remote_asset_stack_id ON remote_asset_entity (stack_id)", + "unique": false, + "columns": [] + } + }, + { + "id": 15, + "references": [ + 1 + ], + "type": "index", + "data": { + "on": 1, + "name": "idx_remote_asset_owner_visibility_deleted_created", + "sql": "CREATE INDEX IF NOT EXISTS idx_remote_asset_owner_visibility_deleted_created\nON remote_asset_entity (owner_id, visibility, deleted_at, created_at DESC)\n", + "unique": false, + "columns": [] + } + }, + { + "id": 16, + "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": 17, + "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": [ + { + "foreign_key": { + "to": { + "table": "user_entity", + "column": "id" + }, + "initially_deferred": false, + "on_update": null, + "on_delete": "cascade" + } + } + ] + }, + { + "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": 18, + "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": [ + { + "foreign_key": { + "to": { + "table": "user_entity", + "column": "id" + }, + "initially_deferred": false, + "on_update": null, + "on_delete": "cascade" + } + } + ] + }, + { + "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": [ + { + "foreign_key": { + "to": { + "table": "user_entity", + "column": "id" + }, + "initially_deferred": false, + "on_update": null, + "on_delete": "cascade" + } + } + ] + }, + { + "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": 19, + "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": [ + { + "foreign_key": { + "to": { + "table": "remote_asset_entity", + "column": "id" + }, + "initially_deferred": false, + "on_update": null, + "on_delete": "cascade" + } + } + ] + }, + { + "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": 20, + "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": [ + { + "foreign_key": { + "to": { + "table": "remote_asset_entity", + "column": "id" + }, + "initially_deferred": false, + "on_update": null, + "on_delete": "cascade" + } + } + ] + }, + { + "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": [ + { + "foreign_key": { + "to": { + "table": "remote_album_entity", + "column": "id" + }, + "initially_deferred": false, + "on_update": null, + "on_delete": "cascade" + } + } + ] + } + ], + "is_virtual": false, + "without_rowid": true, + "constraints": [], + "strict": true, + "explicit_pk": [ + "asset_id", + "album_id" + ] + } + }, + { + "id": 21, + "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": [ + { + "foreign_key": { + "to": { + "table": "remote_album_entity", + "column": "id" + }, + "initially_deferred": false, + "on_update": null, + "on_delete": "cascade" + } + } + ] + }, + { + "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": [ + { + "foreign_key": { + "to": { + "table": "user_entity", + "column": "id" + }, + "initially_deferred": false, + "on_update": null, + "on_delete": "cascade" + } + } + ] + }, + { + "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": 22, + "references": [ + 1 + ], + "type": "table", + "data": { + "name": "remote_asset_cloud_id_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": [ + { + "foreign_key": { + "to": { + "table": "remote_asset_entity", + "column": "id" + }, + "initially_deferred": false, + "on_update": null, + "on_delete": "cascade" + } + } + ] + }, + { + "name": "cloud_id", + "getter_name": "cloudId", + "moor_type": "string", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "created_at", + "getter_name": "createdAt", + "moor_type": "dateTime", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "adjustment_time", + "getter_name": "adjustmentTime", + "moor_type": "dateTime", + "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": [] + } + ], + "is_virtual": false, + "without_rowid": true, + "constraints": [], + "strict": true, + "explicit_pk": [ + "asset_id" + ] + } + }, + { + "id": 23, + "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": [ + { + "foreign_key": { + "to": { + "table": "user_entity", + "column": "id" + }, + "initially_deferred": false, + "on_update": null, + "on_delete": "cascade" + } + } + ] + }, + { + "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": 24, + "references": [ + 1, + 23 + ], + "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": [ + { + "foreign_key": { + "to": { + "table": "remote_asset_entity", + "column": "id" + }, + "initially_deferred": false, + "on_update": null, + "on_delete": "cascade" + } + } + ] + }, + { + "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": [ + { + "foreign_key": { + "to": { + "table": "memory_entity", + "column": "id" + }, + "initially_deferred": false, + "on_update": null, + "on_delete": "cascade" + } + } + ] + } + ], + "is_virtual": false, + "without_rowid": true, + "constraints": [], + "strict": true, + "explicit_pk": [ + "asset_id", + "memory_id" + ] + } + }, + { + "id": 25, + "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": [ + { + "foreign_key": { + "to": { + "table": "user_entity", + "column": "id" + }, + "initially_deferred": false, + "on_update": null, + "on_delete": "cascade" + } + } + ] + }, + { + "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": 26, + "references": [ + 1, + 25 + ], + "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": [ + { + "foreign_key": { + "to": { + "table": "remote_asset_entity", + "column": "id" + }, + "initially_deferred": false, + "on_update": null, + "on_delete": "cascade" + } + } + ] + }, + { + "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": [ + { + "foreign_key": { + "to": { + "table": "person_entity", + "column": "id" + }, + "initially_deferred": false, + "on_update": null, + "on_delete": "setNull" + } + } + ] + }, + { + "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": [] + }, + { + "name": "is_visible", + "getter_name": "isVisible", + "moor_type": "bool", + "nullable": false, + "customConstraints": null, + "defaultConstraints": "CHECK (\"is_visible\" IN (0, 1))", + "dialectAwareDefaultConstraints": { + "sqlite": "CHECK (\"is_visible\" IN (0, 1))" + }, + "default_dart": "const CustomExpression('1')", + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "deleted_at", + "getter_name": "deletedAt", + "moor_type": "dateTime", + "nullable": true, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + } + ], + "is_virtual": false, + "without_rowid": true, + "constraints": [], + "strict": true, + "explicit_pk": [ + "id" + ] + } + }, + { + "id": 27, + "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": 28, + "references": [], + "type": "table", + "data": { + "name": "trashed_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_ms", + "getter_name": "durationMs", + "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": "album_id", + "getter_name": "albumId", + "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": [] + }, + { + "name": "source", + "getter_name": "source", + "moor_type": "int", + "nullable": false, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [], + "type_converter": { + "dart_expr": "const EnumIndexConverter(TrashOrigin.values)", + "dart_type_name": "TrashOrigin" + } + }, + { + "name": "playback_style", + "getter_name": "playbackStyle", + "moor_type": "int", + "nullable": false, + "customConstraints": null, + "default_dart": "const CustomExpression('0')", + "default_client_dart": null, + "dsl_features": [], + "type_converter": { + "dart_expr": "const EnumIndexConverter(AssetPlaybackStyle.values)", + "dart_type_name": "AssetPlaybackStyle" + } + } + ], + "is_virtual": false, + "without_rowid": true, + "constraints": [], + "strict": true, + "explicit_pk": [ + "id", + "album_id" + ] + } + }, + { + "id": 29, + "references": [ + 1 + ], + "type": "table", + "data": { + "name": "asset_edit_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": [ + { + "foreign_key": { + "to": { + "table": "remote_asset_entity", + "column": "id" + }, + "initially_deferred": false, + "on_update": null, + "on_delete": "cascade" + } + } + ] + }, + { + "name": "action", + "getter_name": "action", + "moor_type": "int", + "nullable": false, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [], + "type_converter": { + "dart_expr": "const EnumIndexConverter(AssetEditAction.values)", + "dart_type_name": "AssetEditAction" + } + }, + { + "name": "parameters", + "getter_name": "parameters", + "moor_type": "blob", + "nullable": false, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [], + "type_converter": { + "dart_expr": "editParameterConverter", + "dart_type_name": "Map" + } + }, + { + "name": "sequence", + "getter_name": "sequence", + "moor_type": "int", + "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": 30, + "references": [], + "type": "table", + "data": { + "name": "settings", + "was_declared_in_moor": false, + "columns": [ + { + "name": "key", + "getter_name": "key", + "moor_type": "string", + "nullable": false, + "customConstraints": null, + "default_dart": null, + "default_client_dart": null, + "dsl_features": [] + }, + { + "name": "value", + "getter_name": "value", + "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": [] + } + ], + "is_virtual": false, + "without_rowid": true, + "constraints": [], + "strict": true, + "explicit_pk": [ + "key" + ] + } + }, + { + "id": 31, + "references": [ + 18 + ], + "type": "index", + "data": { + "on": 18, + "name": "idx_partner_shared_with_id", + "sql": "CREATE INDEX IF NOT EXISTS idx_partner_shared_with_id ON partner_entity (shared_with_id)", + "unique": false, + "columns": [] + } + }, + { + "id": 32, + "references": [ + 19 + ], + "type": "index", + "data": { + "on": 19, + "name": "idx_lat_lng", + "sql": "CREATE INDEX IF NOT EXISTS idx_lat_lng ON remote_exif_entity (latitude, longitude)", + "unique": false, + "columns": [] + } + }, + { + "id": 33, + "references": [ + 19 + ], + "type": "index", + "data": { + "on": 19, + "name": "idx_remote_exif_city", + "sql": "CREATE INDEX IF NOT EXISTS idx_remote_exif_city\nON remote_exif_entity (city) WHERE city IS NOT NULL\n", + "unique": false, + "columns": [] + } + }, + { + "id": 34, + "references": [ + 20 + ], + "type": "index", + "data": { + "on": 20, + "name": "idx_remote_album_asset_album_asset", + "sql": "CREATE INDEX IF NOT EXISTS idx_remote_album_asset_album_asset ON remote_album_asset_entity (album_id, asset_id)", + "unique": false, + "columns": [] + } + }, + { + "id": 35, + "references": [ + 22 + ], + "type": "index", + "data": { + "on": 22, + "name": "idx_remote_asset_cloud_id", + "sql": "CREATE INDEX IF NOT EXISTS idx_remote_asset_cloud_id ON remote_asset_cloud_id_entity (cloud_id)", + "unique": false, + "columns": [] + } + }, + { + "id": 36, + "references": [ + 25 + ], + "type": "index", + "data": { + "on": 25, + "name": "idx_person_owner_id", + "sql": "CREATE INDEX IF NOT EXISTS idx_person_owner_id ON person_entity (owner_id)", + "unique": false, + "columns": [] + } + }, + { + "id": 37, + "references": [ + 26 + ], + "type": "index", + "data": { + "on": 26, + "name": "idx_asset_face_person_id", + "sql": "CREATE INDEX IF NOT EXISTS idx_asset_face_person_id ON asset_face_entity (person_id)", + "unique": false, + "columns": [] + } + }, + { + "id": 38, + "references": [ + 26 + ], + "type": "index", + "data": { + "on": 26, + "name": "idx_asset_face_asset_id", + "sql": "CREATE INDEX IF NOT EXISTS idx_asset_face_asset_id ON asset_face_entity (asset_id)", + "unique": false, + "columns": [] + } + }, + { + "id": 39, + "references": [ + 26 + ], + "type": "index", + "data": { + "on": 26, + "name": "idx_asset_face_visible_person", + "sql": "CREATE INDEX IF NOT EXISTS idx_asset_face_visible_person\nON asset_face_entity (person_id, asset_id)\nWHERE is_visible = 1 AND deleted_at IS NULL\n", + "unique": false, + "columns": [] + } + }, + { + "id": 40, + "references": [ + 28 + ], + "type": "index", + "data": { + "on": 28, + "name": "idx_trashed_local_asset_checksum", + "sql": "CREATE INDEX IF NOT EXISTS idx_trashed_local_asset_checksum ON trashed_local_asset_entity (checksum)", + "unique": false, + "columns": [] + } + }, + { + "id": 41, + "references": [ + 28 + ], + "type": "index", + "data": { + "on": 28, + "name": "idx_trashed_local_asset_album", + "sql": "CREATE INDEX IF NOT EXISTS idx_trashed_local_asset_album ON trashed_local_asset_entity (album_id)", + "unique": false, + "columns": [] + } + }, + { + "id": 42, + "references": [ + 29 + ], + "type": "index", + "data": { + "on": 29, + "name": "idx_asset_edit_asset_id", + "sql": "CREATE INDEX IF NOT EXISTS idx_asset_edit_asset_id ON asset_edit_entity (asset_id)", + "unique": false, + "columns": [] + } + } + ], + "fixed_sql": [ + { + "name": "user_entity", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"user_entity\" (\"id\" TEXT NOT NULL, \"name\" TEXT NOT NULL, \"email\" TEXT NOT NULL, \"has_profile_image\" INTEGER NOT NULL DEFAULT 0 CHECK (\"has_profile_image\" IN (0, 1)), \"profile_changed_at\" TEXT NOT NULL DEFAULT (CURRENT_TIMESTAMP), \"avatar_color\" INTEGER NOT NULL DEFAULT 0, PRIMARY KEY (\"id\")) WITHOUT ROWID, STRICT;" + } + ] + }, + { + "name": "remote_asset_entity", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"remote_asset_entity\" (\"name\" TEXT NOT NULL, \"type\" INTEGER NOT NULL, \"created_at\" TEXT NOT NULL DEFAULT (CURRENT_TIMESTAMP), \"updated_at\" TEXT NOT NULL DEFAULT (CURRENT_TIMESTAMP), \"width\" INTEGER NULL, \"height\" INTEGER NULL, \"duration_ms\" INTEGER NULL, \"id\" TEXT NOT NULL, \"checksum\" TEXT NOT NULL, \"is_favorite\" INTEGER NOT NULL DEFAULT 0 CHECK (\"is_favorite\" IN (0, 1)), \"owner_id\" TEXT NOT NULL REFERENCES user_entity (id) ON DELETE CASCADE, \"local_date_time\" TEXT NULL, \"thumb_hash\" TEXT NULL, \"deleted_at\" TEXT NULL, \"uploaded_at\" TEXT NULL, \"live_photo_video_id\" TEXT NULL, \"visibility\" INTEGER NOT NULL, \"stack_id\" TEXT NULL, \"library_id\" TEXT NULL, \"is_edited\" INTEGER NOT NULL DEFAULT 0 CHECK (\"is_edited\" IN (0, 1)), PRIMARY KEY (\"id\")) WITHOUT ROWID, STRICT;" + } + ] + }, + { + "name": "stack_entity", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"stack_entity\" (\"id\" TEXT NOT NULL, \"created_at\" TEXT NOT NULL DEFAULT (CURRENT_TIMESTAMP), \"updated_at\" TEXT NOT NULL DEFAULT (CURRENT_TIMESTAMP), \"owner_id\" TEXT NOT NULL REFERENCES user_entity (id) ON DELETE CASCADE, \"primary_asset_id\" TEXT NOT NULL, PRIMARY KEY (\"id\")) WITHOUT ROWID, STRICT;" + } + ] + }, + { + "name": "local_asset_entity", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"local_asset_entity\" (\"name\" TEXT NOT NULL, \"type\" INTEGER NOT NULL, \"created_at\" TEXT NOT NULL DEFAULT (CURRENT_TIMESTAMP), \"updated_at\" TEXT NOT NULL DEFAULT (CURRENT_TIMESTAMP), \"width\" INTEGER NULL, \"height\" INTEGER NULL, \"duration_ms\" INTEGER NULL, \"id\" TEXT NOT NULL, \"checksum\" TEXT NULL, \"is_favorite\" INTEGER NOT NULL DEFAULT 0 CHECK (\"is_favorite\" IN (0, 1)), \"orientation\" INTEGER NOT NULL DEFAULT 0, \"i_cloud_id\" TEXT NULL, \"adjustment_time\" TEXT NULL, \"latitude\" REAL NULL, \"longitude\" REAL NULL, \"playback_style\" INTEGER NOT NULL DEFAULT 0, PRIMARY KEY (\"id\")) WITHOUT ROWID, STRICT;" + } + ] + }, + { + "name": "remote_album_entity", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"remote_album_entity\" (\"id\" TEXT NOT NULL, \"name\" TEXT NOT NULL, \"description\" TEXT NOT NULL DEFAULT '', \"created_at\" TEXT NOT NULL DEFAULT (CURRENT_TIMESTAMP), \"updated_at\" TEXT NOT NULL DEFAULT (CURRENT_TIMESTAMP), \"thumbnail_asset_id\" TEXT NULL REFERENCES remote_asset_entity (id) ON DELETE SET NULL, \"is_activity_enabled\" INTEGER NOT NULL DEFAULT 1 CHECK (\"is_activity_enabled\" IN (0, 1)), \"order\" INTEGER NOT NULL, PRIMARY KEY (\"id\")) WITHOUT ROWID, STRICT;" + } + ] + }, + { + "name": "local_album_entity", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"local_album_entity\" (\"id\" TEXT NOT NULL, \"name\" TEXT NOT NULL, \"updated_at\" TEXT NOT NULL DEFAULT (CURRENT_TIMESTAMP), \"backup_selection\" INTEGER NOT NULL, \"is_ios_shared_album\" INTEGER NOT NULL DEFAULT 0 CHECK (\"is_ios_shared_album\" IN (0, 1)), \"linked_remote_album_id\" TEXT NULL REFERENCES remote_album_entity (id) ON DELETE SET NULL, \"marker\" INTEGER NULL CHECK (\"marker\" IN (0, 1)), PRIMARY KEY (\"id\")) WITHOUT ROWID, STRICT;" + } + ] + }, + { + "name": "local_album_asset_entity", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"local_album_asset_entity\" (\"asset_id\" TEXT NOT NULL REFERENCES local_asset_entity (id) ON DELETE CASCADE, \"album_id\" TEXT NOT NULL REFERENCES local_album_entity (id) ON DELETE CASCADE, \"marker\" INTEGER NULL CHECK (\"marker\" IN (0, 1)), PRIMARY KEY (\"asset_id\", \"album_id\")) WITHOUT ROWID, STRICT;" + } + ] + }, + { + "name": "idx_local_album_asset_album_asset", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE INDEX IF NOT EXISTS idx_local_album_asset_album_asset ON local_album_asset_entity (album_id, asset_id)" + } + ] + }, + { + "name": "idx_local_asset_checksum", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE INDEX IF NOT EXISTS idx_local_asset_checksum ON local_asset_entity (checksum)" + } + ] + }, + { + "name": "idx_local_asset_cloud_id", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE INDEX IF NOT EXISTS idx_local_asset_cloud_id ON local_asset_entity (i_cloud_id)" + } + ] + }, + { + "name": "idx_stack_primary_asset_id", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE INDEX IF NOT EXISTS idx_stack_primary_asset_id ON stack_entity (primary_asset_id)" + } + ] + }, + { + "name": "UQ_remote_assets_owner_checksum", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum ON remote_asset_entity (owner_id, checksum) WHERE(library_id IS NULL)" + } + ] + }, + { + "name": "UQ_remote_assets_owner_library_checksum", + "sql": [ + { + "dialect": "sqlite", + "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)" + } + ] + }, + { + "name": "idx_remote_asset_checksum", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE INDEX IF NOT EXISTS idx_remote_asset_checksum ON remote_asset_entity (checksum)" + } + ] + }, + { + "name": "idx_remote_asset_stack_id", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE INDEX IF NOT EXISTS idx_remote_asset_stack_id ON remote_asset_entity (stack_id)" + } + ] + }, + { + "name": "idx_remote_asset_owner_visibility_deleted_created", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE INDEX IF NOT EXISTS idx_remote_asset_owner_visibility_deleted_created ON remote_asset_entity (owner_id, visibility, deleted_at, created_at DESC)" + } + ] + }, + { + "name": "auth_user_entity", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"auth_user_entity\" (\"id\" TEXT NOT NULL, \"name\" TEXT NOT NULL, \"email\" TEXT NOT NULL, \"is_admin\" INTEGER NOT NULL DEFAULT 0 CHECK (\"is_admin\" IN (0, 1)), \"has_profile_image\" INTEGER NOT NULL DEFAULT 0 CHECK (\"has_profile_image\" IN (0, 1)), \"profile_changed_at\" TEXT NOT NULL DEFAULT (CURRENT_TIMESTAMP), \"avatar_color\" INTEGER NOT NULL, \"quota_size_in_bytes\" INTEGER NOT NULL DEFAULT 0, \"quota_usage_in_bytes\" INTEGER NOT NULL DEFAULT 0, \"pin_code\" TEXT NULL, PRIMARY KEY (\"id\")) WITHOUT ROWID, STRICT;" + } + ] + }, + { + "name": "user_metadata_entity", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"user_metadata_entity\" (\"user_id\" TEXT NOT NULL REFERENCES user_entity (id) ON DELETE CASCADE, \"key\" INTEGER NOT NULL, \"value\" BLOB NOT NULL, PRIMARY KEY (\"user_id\", \"key\")) WITHOUT ROWID, STRICT;" + } + ] + }, + { + "name": "partner_entity", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"partner_entity\" (\"shared_by_id\" TEXT NOT NULL REFERENCES user_entity (id) ON DELETE CASCADE, \"shared_with_id\" TEXT NOT NULL REFERENCES user_entity (id) ON DELETE CASCADE, \"in_timeline\" INTEGER NOT NULL DEFAULT 0 CHECK (\"in_timeline\" IN (0, 1)), PRIMARY KEY (\"shared_by_id\", \"shared_with_id\")) WITHOUT ROWID, STRICT;" + } + ] + }, + { + "name": "remote_exif_entity", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"remote_exif_entity\" (\"asset_id\" TEXT NOT NULL REFERENCES remote_asset_entity (id) ON DELETE CASCADE, \"city\" TEXT NULL, \"state\" TEXT NULL, \"country\" TEXT NULL, \"date_time_original\" TEXT NULL, \"description\" TEXT NULL, \"height\" INTEGER NULL, \"width\" INTEGER NULL, \"exposure_time\" TEXT NULL, \"f_number\" REAL NULL, \"file_size\" INTEGER NULL, \"focal_length\" REAL NULL, \"latitude\" REAL NULL, \"longitude\" REAL NULL, \"iso\" INTEGER NULL, \"make\" TEXT NULL, \"model\" TEXT NULL, \"lens\" TEXT NULL, \"orientation\" TEXT NULL, \"time_zone\" TEXT NULL, \"rating\" INTEGER NULL, \"projection_type\" TEXT NULL, PRIMARY KEY (\"asset_id\")) WITHOUT ROWID, STRICT;" + } + ] + }, + { + "name": "remote_album_asset_entity", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"remote_album_asset_entity\" (\"asset_id\" TEXT NOT NULL REFERENCES remote_asset_entity (id) ON DELETE CASCADE, \"album_id\" TEXT NOT NULL REFERENCES remote_album_entity (id) ON DELETE CASCADE, PRIMARY KEY (\"asset_id\", \"album_id\")) WITHOUT ROWID, STRICT;" + } + ] + }, + { + "name": "remote_album_user_entity", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"remote_album_user_entity\" (\"album_id\" TEXT NOT NULL REFERENCES remote_album_entity (id) ON DELETE CASCADE, \"user_id\" TEXT NOT NULL REFERENCES user_entity (id) ON DELETE CASCADE, \"role\" INTEGER NOT NULL, PRIMARY KEY (\"album_id\", \"user_id\")) WITHOUT ROWID, STRICT;" + } + ] + }, + { + "name": "remote_asset_cloud_id_entity", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"remote_asset_cloud_id_entity\" (\"asset_id\" TEXT NOT NULL REFERENCES remote_asset_entity (id) ON DELETE CASCADE, \"cloud_id\" TEXT NULL, \"created_at\" TEXT NULL, \"adjustment_time\" TEXT NULL, \"latitude\" REAL NULL, \"longitude\" REAL NULL, PRIMARY KEY (\"asset_id\")) WITHOUT ROWID, STRICT;" + } + ] + }, + { + "name": "memory_entity", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"memory_entity\" (\"id\" TEXT NOT NULL, \"created_at\" TEXT NOT NULL DEFAULT (CURRENT_TIMESTAMP), \"updated_at\" TEXT NOT NULL DEFAULT (CURRENT_TIMESTAMP), \"deleted_at\" TEXT NULL, \"owner_id\" TEXT NOT NULL REFERENCES user_entity (id) ON DELETE CASCADE, \"type\" INTEGER NOT NULL, \"data\" TEXT NOT NULL, \"is_saved\" INTEGER NOT NULL DEFAULT 0 CHECK (\"is_saved\" IN (0, 1)), \"memory_at\" TEXT NOT NULL, \"seen_at\" TEXT NULL, \"show_at\" TEXT NULL, \"hide_at\" TEXT NULL, PRIMARY KEY (\"id\")) WITHOUT ROWID, STRICT;" + } + ] + }, + { + "name": "memory_asset_entity", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"memory_asset_entity\" (\"asset_id\" TEXT NOT NULL REFERENCES remote_asset_entity (id) ON DELETE CASCADE, \"memory_id\" TEXT NOT NULL REFERENCES memory_entity (id) ON DELETE CASCADE, PRIMARY KEY (\"asset_id\", \"memory_id\")) WITHOUT ROWID, STRICT;" + } + ] + }, + { + "name": "person_entity", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"person_entity\" (\"id\" TEXT NOT NULL, \"created_at\" TEXT NOT NULL DEFAULT (CURRENT_TIMESTAMP), \"updated_at\" TEXT NOT NULL DEFAULT (CURRENT_TIMESTAMP), \"owner_id\" TEXT NOT NULL REFERENCES user_entity (id) ON DELETE CASCADE, \"name\" TEXT NOT NULL, \"face_asset_id\" TEXT NULL, \"is_favorite\" INTEGER NOT NULL CHECK (\"is_favorite\" IN (0, 1)), \"is_hidden\" INTEGER NOT NULL CHECK (\"is_hidden\" IN (0, 1)), \"color\" TEXT NULL, \"birth_date\" TEXT NULL, PRIMARY KEY (\"id\")) WITHOUT ROWID, STRICT;" + } + ] + }, + { + "name": "asset_face_entity", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"asset_face_entity\" (\"id\" TEXT NOT NULL, \"asset_id\" TEXT NOT NULL REFERENCES remote_asset_entity (id) ON DELETE CASCADE, \"person_id\" TEXT NULL REFERENCES person_entity (id) ON DELETE SET NULL, \"image_width\" INTEGER NOT NULL, \"image_height\" INTEGER NOT NULL, \"bounding_box_x1\" INTEGER NOT NULL, \"bounding_box_y1\" INTEGER NOT NULL, \"bounding_box_x2\" INTEGER NOT NULL, \"bounding_box_y2\" INTEGER NOT NULL, \"source_type\" TEXT NOT NULL, \"is_visible\" INTEGER NOT NULL DEFAULT 1 CHECK (\"is_visible\" IN (0, 1)), \"deleted_at\" TEXT NULL, PRIMARY KEY (\"id\")) WITHOUT ROWID, STRICT;" + } + ] + }, + { + "name": "store_entity", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"store_entity\" (\"id\" INTEGER NOT NULL, \"string_value\" TEXT NULL, \"int_value\" INTEGER NULL, PRIMARY KEY (\"id\")) WITHOUT ROWID, STRICT;" + } + ] + }, + { + "name": "trashed_local_asset_entity", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"trashed_local_asset_entity\" (\"name\" TEXT NOT NULL, \"type\" INTEGER NOT NULL, \"created_at\" TEXT NOT NULL DEFAULT (CURRENT_TIMESTAMP), \"updated_at\" TEXT NOT NULL DEFAULT (CURRENT_TIMESTAMP), \"width\" INTEGER NULL, \"height\" INTEGER NULL, \"duration_ms\" INTEGER NULL, \"id\" TEXT NOT NULL, \"album_id\" TEXT NOT NULL, \"checksum\" TEXT NULL, \"is_favorite\" INTEGER NOT NULL DEFAULT 0 CHECK (\"is_favorite\" IN (0, 1)), \"orientation\" INTEGER NOT NULL DEFAULT 0, \"source\" INTEGER NOT NULL, \"playback_style\" INTEGER NOT NULL DEFAULT 0, PRIMARY KEY (\"id\", \"album_id\")) WITHOUT ROWID, STRICT;" + } + ] + }, + { + "name": "asset_edit_entity", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"asset_edit_entity\" (\"id\" TEXT NOT NULL, \"asset_id\" TEXT NOT NULL REFERENCES remote_asset_entity (id) ON DELETE CASCADE, \"action\" INTEGER NOT NULL, \"parameters\" BLOB NOT NULL, \"sequence\" INTEGER NOT NULL, PRIMARY KEY (\"id\")) WITHOUT ROWID, STRICT;" + } + ] + }, + { + "name": "settings", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE TABLE IF NOT EXISTS \"settings\" (\"key\" TEXT NOT NULL, \"value\" TEXT NOT NULL, \"updated_at\" TEXT NOT NULL DEFAULT (CURRENT_TIMESTAMP), PRIMARY KEY (\"key\")) WITHOUT ROWID, STRICT;" + } + ] + }, + { + "name": "idx_partner_shared_with_id", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE INDEX IF NOT EXISTS idx_partner_shared_with_id ON partner_entity (shared_with_id)" + } + ] + }, + { + "name": "idx_lat_lng", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE INDEX IF NOT EXISTS idx_lat_lng ON remote_exif_entity (latitude, longitude)" + } + ] + }, + { + "name": "idx_remote_exif_city", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE INDEX IF NOT EXISTS idx_remote_exif_city ON remote_exif_entity (city) WHERE city IS NOT NULL" + } + ] + }, + { + "name": "idx_remote_album_asset_album_asset", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE INDEX IF NOT EXISTS idx_remote_album_asset_album_asset ON remote_album_asset_entity (album_id, asset_id)" + } + ] + }, + { + "name": "idx_remote_asset_cloud_id", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE INDEX IF NOT EXISTS idx_remote_asset_cloud_id ON remote_asset_cloud_id_entity (cloud_id)" + } + ] + }, + { + "name": "idx_person_owner_id", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE INDEX IF NOT EXISTS idx_person_owner_id ON person_entity (owner_id)" + } + ] + }, + { + "name": "idx_asset_face_person_id", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE INDEX IF NOT EXISTS idx_asset_face_person_id ON asset_face_entity (person_id)" + } + ] + }, + { + "name": "idx_asset_face_asset_id", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE INDEX IF NOT EXISTS idx_asset_face_asset_id ON asset_face_entity (asset_id)" + } + ] + }, + { + "name": "idx_asset_face_visible_person", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE INDEX IF NOT EXISTS idx_asset_face_visible_person ON asset_face_entity (person_id, asset_id) WHERE is_visible = 1 AND deleted_at IS NULL" + } + ] + }, + { + "name": "idx_trashed_local_asset_checksum", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE INDEX IF NOT EXISTS idx_trashed_local_asset_checksum ON trashed_local_asset_entity (checksum)" + } + ] + }, + { + "name": "idx_trashed_local_asset_album", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE INDEX IF NOT EXISTS idx_trashed_local_asset_album ON trashed_local_asset_entity (album_id)" + } + ] + }, + { + "name": "idx_asset_edit_asset_id", + "sql": [ + { + "dialect": "sqlite", + "sql": "CREATE INDEX IF NOT EXISTS idx_asset_edit_asset_id ON asset_edit_entity (asset_id)" + } + ] + } + ] +} \ No newline at end of file diff --git a/mobile/ios/Runner.xcodeproj/project.pbxproj b/mobile/ios/Runner.xcodeproj/project.pbxproj index e3325525eb..42926ef45e 100644 --- a/mobile/ios/Runner.xcodeproj/project.pbxproj +++ b/mobile/ios/Runner.xcodeproj/project.pbxproj @@ -9,7 +9,7 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 3B6A31FED0FC846D6BD69BBC /* Pods_ShareExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 357FC57E54FD0F51795CF28A /* Pods_ShareExtension.framework */; }; + 467DA6EAF83F3481F8BD94AB /* Pods_ShareExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8AB817AA297EDEC88B23F3F6 /* Pods_ShareExtension.framework */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; @@ -22,7 +22,7 @@ B2EE00022E72CA15008B6CA7 /* PermissionApi.g.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2EE00012E72CA15008B6CA7 /* PermissionApi.g.swift */; }; B2EE00042E72CA15008B6CA7 /* PermissionApiImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2EE00032E72CA15008B6CA7 /* PermissionApiImpl.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 */; }; + D3BED739C0BC29BB32E18EB2 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CC499FBCE6B29B2DAFED7130 /* 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 */; }; F0B57D3C2DF764BD00DC5BCC /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F0B57D3B2DF764BD00DC5BCC /* SwiftUI.framework */; }; @@ -85,16 +85,18 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 10B378D23F917891A0F23E33 /* Pods-ShareExtension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ShareExtension.release.xcconfig"; path = "Target Support Files/Pods-ShareExtension/Pods-ShareExtension.release.xcconfig"; sourceTree = ""; }; 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 2E3441B73560D0F6FD25E04F /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; - 357FC57E54FD0F51795CF28A /* Pods_ShareExtension.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ShareExtension.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 571EAA93D77181C7C98C2EA6 /* Pods-ShareExtension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ShareExtension.release.xcconfig"; path = "Target Support Files/Pods-ShareExtension/Pods-ShareExtension.release.xcconfig"; sourceTree = ""; }; + 614A7F5DC5DB09E89E4FCBE8 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 681FBA560D5D2ADDE4F0B59E /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + 6D160F04A389B9FFBC557803 /* 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 = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; - 886774DBDDE6B35BF2B4F2CD /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 8AB817AA297EDEC88B23F3F6 /* Pods_ShareExtension.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ShareExtension.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 937632897A02DE9C249F20A6 /* Pods-ShareExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ShareExtension.debug.xcconfig"; path = "Target Support Files/Pods-ShareExtension/Pods-ShareExtension.debug.xcconfig"; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Immich-Debug.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Immich-Debug.app"; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -103,7 +105,6 @@ 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 = ""; }; A01DD6982F7F43B40049AB63 /* ImageRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageRequest.swift; 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 = ""; }; @@ -111,12 +112,11 @@ B2EE00012E72CA15008B6CA7 /* PermissionApi.g.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PermissionApi.g.swift; sourceTree = ""; }; B2EE00032E72CA15008B6CA7 /* PermissionApiImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PermissionApiImpl.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 = ""; }; + C4A6A71F33CE37B3C913115C /* 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 = ""; }; + CC499FBCE6B29B2DAFED7130 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 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; }; F0B57D3B2DF764BD00DC5BCC /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; }; - F7101BB0391A314774615E89 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; - F8A35EA3C3E01BD66AFDE0E5 /* Pods-ShareExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ShareExtension.debug.xcconfig"; path = "Target Support Files/Pods-ShareExtension/Pods-ShareExtension.debug.xcconfig"; sourceTree = ""; }; FA9973382CF6DF4B000EF859 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; }; FAC6F8902D287C890078CB2F /* ShareExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = ShareExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; FAC6F8B12D287F120078CB2F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -199,7 +199,7 @@ FEE084F82EC172460045228E /* SQLiteData in Frameworks */, FEE084FB2EC1725A0045228E /* RawStructuredFieldValues in Frameworks */, FEE084FD2EC1725A0045228E /* StructuredFieldValues in Frameworks */, - D218389C4A4C4693F141F7D1 /* Pods_Runner.framework in Frameworks */, + D3BED739C0BC29BB32E18EB2 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -216,7 +216,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 3B6A31FED0FC846D6BD69BBC /* Pods_ShareExtension.framework in Frameworks */, + 467DA6EAF83F3481F8BD94AB /* Pods_ShareExtension.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -226,12 +226,12 @@ 0FB772A5B9601143383626CA /* Pods */ = { isa = PBXGroup; children = ( - 2E3441B73560D0F6FD25E04F /* Pods-Runner.debug.xcconfig */, - E0E99CDC17B3EB7FA8BA2332 /* Pods-Runner.release.xcconfig */, - F7101BB0391A314774615E89 /* Pods-Runner.profile.xcconfig */, - F8A35EA3C3E01BD66AFDE0E5 /* Pods-ShareExtension.debug.xcconfig */, - 571EAA93D77181C7C98C2EA6 /* Pods-ShareExtension.release.xcconfig */, - B1FBA9EE014DE20271B0FE77 /* Pods-ShareExtension.profile.xcconfig */, + 614A7F5DC5DB09E89E4FCBE8 /* Pods-Runner.debug.xcconfig */, + 6D160F04A389B9FFBC557803 /* Pods-Runner.release.xcconfig */, + 681FBA560D5D2ADDE4F0B59E /* Pods-Runner.profile.xcconfig */, + 937632897A02DE9C249F20A6 /* Pods-ShareExtension.debug.xcconfig */, + 10B378D23F917891A0F23E33 /* Pods-ShareExtension.release.xcconfig */, + C4A6A71F33CE37B3C913115C /* Pods-ShareExtension.profile.xcconfig */, ); path = Pods; sourceTree = ""; @@ -239,10 +239,10 @@ 1754452DD81DA6620E279E51 /* Frameworks */ = { isa = PBXGroup; children = ( - 886774DBDDE6B35BF2B4F2CD /* Pods_Runner.framework */, - 357FC57E54FD0F51795CF28A /* Pods_ShareExtension.framework */, F0B57D392DF764BD00DC5BCC /* WidgetKit.framework */, F0B57D3B2DF764BD00DC5BCC /* SwiftUI.framework */, + CC499FBCE6B29B2DAFED7130 /* Pods_Runner.framework */, + 8AB817AA297EDEC88B23F3F6 /* Pods_ShareExtension.framework */, ); name = Frameworks; sourceTree = ""; @@ -370,7 +370,7 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - 4044AF030EF7D8721844FFBA /* [CP] Check Pods Manifest.lock */, + BAEA01ACA3F5C9CD3D732370 /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, @@ -378,8 +378,8 @@ 9705A1C41CF9048500538489 /* Embed Frameworks */, FAC6F89A2D287C890078CB2F /* Embed Foundation Extensions */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - D218A34AEE62BC1EF119F5B0 /* [CP] Embed Pods Frameworks */, - 6724EEB7D74949FA08581154 /* [CP] Copy Pods Resources */, + 513DA7292DED6106813332F4 /* [CP] Embed Pods Frameworks */, + 2FA39DEC809D6D7C4A01EFCB /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -393,6 +393,9 @@ FEE084F22EC172080045228E /* Schemas */, ); name = Runner; + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); productName = Runner; productReference = 97C146EE1CF9000F007C117D /* Immich-Debug.app */; productType = "com.apple.product-type.application"; @@ -421,7 +424,7 @@ isa = PBXNativeTarget; buildConfigurationList = FAC6F8A02D287C890078CB2F /* Build configuration list for PBXNativeTarget "ShareExtension" */; buildPhases = ( - 3BEF3D71D97E337D921C0EB5 /* [CP] Check Pods Manifest.lock */, + 8EC9CF3E20AF32BF24D4F3E1 /* [CP] Check Pods Manifest.lock */, FAC6F88C2D287C890078CB2F /* Sources */, FAC6F88D2D287C890078CB2F /* Frameworks */, FAC6F88E2D287C890078CB2F /* Resources */, @@ -470,7 +473,7 @@ ); mainGroup = 97C146E51CF9000F007C117D; packageReferences = ( - 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */, + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */, FEE084F62EC172460045228E /* XCRemoteSwiftPackageReference "sqlite-data" */, FEE084F92EC1725A0045228E /* XCRemoteSwiftPackageReference "swift-http-structured-headers" */, ); @@ -517,6 +520,23 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 2FA39DEC809D6D7C4A01EFCB /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Copy Pods Resources"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -533,7 +553,24 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; - 3BEF3D71D97E337D921C0EB5 /* [CP] Check Pods Manifest.lock */ = { + 513DA7292DED6106813332F4 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 8EC9CF3E20AF32BF24D4F3E1 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -555,7 +592,22 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 4044AF030EF7D8721844FFBA /* [CP] Check Pods Manifest.lock */ = { + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n"; + }; + BAEA01ACA3F5C9CD3D732370 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -577,55 +629,6 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 6724EEB7D74949FA08581154 /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Copy Pods Resources"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; - 9740EEB61CF901F6004384FC /* Run Script */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Run Script"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n"; - }; - D218A34AEE62BC1EF119F5B0 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -1092,7 +1095,7 @@ }; FAC6F89C2D287C890078CB2F /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = F8A35EA3C3E01BD66AFDE0E5 /* Pods-ShareExtension.debug.xcconfig */; + baseConfigurationReference = 937632897A02DE9C249F20A6 /* Pods-ShareExtension.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; @@ -1135,7 +1138,7 @@ }; FAC6F89D2D287C890078CB2F /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 571EAA93D77181C7C98C2EA6 /* Pods-ShareExtension.release.xcconfig */; + baseConfigurationReference = 10B378D23F917891A0F23E33 /* Pods-ShareExtension.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; @@ -1175,7 +1178,7 @@ }; FAC6F89E2D287C890078CB2F /* Profile */ = { isa = XCBuildConfiguration; - baseConfigurationReference = B1FBA9EE014DE20271B0FE77 /* Pods-ShareExtension.profile.xcconfig */; + baseConfigurationReference = C4A6A71F33CE37B3C913115C /* Pods-ShareExtension.profile.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; @@ -1258,6 +1261,13 @@ }; /* End XCConfigurationList section */ +/* Begin XCLocalSwiftPackageReference section */ + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; + }; +/* End XCLocalSwiftPackageReference section */ + /* Begin XCRemoteSwiftPackageReference section */ FEE084F62EC172460045228E /* XCRemoteSwiftPackageReference "sqlite-data" */ = { isa = XCRemoteSwiftPackageReference; @@ -1278,6 +1288,10 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = FlutterGeneratedPluginSwiftPackage; + }; FEE084F72EC172460045228E /* SQLiteData */ = { isa = XCSwiftPackageProductDependency; package = FEE084F62EC172460045228E /* XCRemoteSwiftPackageReference "sqlite-data" */; diff --git a/mobile/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved b/mobile/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved index ed1e2d084d..0e2d8e8a3d 100644 --- a/mobile/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/mobile/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -5,8 +5,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/combine-schedulers", "state" : { - "revision" : "5928286acce13def418ec36d05a001a9641086f2", - "version" : "1.0.3" + "revision" : "fd16d76fd8b9a976d88bfb6cacc05ca8d19c91b6", + "version" : "1.1.0" } }, { diff --git a/mobile/ios/fastlane/Fastfile b/mobile/ios/fastlane/Fastfile index f5f7f7887a..17fa91786e 100644 --- a/mobile/ios/fastlane/Fastfile +++ b/mobile/ios/fastlane/Fastfile @@ -49,7 +49,7 @@ def get_version_from_pubspec pubspec = YAML.load_file(pubspec_path) version_string = pubspec['version'] - version_string ? version_string.split('+').first : nil + version_string ? version_string.split('+').first.split('-').first : nil end # Helper method to configure code signing for all targets diff --git a/mobile/lib/domain/models/config/app_config.dart b/mobile/lib/domain/models/config/app_config.dart index dc1bf96679..a955fb73a8 100644 --- a/mobile/lib/domain/models/config/app_config.dart +++ b/mobile/lib/domain/models/config/app_config.dart @@ -12,7 +12,7 @@ import 'package:immich_mobile/domain/models/config/theme_config.dart'; import 'package:immich_mobile/domain/models/config/timeline_config.dart'; import 'package:immich_mobile/domain/models/config/viewer_config.dart'; import 'package:immich_mobile/domain/models/log.model.dart'; -import 'package:immich_mobile/domain/models/metadata_key.dart'; +import 'package:immich_mobile/domain/models/settings_key.dart'; import 'package:immich_mobile/domain/models/timeline.model.dart'; import 'package:immich_mobile/providers/album/album_sort_by_options.provider.dart'; @@ -95,7 +95,7 @@ class AppConfig { String toString() => 'AppConfig(logLevel: $logLevel, theme: $theme, cleanup: $cleanup, map: $map, timeline: $timeline, image: $image, viewer: $viewer, slideshow: $slideshow, album: $album, backup: $backup, network: $network)'; - T read(MetadataKey key) => + T read(SettingsKey key) => (switch (key) { .logLevel => logLevel, .themePrimaryColor => theme.primaryColor, @@ -143,15 +143,10 @@ class AppConfig { }) as T; - factory AppConfig.fromEntries(Map, Object> entries) { - var config = const AppConfig(); - for (final MapEntry(key: key, value: value) in entries.entries) { - config = config.write(key, value); - } - return config; - } + factory AppConfig.fromEntries(Map, Object> overrides) => + overrides.entries.fold(const AppConfig(), (config, entry) => config.write(entry.key, entry.value)); - AppConfig write(MetadataKey key, T value) { + AppConfig write(SettingsKey key, T value) { return switch (key) { .logLevel => copyWith(logLevel: value as LogLevel), .themePrimaryColor => copyWith(theme: theme.copyWith(primaryColor: value as ImmichColorPreset)), diff --git a/mobile/lib/domain/models/metadata_key.dart b/mobile/lib/domain/models/settings_key.dart similarity index 76% rename from mobile/lib/domain/models/metadata_key.dart rename to mobile/lib/domain/models/settings_key.dart index f68af0be84..5277a37a60 100644 --- a/mobile/lib/domain/models/metadata_key.dart +++ b/mobile/lib/domain/models/settings_key.dart @@ -7,14 +7,7 @@ import 'package:immich_mobile/domain/models/log.model.dart'; import 'package:immich_mobile/domain/models/timeline.model.dart'; import 'package:immich_mobile/providers/album/album_sort_by_options.provider.dart'; -enum MetadataScope { - user, // keys with this scope are deleted on logout - system; - - const MetadataScope(); -} - -enum MetadataKey { +enum SettingsKey { // Theme themePrimaryColor(codec: _EnumCodec(ImmichColorPreset.values)), themeMode(codec: _EnumCodec(ThemeMode.values)), @@ -32,14 +25,11 @@ enum MetadataKey { viewerTapToNavigate(), // Network - networkAutoEndpointSwitching(scope: .system), - networkPreferredWifiName(scope: .system), - networkLocalEndpoint(scope: .system), - networkExternalEndpointList>(scope: .system, codec: _ListCodec(_PrimitiveCodec.string)), - networkCustomHeaders>( - scope: .system, - codec: _MapCodec(_PrimitiveCodec.string, _PrimitiveCodec.string), - ), + networkAutoEndpointSwitching(), + networkPreferredWifiName(), + networkLocalEndpoint(), + networkExternalEndpointList>(codec: _ListCodec(_PrimitiveCodec.string)), + networkCustomHeaders>(codec: _MapCodec(_PrimitiveCodec.string, _PrimitiveCodec.string)), // Album albumSortMode(codec: _EnumCodec(AlbumSortMode.values)), @@ -60,7 +50,7 @@ enum MetadataKey { timelineStorageIndicator(), // Log - logLevel(scope: .system, codec: _EnumCodec(LogLevel.values)), + logLevel(codec: _EnumCodec(LogLevel.values)), // Map mapShowFavoriteOnly(), @@ -83,25 +73,24 @@ enum MetadataKey { slideshowLook(codec: _EnumCodec(SlideshowLook.values)), slideshowDirection(codec: _EnumCodec(SlideshowDirection.values)); - final MetadataScope scope; - final _MetadataCodec? _codecOverride; + final _SettingsCodec? _codecOverride; - const MetadataKey({this.scope = .user, _MetadataCodec? codec}) : _codecOverride = codec; + const SettingsKey({_SettingsCodec? codec}) : _codecOverride = codec; - _MetadataCodec get _codec => _codecOverride ?? _MetadataCodec.forType(T); + _SettingsCodec get _codec => _codecOverride ?? _SettingsCodec.forType(T); String encode(T value) => _codec.encode(value); T decode(String raw) => _codec.decode(raw); } -sealed class _MetadataCodec { - const _MetadataCodec(); +sealed class _SettingsCodec { + const _SettingsCodec(); String encode(T value); T decode(String raw); - static const Map> _primitives = { + static const Map> _primitives = { int: _PrimitiveCodec.integer, double: _PrimitiveCodec.real, bool: _PrimitiveCodec.boolean, @@ -109,16 +98,16 @@ sealed class _MetadataCodec { DateTime: _DateTimeCodec(), }; - static _MetadataCodec forType(Type runtimeType) { + static _SettingsCodec forType(Type runtimeType) { final codec = _primitives[runtimeType]; if (codec == null) { - throw StateError('No primitive codec for $runtimeType. Provide an explicit codec when defining the MetadataKey.'); + throw StateError('No primitive codec for $runtimeType. Provide an explicit codec when defining the SettingsKey.'); } - return codec as _MetadataCodec; + return codec as _SettingsCodec; } } -final class _EnumCodec extends _MetadataCodec { +final class _EnumCodec extends _SettingsCodec { final List values; const _EnumCodec(this.values); @@ -130,7 +119,7 @@ final class _EnumCodec extends _MetadataCodec { T decode(String raw) => values.firstWhere((v) => v.name == raw); } -final class _DateTimeCodec extends _MetadataCodec { +final class _DateTimeCodec extends _SettingsCodec { const _DateTimeCodec(); @override @@ -140,9 +129,9 @@ final class _DateTimeCodec extends _MetadataCodec { DateTime decode(String raw) => DateTime.parse(raw); } -final class _MapCodec extends _MetadataCodec> { - final _MetadataCodec _keyCodec; - final _MetadataCodec _valueCodec; +final class _MapCodec extends _SettingsCodec> { + final _SettingsCodec _keyCodec; + final _SettingsCodec _valueCodec; const _MapCodec(this._keyCodec, this._valueCodec); @@ -178,8 +167,8 @@ final class _MapCodec extends _MetadataCodec } } -final class _ListCodec extends _MetadataCodec> { - final _MetadataCodec _elementCodec; +final class _ListCodec extends _SettingsCodec> { + final _SettingsCodec _elementCodec; const _ListCodec(this._elementCodec); @@ -208,7 +197,7 @@ final class _ListCodec extends _MetadataCodec> { } } -final class _PrimitiveCodec extends _MetadataCodec { +final class _PrimitiveCodec extends _SettingsCodec { final T Function(String) _parse; const _PrimitiveCodec._(this._parse); diff --git a/mobile/lib/domain/services/background_worker.service.dart b/mobile/lib/domain/services/background_worker.service.dart index 3411f8aa13..d28f7ff14b 100644 --- a/mobile/lib/domain/services/background_worker.service.dart +++ b/mobile/lib/domain/services/background_worker.service.dart @@ -11,7 +11,7 @@ import 'package:immich_mobile/entities/store.entity.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/infrastructure/repositories/metadata.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/settings.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/background_sync.provider.dart'; @@ -39,7 +39,7 @@ class BackgroundWorkerFgService { _foregroundHostApi.saveNotificationMessage(title, body); Future configure({int? minimumDelaySeconds, bool? requireCharging}) { - final backup = MetadataRepository.instance.appConfig.backup; + final backup = SettingsRepository.instance.appConfig.backup; return _foregroundHostApi.configure( BackgroundWorkerSettings( minimumDelaySeconds: minimumDelaySeconds ?? backup.triggerDelay, @@ -67,7 +67,7 @@ class BackgroundWorkerBgService extends BackgroundWorkerFlutterApi { BackgroundWorkerFlutterApi.setUp(this); } - bool get _isBackupEnabled => MetadataRepository.instance.appConfig.backup.enabled; + bool get _isBackupEnabled => SettingsRepository.instance.appConfig.backup.enabled; Future init() async { try { diff --git a/mobile/lib/domain/services/log.service.dart b/mobile/lib/domain/services/log.service.dart index ac65c9bedf..216f030b12 100644 --- a/mobile/lib/domain/services/log.service.dart +++ b/mobile/lib/domain/services/log.service.dart @@ -2,9 +2,9 @@ import 'dart:async'; import 'package:immich_mobile/constants/constants.dart'; import 'package:immich_mobile/domain/models/log.model.dart'; -import 'package:immich_mobile/domain/models/metadata_key.dart'; +import 'package:immich_mobile/domain/models/settings_key.dart'; import 'package:immich_mobile/infrastructure/repositories/log.repository.dart'; -import 'package:immich_mobile/infrastructure/repositories/metadata.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/settings.repository.dart'; import 'package:immich_mobile/utils/debug_print.dart'; import 'package:logging/logging.dart'; @@ -12,10 +12,10 @@ import 'package:logging/logging.dart'; /// /// It listens to Dart's [Logger.root], buffers logs in memory (optionally), /// writes them to a persistent [LogRepository], and manages log levels via -/// [MetadataRepository]. +/// [SettingsRepository]. class LogService { final LogRepository _logRepository; - final MetadataRepository _metadataRepository; + final SettingsRepository _settingsRepository; final List _msgBuffer = []; @@ -38,12 +38,12 @@ class LogService { static Future init({ required LogRepository logRepository, - required MetadataRepository metadataRepository, + required SettingsRepository settingsRepository, bool shouldBuffer = true, }) async { _instance ??= await create( logRepository: logRepository, - metadataRepository: metadataRepository, + settingsRepository: settingsRepository, shouldBuffer: shouldBuffer, ); return _instance!; @@ -51,17 +51,17 @@ class LogService { static Future create({ required LogRepository logRepository, - required MetadataRepository metadataRepository, + required SettingsRepository settingsRepository, bool shouldBuffer = true, }) async { - final instance = LogService._(logRepository, metadataRepository, shouldBuffer); + final instance = LogService._(logRepository, settingsRepository, shouldBuffer); await logRepository.truncate(limit: kLogTruncateLimit); - final level = instance._metadataRepository.appConfig.logLevel; + final level = instance._settingsRepository.appConfig.logLevel; Logger.root.level = Level.LEVELS.elementAtOrNull(level.index) ?? Level.INFO; return instance; } - LogService._(this._logRepository, this._metadataRepository, this._shouldBuffer) { + LogService._(this._logRepository, this._settingsRepository, this._shouldBuffer) { _logSubscription = Logger.root.onRecord.listen(_handleLogRecord); } @@ -91,7 +91,7 @@ class LogService { } Future setLogLevel(LogLevel level) async { - await _metadataRepository.write(MetadataKey.logLevel, level); + await _settingsRepository.write(SettingsKey.logLevel, level); Logger.root.level = level.toLevel(); } diff --git a/mobile/lib/domain/services/remote_album.service.dart b/mobile/lib/domain/services/remote_album.service.dart index e873a7631f..35a8f899a8 100644 --- a/mobile/lib/domain/services/remote_album.service.dart +++ b/mobile/lib/domain/services/remote_album.service.dart @@ -192,43 +192,30 @@ class RemoteAlbumService { required UserDto uploader, required AlbumAssetCandidates candidates, UploadCallbacks uploadCallbacks = const UploadCallbacks(), + Completer? cancelToken, }) async { int addedCount = 0; if (candidates.remoteAssetIds.isNotEmpty) { addedCount += await addAssets(albumId: albumId, assetIds: candidates.remoteAssetIds); } if (candidates.localAssetsToUpload.isNotEmpty) { - addedCount += await _uploadAndAddLocals(albumId, uploader, candidates.localAssetsToUpload, uploadCallbacks); + addedCount += await _uploadAndAddLocals( + albumId, + uploader, + candidates.localAssetsToUpload, + uploadCallbacks, + cancelToken, + ); } return addedCount; } - /// Creates an album, seeding it with already-remote asset IDs, then uploads - /// local-only assets and links each one as it finishes. - Future createAlbumWithAssets({ - required String title, - required UserDto owner, - String? description, - AlbumAssetCandidates candidates = const AlbumAssetCandidates(remoteAssetIds: [], localAssetsToUpload: []), - UploadCallbacks uploadCallbacks = const UploadCallbacks(), - }) async { - final album = await createAlbum( - title: title, - owner: owner, - description: description, - assetIds: candidates.remoteAssetIds, - ); - if (candidates.localAssetsToUpload.isNotEmpty) { - await _uploadAndAddLocals(album.id, owner, candidates.localAssetsToUpload, uploadCallbacks); - } - return album; - } - Future _uploadAndAddLocals( String albumId, UserDto uploader, List localAssets, UploadCallbacks userCallbacks, + Completer? cancelToken, ) async { int addedCount = 0; final pendingAdds = >[]; @@ -258,7 +245,7 @@ class RemoteAlbumService { return; } pendingAdds.add( - _linkUploadedAssetToAlbum(albumId, remoteId, uploader, source) + linkUploadedAssetToAlbum(albumId, remoteId, uploader, source) .then((added) { addedCount += added; }) @@ -269,7 +256,7 @@ class RemoteAlbumService { }, ); - await _uploadService.uploadManual(localAssets, callbacks: wrappedCallbacks); + await _uploadService.uploadManual(localAssets, callbacks: wrappedCallbacks, cancelToken: cancelToken); await Future.wait(pendingAdds); return addedCount; } @@ -288,7 +275,7 @@ class RemoteAlbumService { /// `remote_asset_entity` row from the local source so the FK-protected /// junction insert succeeds. Sync overwrites the placeholder later with /// the authoritative server data. - Future _linkUploadedAssetToAlbum(String albumId, String remoteId, UserDto uploader, LocalAsset source) async { + Future linkUploadedAssetToAlbum(String albumId, String remoteId, UserDto uploader, LocalAsset source) async { final result = await _albumApiRepository.addAssets(albumId, [remoteId]); if (result.added.isEmpty) { return 0; diff --git a/mobile/lib/domain/services/timeline.service.dart b/mobile/lib/domain/services/timeline.service.dart index c6324b356e..4cc58b0fe7 100644 --- a/mobile/lib/domain/services/timeline.service.dart +++ b/mobile/lib/domain/services/timeline.service.dart @@ -7,7 +7,7 @@ import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/events.model.dart'; import 'package:immich_mobile/domain/models/timeline.model.dart'; import 'package:immich_mobile/domain/utils/event_stream.dart'; -import 'package:immich_mobile/infrastructure/repositories/metadata.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/settings.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/timeline.repository.dart'; import 'package:immich_mobile/utils/async_mutex.dart'; @@ -39,12 +39,12 @@ enum TimelineOrigin { class TimelineFactory { final DriftTimelineRepository _timelineRepository; - final MetadataRepository _metadataRepository; + final SettingsRepository _settingsRepository; - const TimelineFactory({required this._timelineRepository, required this._metadataRepository}); + const TimelineFactory({required this._timelineRepository, required this._settingsRepository}); GroupAssetsBy get groupBy { - final group = _metadataRepository.appConfig.timeline.groupAssetsBy; + final group = _settingsRepository.appConfig.timeline.groupAssetsBy; // We do not support auto grouping in the new timeline yet, fallback to day grouping return group == GroupAssetsBy.auto ? GroupAssetsBy.day : group; } diff --git a/mobile/lib/infrastructure/entities/metadata.entity.dart b/mobile/lib/infrastructure/entities/settings.entity.dart similarity index 72% rename from mobile/lib/infrastructure/entities/metadata.entity.dart rename to mobile/lib/infrastructure/entities/settings.entity.dart index 2908245040..36e0bcc990 100644 --- a/mobile/lib/infrastructure/entities/metadata.entity.dart +++ b/mobile/lib/infrastructure/entities/settings.entity.dart @@ -1,8 +1,8 @@ import 'package:drift/drift.dart'; import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; -class MetadataEntity extends Table with DriftDefaultsMixin { - const MetadataEntity(); +class SettingsEntity extends Table with DriftDefaultsMixin { + const SettingsEntity(); TextColumn get key => text()(); @@ -14,5 +14,5 @@ class MetadataEntity extends Table with DriftDefaultsMixin { Set get primaryKey => {key}; @override - String get tableName => "metadata"; + String get tableName => "settings"; } diff --git a/mobile/lib/infrastructure/entities/metadata.entity.drift.dart b/mobile/lib/infrastructure/entities/settings.entity.drift.dart similarity index 74% rename from mobile/lib/infrastructure/entities/metadata.entity.drift.dart rename to mobile/lib/infrastructure/entities/settings.entity.drift.dart index 80bf7bfc43..e2cac89a5e 100644 --- a/mobile/lib/infrastructure/entities/metadata.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/settings.entity.drift.dart @@ -1,28 +1,28 @@ // dart format width=80 // ignore_for_file: type=lint import 'package:drift/drift.dart' as i0; -import 'package:immich_mobile/infrastructure/entities/metadata.entity.drift.dart' +import 'package:immich_mobile/infrastructure/entities/settings.entity.drift.dart' as i1; -import 'package:immich_mobile/infrastructure/entities/metadata.entity.dart' +import 'package:immich_mobile/infrastructure/entities/settings.entity.dart' as i2; import 'package:drift/src/runtime/query_builder/query_builder.dart' as i3; -typedef $$MetadataEntityTableCreateCompanionBuilder = - i1.MetadataEntityCompanion Function({ +typedef $$SettingsEntityTableCreateCompanionBuilder = + i1.SettingsEntityCompanion Function({ required String key, required String value, i0.Value updatedAt, }); -typedef $$MetadataEntityTableUpdateCompanionBuilder = - i1.MetadataEntityCompanion Function({ +typedef $$SettingsEntityTableUpdateCompanionBuilder = + i1.SettingsEntityCompanion Function({ i0.Value key, i0.Value value, i0.Value updatedAt, }); -class $$MetadataEntityTableFilterComposer - extends i0.Composer { - $$MetadataEntityTableFilterComposer({ +class $$SettingsEntityTableFilterComposer + extends i0.Composer { + $$SettingsEntityTableFilterComposer({ required super.$db, required super.$table, super.joinBuilder, @@ -45,9 +45,9 @@ class $$MetadataEntityTableFilterComposer ); } -class $$MetadataEntityTableOrderingComposer - extends i0.Composer { - $$MetadataEntityTableOrderingComposer({ +class $$SettingsEntityTableOrderingComposer + extends i0.Composer { + $$SettingsEntityTableOrderingComposer({ required super.$db, required super.$table, super.joinBuilder, @@ -70,9 +70,9 @@ class $$MetadataEntityTableOrderingComposer ); } -class $$MetadataEntityTableAnnotationComposer - extends i0.Composer { - $$MetadataEntityTableAnnotationComposer({ +class $$SettingsEntityTableAnnotationComposer + extends i0.Composer { + $$SettingsEntityTableAnnotationComposer({ required super.$db, required super.$table, super.joinBuilder, @@ -89,47 +89,47 @@ class $$MetadataEntityTableAnnotationComposer $composableBuilder(column: $table.updatedAt, builder: (column) => column); } -class $$MetadataEntityTableTableManager +class $$SettingsEntityTableTableManager extends i0.RootTableManager< i0.GeneratedDatabase, - i1.$MetadataEntityTable, - i1.MetadataEntityData, - i1.$$MetadataEntityTableFilterComposer, - i1.$$MetadataEntityTableOrderingComposer, - i1.$$MetadataEntityTableAnnotationComposer, - $$MetadataEntityTableCreateCompanionBuilder, - $$MetadataEntityTableUpdateCompanionBuilder, + i1.$SettingsEntityTable, + i1.SettingsEntityData, + i1.$$SettingsEntityTableFilterComposer, + i1.$$SettingsEntityTableOrderingComposer, + i1.$$SettingsEntityTableAnnotationComposer, + $$SettingsEntityTableCreateCompanionBuilder, + $$SettingsEntityTableUpdateCompanionBuilder, ( - i1.MetadataEntityData, + i1.SettingsEntityData, i0.BaseReferences< i0.GeneratedDatabase, - i1.$MetadataEntityTable, - i1.MetadataEntityData + i1.$SettingsEntityTable, + i1.SettingsEntityData >, ), - i1.MetadataEntityData, + i1.SettingsEntityData, i0.PrefetchHooks Function() > { - $$MetadataEntityTableTableManager( + $$SettingsEntityTableTableManager( i0.GeneratedDatabase db, - i1.$MetadataEntityTable table, + i1.$SettingsEntityTable table, ) : super( i0.TableManagerState( db: db, table: table, createFilteringComposer: () => - i1.$$MetadataEntityTableFilterComposer($db: db, $table: table), + i1.$$SettingsEntityTableFilterComposer($db: db, $table: table), createOrderingComposer: () => - i1.$$MetadataEntityTableOrderingComposer($db: db, $table: table), + i1.$$SettingsEntityTableOrderingComposer($db: db, $table: table), createComputedFieldComposer: () => i1 - .$$MetadataEntityTableAnnotationComposer($db: db, $table: table), + .$$SettingsEntityTableAnnotationComposer($db: db, $table: table), updateCompanionCallback: ({ i0.Value key = const i0.Value.absent(), i0.Value value = const i0.Value.absent(), i0.Value updatedAt = const i0.Value.absent(), - }) => i1.MetadataEntityCompanion( + }) => i1.SettingsEntityCompanion( key: key, value: value, updatedAt: updatedAt, @@ -139,7 +139,7 @@ class $$MetadataEntityTableTableManager required String key, required String value, i0.Value updatedAt = const i0.Value.absent(), - }) => i1.MetadataEntityCompanion.insert( + }) => i1.SettingsEntityCompanion.insert( key: key, value: value, updatedAt: updatedAt, @@ -152,34 +152,34 @@ class $$MetadataEntityTableTableManager ); } -typedef $$MetadataEntityTableProcessedTableManager = +typedef $$SettingsEntityTableProcessedTableManager = i0.ProcessedTableManager< i0.GeneratedDatabase, - i1.$MetadataEntityTable, - i1.MetadataEntityData, - i1.$$MetadataEntityTableFilterComposer, - i1.$$MetadataEntityTableOrderingComposer, - i1.$$MetadataEntityTableAnnotationComposer, - $$MetadataEntityTableCreateCompanionBuilder, - $$MetadataEntityTableUpdateCompanionBuilder, + i1.$SettingsEntityTable, + i1.SettingsEntityData, + i1.$$SettingsEntityTableFilterComposer, + i1.$$SettingsEntityTableOrderingComposer, + i1.$$SettingsEntityTableAnnotationComposer, + $$SettingsEntityTableCreateCompanionBuilder, + $$SettingsEntityTableUpdateCompanionBuilder, ( - i1.MetadataEntityData, + i1.SettingsEntityData, i0.BaseReferences< i0.GeneratedDatabase, - i1.$MetadataEntityTable, - i1.MetadataEntityData + i1.$SettingsEntityTable, + i1.SettingsEntityData >, ), - i1.MetadataEntityData, + i1.SettingsEntityData, i0.PrefetchHooks Function() >; -class $MetadataEntityTable extends i2.MetadataEntity - with i0.TableInfo<$MetadataEntityTable, i1.MetadataEntityData> { +class $SettingsEntityTable extends i2.SettingsEntity + with i0.TableInfo<$SettingsEntityTable, i1.SettingsEntityData> { @override final i0.GeneratedDatabase attachedDatabase; final String? _alias; - $MetadataEntityTable(this.attachedDatabase, [this._alias]); + $SettingsEntityTable(this.attachedDatabase, [this._alias]); static const i0.VerificationMeta _keyMeta = const i0.VerificationMeta('key'); @override late final i0.GeneratedColumn key = i0.GeneratedColumn( @@ -219,10 +219,10 @@ class $MetadataEntityTable extends i2.MetadataEntity String get aliasedName => _alias ?? actualTableName; @override String get actualTableName => $name; - static const String $name = 'metadata'; + static const String $name = 'settings'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, { + i0.Insertable instance, { bool isInserting = false, }) { final context = i0.VerificationContext(); @@ -255,9 +255,9 @@ class $MetadataEntityTable extends i2.MetadataEntity @override Set get $primaryKey => {key}; @override - i1.MetadataEntityData map(Map data, {String? tablePrefix}) { + i1.SettingsEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; - return i1.MetadataEntityData( + return i1.SettingsEntityData( key: attachedDatabase.typeMapping.read( i0.DriftSqlType.string, data['${effectivePrefix}key'], @@ -274,8 +274,8 @@ class $MetadataEntityTable extends i2.MetadataEntity } @override - $MetadataEntityTable createAlias(String alias) { - return $MetadataEntityTable(attachedDatabase, alias); + $SettingsEntityTable createAlias(String alias) { + return $SettingsEntityTable(attachedDatabase, alias); } @override @@ -284,12 +284,12 @@ class $MetadataEntityTable extends i2.MetadataEntity bool get isStrict => true; } -class MetadataEntityData extends i0.DataClass - implements i0.Insertable { +class SettingsEntityData extends i0.DataClass + implements i0.Insertable { final String key; final String value; final DateTime updatedAt; - const MetadataEntityData({ + const SettingsEntityData({ required this.key, required this.value, required this.updatedAt, @@ -303,12 +303,12 @@ class MetadataEntityData extends i0.DataClass return map; } - factory MetadataEntityData.fromJson( + factory SettingsEntityData.fromJson( Map json, { i0.ValueSerializer? serializer, }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; - return MetadataEntityData( + return SettingsEntityData( key: serializer.fromJson(json['key']), value: serializer.fromJson(json['value']), updatedAt: serializer.fromJson(json['updatedAt']), @@ -324,17 +324,17 @@ class MetadataEntityData extends i0.DataClass }; } - i1.MetadataEntityData copyWith({ + i1.SettingsEntityData copyWith({ String? key, String? value, DateTime? updatedAt, - }) => i1.MetadataEntityData( + }) => i1.SettingsEntityData( key: key ?? this.key, value: value ?? this.value, updatedAt: updatedAt ?? this.updatedAt, ); - MetadataEntityData copyWithCompanion(i1.MetadataEntityCompanion data) { - return MetadataEntityData( + SettingsEntityData copyWithCompanion(i1.SettingsEntityCompanion data) { + return SettingsEntityData( key: data.key.present ? data.key.value : this.key, value: data.value.present ? data.value.value : this.value, updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, @@ -343,7 +343,7 @@ class MetadataEntityData extends i0.DataClass @override String toString() { - return (StringBuffer('MetadataEntityData(') + return (StringBuffer('SettingsEntityData(') ..write('key: $key, ') ..write('value: $value, ') ..write('updatedAt: $updatedAt') @@ -356,29 +356,29 @@ class MetadataEntityData extends i0.DataClass @override bool operator ==(Object other) => identical(this, other) || - (other is i1.MetadataEntityData && + (other is i1.SettingsEntityData && other.key == this.key && other.value == this.value && other.updatedAt == this.updatedAt); } -class MetadataEntityCompanion - extends i0.UpdateCompanion { +class SettingsEntityCompanion + extends i0.UpdateCompanion { final i0.Value key; final i0.Value value; final i0.Value updatedAt; - const MetadataEntityCompanion({ + const SettingsEntityCompanion({ this.key = const i0.Value.absent(), this.value = const i0.Value.absent(), this.updatedAt = const i0.Value.absent(), }); - MetadataEntityCompanion.insert({ + SettingsEntityCompanion.insert({ required String key, required String value, this.updatedAt = const i0.Value.absent(), }) : key = i0.Value(key), value = i0.Value(value); - static i0.Insertable custom({ + static i0.Insertable custom({ i0.Expression? key, i0.Expression? value, i0.Expression? updatedAt, @@ -390,12 +390,12 @@ class MetadataEntityCompanion }); } - i1.MetadataEntityCompanion copyWith({ + i1.SettingsEntityCompanion copyWith({ i0.Value? key, i0.Value? value, i0.Value? updatedAt, }) { - return i1.MetadataEntityCompanion( + return i1.SettingsEntityCompanion( key: key ?? this.key, value: value ?? this.value, updatedAt: updatedAt ?? this.updatedAt, @@ -419,7 +419,7 @@ class MetadataEntityCompanion @override String toString() { - return (StringBuffer('MetadataEntityCompanion(') + return (StringBuffer('SettingsEntityCompanion(') ..write('key: $key, ') ..write('value: $value, ') ..write('updatedAt: $updatedAt') diff --git a/mobile/lib/infrastructure/repositories/db.repository.dart b/mobile/lib/infrastructure/repositories/db.repository.dart index e81fe58ba9..6bb2e946f1 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.dart @@ -13,7 +13,7 @@ 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/entities/memory.entity.dart'; import 'package:immich_mobile/infrastructure/entities/memory_asset.entity.dart'; -import 'package:immich_mobile/infrastructure/entities/metadata.entity.dart'; +import 'package:immich_mobile/infrastructure/entities/settings.entity.dart'; import 'package:immich_mobile/infrastructure/entities/partner.entity.dart'; import 'package:immich_mobile/infrastructure/entities/person.entity.dart'; import 'package:immich_mobile/infrastructure/entities/remote_album.entity.dart'; @@ -55,7 +55,7 @@ import 'package:logging/logging.dart'; StoreEntity, TrashedLocalAssetEntity, AssetEditEntity, - MetadataEntity, + SettingsEntity, ], include: {'package:immich_mobile/infrastructure/entities/merged_asset.drift'}, ) @@ -98,7 +98,7 @@ class Drift extends $Drift { } @override - int get schemaVersion => 26; + int get schemaVersion => 27; @override MigrationStrategy get migration => MigrationStrategy( @@ -276,6 +276,9 @@ class Drift extends $Drift { from25To26: (m, v26) async { await m.addColumn(v26.remoteAssetEntity, v26.remoteAssetEntity.uploadedAt); }, + from26To27: (m, v27) async { + await customStatement('ALTER TABLE metadata RENAME TO settings'); + }, ), ); diff --git a/mobile/lib/infrastructure/repositories/db.repository.drift.dart b/mobile/lib/infrastructure/repositories/db.repository.drift.dart index c43a83f86a..692523219b 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.drift.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.drift.dart @@ -43,7 +43,7 @@ import 'package:immich_mobile/infrastructure/entities/trashed_local_asset.entity as i20; import 'package:immich_mobile/infrastructure/entities/asset_edit.entity.drift.dart' as i21; -import 'package:immich_mobile/infrastructure/entities/metadata.entity.drift.dart' +import 'package:immich_mobile/infrastructure/entities/settings.entity.drift.dart' as i22; import 'package:immich_mobile/infrastructure/entities/merged_asset.drift.dart' as i23; @@ -91,7 +91,7 @@ abstract class $Drift extends i0.GeneratedDatabase { .$TrashedLocalAssetEntityTable(this); late final i21.$AssetEditEntityTable assetEditEntity = i21 .$AssetEditEntityTable(this); - late final i22.$MetadataEntityTable metadataEntity = i22.$MetadataEntityTable( + late final i22.$SettingsEntityTable settingsEntity = i22.$SettingsEntityTable( this, ); i23.MergedAssetDrift get mergedAssetDrift => i24.ReadDatabaseContainer( @@ -132,7 +132,7 @@ abstract class $Drift extends i0.GeneratedDatabase { storeEntity, trashedLocalAssetEntity, assetEditEntity, - metadataEntity, + settingsEntity, i10.idxPartnerSharedWithId, i11.idxLatLng, i11.idxRemoteExifCity, @@ -395,6 +395,6 @@ class $DriftManager { ); i21.$$AssetEditEntityTableTableManager get assetEditEntity => i21.$$AssetEditEntityTableTableManager(_db, _db.assetEditEntity); - i22.$$MetadataEntityTableTableManager get metadataEntity => - i22.$$MetadataEntityTableTableManager(_db, _db.metadataEntity); + i22.$$SettingsEntityTableTableManager get settingsEntity => + i22.$$SettingsEntityTableTableManager(_db, _db.settingsEntity); } diff --git a/mobile/lib/infrastructure/repositories/db.repository.steps.dart b/mobile/lib/infrastructure/repositories/db.repository.steps.dart index 1fb88de1d0..a51174d980 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.steps.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.steps.dart @@ -13539,6 +13539,550 @@ i1.GeneratedColumn _column_212(String aliasedName) => type: i1.DriftSqlType.string, $customConstraints: 'NULL', ); + +final class Schema27 extends i0.VersionedSchema { + Schema27({required super.database}) : super(version: 27); + @override + late final List entities = [ + userEntity, + remoteAssetEntity, + stackEntity, + localAssetEntity, + remoteAlbumEntity, + localAlbumEntity, + localAlbumAssetEntity, + idxLocalAlbumAssetAlbumAsset, + idxLocalAssetChecksum, + idxLocalAssetCloudId, + idxStackPrimaryAssetId, + uQRemoteAssetsOwnerChecksum, + uQRemoteAssetsOwnerLibraryChecksum, + idxRemoteAssetChecksum, + idxRemoteAssetStackId, + idxRemoteAssetOwnerVisibilityDeletedCreated, + authUserEntity, + userMetadataEntity, + partnerEntity, + remoteExifEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + remoteAssetCloudIdEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + assetFaceEntity, + storeEntity, + trashedLocalAssetEntity, + assetEditEntity, + settings, + idxPartnerSharedWithId, + idxLatLng, + idxRemoteExifCity, + idxRemoteAlbumAssetAlbumAsset, + idxRemoteAssetCloudId, + idxPersonOwnerId, + idxAssetFacePersonId, + idxAssetFaceAssetId, + idxAssetFaceVisiblePerson, + idxTrashedLocalAssetChecksum, + idxTrashedLocalAssetAlbum, + idxAssetEditAssetId, + ]; + late final Shape33 userEntity = Shape33( + source: i0.VersionedTable( + entityName: 'user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_107, + _column_108, + _column_109, + _column_110, + _column_111, + _column_112, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape50 remoteAssetEntity = Shape50( + source: i0.VersionedTable( + entityName: 'remote_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_108, + _column_113, + _column_114, + _column_115, + _column_116, + _column_117, + _column_118, + _column_107, + _column_119, + _column_120, + _column_121, + _column_122, + _column_123, + _column_124, + _column_212, + _column_125, + _column_126, + _column_127, + _column_128, + _column_129, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape35 stackEntity = Shape35( + source: i0.VersionedTable( + entityName: 'stack_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_107, + _column_114, + _column_115, + _column_121, + _column_130, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape36 localAssetEntity = Shape36( + source: i0.VersionedTable( + entityName: 'local_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_108, + _column_113, + _column_114, + _column_115, + _column_116, + _column_117, + _column_118, + _column_107, + _column_131, + _column_120, + _column_132, + _column_133, + _column_134, + _column_135, + _column_136, + _column_137, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape48 remoteAlbumEntity = Shape48( + source: i0.VersionedTable( + entityName: 'remote_album_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_107, + _column_108, + _column_138, + _column_114, + _column_115, + _column_139, + _column_140, + _column_141, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape38 localAlbumEntity = Shape38( + source: i0.VersionedTable( + entityName: 'local_album_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_107, + _column_108, + _column_115, + _column_142, + _column_143, + _column_144, + _column_145, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape39 localAlbumAssetEntity = Shape39( + source: i0.VersionedTable( + entityName: 'local_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, album_id)'], + columns: [_column_146, _column_147, _column_145], + attachedDatabase: database, + ), + alias: null, + ); + final i1.Index idxLocalAlbumAssetAlbumAsset = i1.Index( + 'idx_local_album_asset_album_asset', + 'CREATE INDEX IF NOT EXISTS idx_local_album_asset_album_asset ON local_album_asset_entity (album_id, asset_id)', + ); + 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 idxLocalAssetCloudId = i1.Index( + 'idx_local_asset_cloud_id', + 'CREATE INDEX IF NOT EXISTS idx_local_asset_cloud_id ON local_asset_entity (i_cloud_id)', + ); + final i1.Index idxStackPrimaryAssetId = i1.Index( + 'idx_stack_primary_asset_id', + 'CREATE INDEX IF NOT EXISTS idx_stack_primary_asset_id ON stack_entity (primary_asset_id)', + ); + 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)', + ); + final i1.Index idxRemoteAssetStackId = i1.Index( + 'idx_remote_asset_stack_id', + 'CREATE INDEX IF NOT EXISTS idx_remote_asset_stack_id ON remote_asset_entity (stack_id)', + ); + final i1.Index idxRemoteAssetOwnerVisibilityDeletedCreated = i1.Index( + 'idx_remote_asset_owner_visibility_deleted_created', + 'CREATE INDEX IF NOT EXISTS idx_remote_asset_owner_visibility_deleted_created ON remote_asset_entity (owner_id, visibility, deleted_at, created_at DESC)', + ); + late final Shape40 authUserEntity = Shape40( + source: i0.VersionedTable( + entityName: 'auth_user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_107, + _column_108, + _column_109, + _column_148, + _column_110, + _column_111, + _column_149, + _column_150, + _column_151, + _column_152, + ], + 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_153, _column_154, _column_155], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape41 partnerEntity = Shape41( + source: i0.VersionedTable( + entityName: 'partner_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(shared_by_id, shared_with_id)'], + columns: [_column_156, _column_157, _column_158], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape42 remoteExifEntity = Shape42( + source: i0.VersionedTable( + entityName: 'remote_exif_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id)'], + columns: [ + _column_159, + _column_160, + _column_161, + _column_162, + _column_163, + _column_164, + _column_117, + _column_116, + _column_165, + _column_166, + _column_167, + _column_168, + _column_135, + _column_136, + _column_169, + _column_170, + _column_171, + _column_172, + _column_173, + _column_174, + _column_175, + _column_176, + ], + 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_159, _column_177], + 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_177, _column_153, _column_178], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape43 remoteAssetCloudIdEntity = Shape43( + source: i0.VersionedTable( + entityName: 'remote_asset_cloud_id_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id)'], + columns: [ + _column_159, + _column_179, + _column_180, + _column_134, + _column_135, + _column_136, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape44 memoryEntity = Shape44( + source: i0.VersionedTable( + entityName: 'memory_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_107, + _column_114, + _column_115, + _column_124, + _column_121, + _column_113, + _column_181, + _column_182, + _column_183, + _column_184, + _column_185, + _column_186, + ], + 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_159, _column_187], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape45 personEntity = Shape45( + source: i0.VersionedTable( + entityName: 'person_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_107, + _column_114, + _column_115, + _column_121, + _column_108, + _column_188, + _column_189, + _column_190, + _column_191, + _column_192, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape46 assetFaceEntity = Shape46( + source: i0.VersionedTable( + entityName: 'asset_face_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_107, + _column_159, + _column_193, + _column_194, + _column_195, + _column_196, + _column_197, + _column_198, + _column_199, + _column_200, + _column_201, + _column_124, + ], + 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_202, _column_203, _column_204], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape47 trashedLocalAssetEntity = Shape47( + source: i0.VersionedTable( + entityName: 'trashed_local_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id, album_id)'], + columns: [ + _column_108, + _column_113, + _column_114, + _column_115, + _column_116, + _column_117, + _column_118, + _column_107, + _column_205, + _column_131, + _column_120, + _column_132, + _column_206, + _column_137, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape32 assetEditEntity = Shape32( + source: i0.VersionedTable( + entityName: 'asset_edit_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_107, + _column_159, + _column_207, + _column_208, + _column_209, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape49 settings = Shape49( + source: i0.VersionedTable( + entityName: 'settings', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY("key")'], + columns: [_column_210, _column_211, _column_115], + attachedDatabase: database, + ), + alias: null, + ); + final i1.Index idxPartnerSharedWithId = i1.Index( + 'idx_partner_shared_with_id', + 'CREATE INDEX IF NOT EXISTS idx_partner_shared_with_id ON partner_entity (shared_with_id)', + ); + final i1.Index idxLatLng = i1.Index( + 'idx_lat_lng', + 'CREATE INDEX IF NOT EXISTS idx_lat_lng ON remote_exif_entity (latitude, longitude)', + ); + final i1.Index idxRemoteExifCity = i1.Index( + 'idx_remote_exif_city', + 'CREATE INDEX IF NOT EXISTS idx_remote_exif_city ON remote_exif_entity (city) WHERE city IS NOT NULL', + ); + final i1.Index idxRemoteAlbumAssetAlbumAsset = i1.Index( + 'idx_remote_album_asset_album_asset', + 'CREATE INDEX IF NOT EXISTS idx_remote_album_asset_album_asset ON remote_album_asset_entity (album_id, asset_id)', + ); + final i1.Index idxRemoteAssetCloudId = i1.Index( + 'idx_remote_asset_cloud_id', + 'CREATE INDEX IF NOT EXISTS idx_remote_asset_cloud_id ON remote_asset_cloud_id_entity (cloud_id)', + ); + final i1.Index idxPersonOwnerId = i1.Index( + 'idx_person_owner_id', + 'CREATE INDEX IF NOT EXISTS idx_person_owner_id ON person_entity (owner_id)', + ); + final i1.Index idxAssetFacePersonId = i1.Index( + 'idx_asset_face_person_id', + 'CREATE INDEX IF NOT EXISTS idx_asset_face_person_id ON asset_face_entity (person_id)', + ); + final i1.Index idxAssetFaceAssetId = i1.Index( + 'idx_asset_face_asset_id', + 'CREATE INDEX IF NOT EXISTS idx_asset_face_asset_id ON asset_face_entity (asset_id)', + ); + final i1.Index idxAssetFaceVisiblePerson = i1.Index( + 'idx_asset_face_visible_person', + 'CREATE INDEX IF NOT EXISTS idx_asset_face_visible_person ON asset_face_entity (person_id, asset_id) WHERE is_visible = 1 AND deleted_at IS NULL', + ); + final i1.Index idxTrashedLocalAssetChecksum = i1.Index( + 'idx_trashed_local_asset_checksum', + 'CREATE INDEX IF NOT EXISTS idx_trashed_local_asset_checksum ON trashed_local_asset_entity (checksum)', + ); + final i1.Index idxTrashedLocalAssetAlbum = i1.Index( + 'idx_trashed_local_asset_album', + 'CREATE INDEX IF NOT EXISTS idx_trashed_local_asset_album ON trashed_local_asset_entity (album_id)', + ); + final i1.Index idxAssetEditAssetId = i1.Index( + 'idx_asset_edit_asset_id', + 'CREATE INDEX IF NOT EXISTS idx_asset_edit_asset_id ON asset_edit_entity (asset_id)', + ); +} + i0.MigrationStepWithVersion migrationSteps({ required Future Function(i1.Migrator m, Schema2 schema) from1To2, required Future Function(i1.Migrator m, Schema3 schema) from2To3, @@ -13565,6 +14109,7 @@ i0.MigrationStepWithVersion migrationSteps({ required Future Function(i1.Migrator m, Schema24 schema) from23To24, required Future Function(i1.Migrator m, Schema25 schema) from24To25, required Future Function(i1.Migrator m, Schema26 schema) from25To26, + required Future Function(i1.Migrator m, Schema27 schema) from26To27, }) { return (currentVersion, database) async { switch (currentVersion) { @@ -13693,6 +14238,11 @@ i0.MigrationStepWithVersion migrationSteps({ final migrator = i1.Migrator(database, schema); await from25To26(migrator, schema); return 26; + case 26: + final schema = Schema27(database: database); + final migrator = i1.Migrator(database, schema); + await from26To27(migrator, schema); + return 27; default: throw ArgumentError.value('Unknown migration from $currentVersion'); } @@ -13725,6 +14275,7 @@ i1.OnUpgrade stepByStep({ required Future Function(i1.Migrator m, Schema24 schema) from23To24, required Future Function(i1.Migrator m, Schema25 schema) from24To25, required Future Function(i1.Migrator m, Schema26 schema) from25To26, + required Future Function(i1.Migrator m, Schema27 schema) from26To27, }) => i0.VersionedSchema.stepByStepHelper( step: migrationSteps( from1To2: from1To2, @@ -13752,5 +14303,6 @@ i1.OnUpgrade stepByStep({ from23To24: from23To24, from24To25: from24To25, from25To26: from25To26, + from26To27: from26To27, ), ); diff --git a/mobile/lib/infrastructure/repositories/metadata.repository.dart b/mobile/lib/infrastructure/repositories/metadata.repository.dart deleted file mode 100644 index d43f67fe87..0000000000 --- a/mobile/lib/infrastructure/repositories/metadata.repository.dart +++ /dev/null @@ -1,72 +0,0 @@ -import 'package:collection/collection.dart'; -import 'package:drift/drift.dart'; -import 'package:immich_mobile/domain/models/config/app_config.dart'; -import 'package:immich_mobile/domain/models/metadata_key.dart'; -import 'package:immich_mobile/infrastructure/entities/metadata.entity.drift.dart'; -import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; - -class MetadataRepository extends DriftDatabaseRepository { - final Drift _db; - - MetadataRepository._(this._db) : super(_db); - - static MetadataRepository? _instance; - - static MetadataRepository get instance { - final instance = _instance; - if (instance == null) { - throw StateError('MetadataRepository not initialized. Call ensureInitialized() first'); - } - return instance; - } - - AppConfig _appConfig = const .new(); - AppConfig get appConfig => _appConfig; - - static Future ensureInitialized(Drift db) async { - if (_instance == null) { - final instance = MetadataRepository._(db); - await instance.refresh(); - _instance = instance; - } - return _instance!; - } - - Future refresh() async => _applyOverrides(await _db.select(_db.metadataEntity).get()); - - Future write(MetadataKey key, U value) async { - if (value == _appConfig.read(key)) { - return; - } - - if (value == defaultConfig.read(key)) { - await (_db.delete(_db.metadataEntity)..where((t) => t.key.equals(key.name))).go(); - } else { - await _db - .into(_db.metadataEntity) - .insertOnConflictUpdate( - MetadataEntityCompanion.insert(key: key.name, value: key.encode(value), updatedAt: Value(DateTime.now())), - ); - } - - _appConfig = _appConfig.write(key, value); - } - - Stream watchConfig() => _db.select(_db.metadataEntity).watch().map((rows) { - _applyOverrides(rows); - return _appConfig; - }); - - void _applyOverrides(List rows) { - _appConfig = AppConfig.fromEntries( - rows.fold({}, (overrides, row) { - final metadataKey = MetadataKey.values.firstWhereOrNull((key) => key.name == row.key); - if (metadataKey == null) { - return overrides; - } - - return {...overrides, metadataKey: metadataKey.decode(row.value)}; - }), - ); - } -} diff --git a/mobile/lib/infrastructure/repositories/settings.repository.dart b/mobile/lib/infrastructure/repositories/settings.repository.dart new file mode 100644 index 0000000000..56066f543a --- /dev/null +++ b/mobile/lib/infrastructure/repositories/settings.repository.dart @@ -0,0 +1,84 @@ +import 'package:collection/collection.dart'; +import 'package:drift/drift.dart'; +import 'package:immich_mobile/domain/models/config/app_config.dart'; +import 'package:immich_mobile/domain/models/settings_key.dart'; +import 'package:immich_mobile/infrastructure/entities/settings.entity.drift.dart'; +import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; + +class SettingsRepository extends DriftDatabaseRepository { + final Drift _db; + + SettingsRepository._(this._db) : super(_db); + + static SettingsRepository? _instance; + + static SettingsRepository get instance { + final instance = _instance; + if (instance == null) { + throw StateError('SettingsRepository not initialized. Call ensureInitialized() first'); + } + return instance; + } + + AppConfig _appConfig = const .new(); + AppConfig get appConfig => _appConfig; + + static Future ensureInitialized(Drift db) async { + if (_instance == null) { + final instance = SettingsRepository._(db); + await instance.refresh(); + _instance = instance; + } + return _instance!; + } + + Future refresh() async => _applyOverrides(await _db.select(_db.settingsEntity).get()); + + Future clear(Iterable keys) async { + if (keys.isEmpty) { + return; + } + + final names = keys.map((key) => key.name).toList(); + await (_db.delete(_db.settingsEntity)..where((row) => row.key.isIn(names))).go(); + + for (final key in keys) { + _appConfig = _appConfig.write(key, defaultConfig.read(key)); + } + } + + Future write(SettingsKey key, U value) async { + if (value == _appConfig.read(key)) { + return; + } + + if (value == defaultConfig.read(key)) { + return clear([key]); + } + + await _db + .into(_db.settingsEntity) + .insertOnConflictUpdate( + SettingsEntityCompanion.insert(key: key.name, value: key.encode(value), updatedAt: Value(DateTime.now())), + ); + _appConfig = _appConfig.write(key, value); + } + + Stream watchConfig() => _db.select(_db.settingsEntity).watch().map((rows) { + _applyOverrides(rows); + return _appConfig; + }); + + void _applyOverrides(List rows) { + _appConfig = AppConfig.fromEntries( + rows.fold({}, (overrides, row) { + final metadataKey = SettingsKey.values.firstWhereOrNull((key) => key.name == row.key); + if (metadataKey == null) { + return overrides; + } + + return {...overrides, metadataKey: metadataKey.decode(row.value)}; + }), + ); + } +} diff --git a/mobile/lib/main.dart b/mobile/lib/main.dart index 19455be61c..cc5f131572 100644 --- a/mobile/lib/main.dart +++ b/mobile/lib/main.dart @@ -25,7 +25,7 @@ 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/infrastructure/db.provider.dart'; -import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/settings.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'; diff --git a/mobile/lib/models/server_info/server_version.model.dart b/mobile/lib/models/server_info/server_version.model.dart index c8bf73db81..40f35a3cd0 100644 --- a/mobile/lib/models/server_info/server_version.model.dart +++ b/mobile/lib/models/server_info/server_version.model.dart @@ -2,16 +2,12 @@ import 'package:immich_mobile/utils/semver.dart'; import 'package:openapi/api.dart'; class ServerVersion extends SemVer { - const ServerVersion({required super.major, required super.minor, required super.patch}); + const ServerVersion({required super.major, required super.minor, required super.patch, super.prerelease}); - @override - String toString() { - return 'ServerVersion(major: $major, minor: $minor, patch: $patch)'; - } + ServerVersion.fromDto(ServerVersionResponseDto dto) + : super(major: dto.major, minor: dto.minor, patch: dto.patch_, prerelease: dto.prerelease); - ServerVersion.fromDto(ServerVersionResponseDto dto) : super(major: dto.major, minor: dto.minor, patch: dto.patch_); - - bool isAtLeast({int major = 0, int minor = 0, int patch = 0}) { - return this >= SemVer(major: major, minor: minor, patch: patch); + bool isAtLeast({int major = 0, int minor = 0, int patch = 0, int? prerelease}) { + return this >= SemVer(major: major, minor: minor, patch: patch, prerelease: prerelease); } } 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 c9398febc6..f999e7671f 100644 --- a/mobile/lib/pages/backup/drift_backup_album_selection.page.dart +++ b/mobile/lib/pages/backup/drift_backup_album_selection.page.dart @@ -8,11 +8,11 @@ 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/infrastructure/repositories/metadata.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/settings.repository.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/metadata.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/settings.provider.dart'; import 'package:immich_mobile/providers/infrastructure/platform.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/widgets/backup/drift_album_info_list_tile.dart'; @@ -103,7 +103,7 @@ class _DriftBackupAlbumSelectionPageState extends ConsumerState p.totalCount)); final totalChanged = currentTotalAssetCount != _initialTotalAssetCount; diff --git a/mobile/lib/pages/backup/drift_backup_options.page.dart b/mobile/lib/pages/backup/drift_backup_options.page.dart index ee26d0bf87..42643a0496 100644 --- a/mobile/lib/pages/backup/drift_backup_options.page.dart +++ b/mobile/lib/pages/backup/drift_backup_options.page.dart @@ -4,10 +4,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/infrastructure/repositories/metadata.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/settings.repository.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/infrastructure/metadata.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/settings.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/widgets/settings/backup_settings/drift_backup_settings.dart'; import 'package:logging/logging.dart'; @@ -27,7 +27,7 @@ class DriftBackupOptionsPage extends ConsumerWidget { // There is an issue with Flutter where the pop event // can be triggered multiple times, so we guard it with _hasPopped - final currentBackup = ref.read(metadataProvider).appConfig.backup; + final currentBackup = ref.read(appConfigProvider).backup; final currentCellularForVideos = currentBackup.useCellularForVideos; final currentCellularForPhotos = currentBackup.useCellularForPhotos; @@ -45,7 +45,7 @@ class DriftBackupOptionsPage extends ConsumerWidget { } await ref.read(driftBackupProvider.notifier).getBackupStatus(currentUser.id); - final isBackupEnabled = MetadataRepository.instance.appConfig.backup.enabled; + final isBackupEnabled = SettingsRepository.instance.appConfig.backup.enabled; if (!isBackupEnabled) { return; } diff --git a/mobile/lib/pages/common/headers_settings.page.dart b/mobile/lib/pages/common/headers_settings.page.dart index d342add5af..9a6b602b04 100644 --- a/mobile/lib/pages/common/headers_settings.page.dart +++ b/mobile/lib/pages/common/headers_settings.page.dart @@ -3,10 +3,9 @@ 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/metadata_key.dart'; import 'package:immich_mobile/generated/translations.g.dart'; import 'package:immich_mobile/providers/api.provider.dart'; -import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/settings.provider.dart'; class SettingsHeader { String key = ""; @@ -22,7 +21,7 @@ class HeaderSettingsPage extends HookConsumerWidget { final headers = useState>([]); final setInitialHeaders = useState(false); - final storedHeaders = ref.read(metadataProvider).appConfig.network.customHeaders; + final storedHeaders = ref.read(appConfigProvider).network.customHeaders; if (!setInitialHeaders.value) { storedHeaders.forEach((k, v) { final header = SettingsHeader(); @@ -94,7 +93,7 @@ class HeaderSettingsPage extends HookConsumerWidget { headersMap[key] = value; } - await ref.read(metadataProvider).write(MetadataKey.networkCustomHeaders, headersMap); + await ref.read(settingsProvider).write(.networkCustomHeaders, headersMap); await ref.read(apiServiceProvider).updateHeaders(); } } diff --git a/mobile/lib/pages/common/splash_screen.page.dart b/mobile/lib/pages/common/splash_screen.page.dart index 36fd9f5bf0..aaa9fffc05 100644 --- a/mobile/lib/pages/common/splash_screen.page.dart +++ b/mobile/lib/pages/common/splash_screen.page.dart @@ -12,7 +12,7 @@ import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/generated/codegen_loader.g.dart'; import 'package:immich_mobile/generated/translations.g.dart'; -import 'package:immich_mobile/infrastructure/repositories/metadata.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/settings.repository.dart'; import 'package:immich_mobile/providers/auth.provider.dart'; import 'package:immich_mobile/providers/background_sync.provider.dart'; import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; @@ -341,7 +341,7 @@ class SplashScreenPageState extends ConsumerState { await backgroundManager.hashAssets(); } - if (MetadataRepository.instance.appConfig.backup.syncAlbums) { + if (SettingsRepository.instance.appConfig.backup.syncAlbums) { await backgroundManager.syncLinkedAlbum(); } } catch (e) { @@ -370,7 +370,7 @@ class SplashScreenPageState extends ConsumerState { } Future _resumeBackup(DriftBackupNotifier notifier) async { - final isEnableBackup = MetadataRepository.instance.appConfig.backup.enabled; + final isEnableBackup = SettingsRepository.instance.appConfig.backup.enabled; if (isEnableBackup) { final currentUser = Store.tryGet(StoreKey.currentUser); diff --git a/mobile/lib/presentation/pages/drift_slideshow.page.dart b/mobile/lib/presentation/pages/drift_slideshow.page.dart index 4c4ee48cf9..3a5f95554c 100644 --- a/mobile/lib/presentation/pages/drift_slideshow.page.dart +++ b/mobile/lib/presentation/pages/drift_slideshow.page.dart @@ -17,7 +17,7 @@ import 'package:immich_mobile/presentation/widgets/asset_viewer/video_viewer.wid import 'package:immich_mobile/presentation/widgets/images/image_provider.dart'; import 'package:immich_mobile/providers/asset_viewer/asset_viewer.provider.dart'; import 'package:immich_mobile/providers/asset_viewer/video_player_provider.dart'; -import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/settings.provider.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/widgets/common/immich_loading_indicator.dart'; import 'package:immich_mobile/widgets/photo_view/photo_view.dart'; diff --git a/mobile/lib/presentation/widgets/action_buttons/add_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/add_action_button.widget.dart index ecfe4a60fe..1ab3f2039d 100644 --- a/mobile/lib/presentation/widgets/action_buttons/add_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/add_action_button.widget.dart @@ -6,6 +6,7 @@ import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_bu import 'package:immich_mobile/presentation/widgets/action_buttons/unarchive_action_button.widget.dart'; import 'package:immich_mobile/providers/asset_viewer/asset_viewer.provider.dart'; import 'package:immich_mobile/presentation/widgets/album/album_selector.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; import 'package:immich_mobile/providers/routes.provider.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; @@ -142,13 +143,18 @@ class _AddActionButtonState extends ConsumerState { return; } - final addedCount = await ref.read(remoteAlbumProvider.notifier).addAssets(album.id, [latest.remoteId!]); + final result = await ref.read(actionProvider.notifier).addToAlbum(ActionSource.viewer, album); if (!context.mounted) { return; } - if (addedCount == 0) { + if (!result.success) { + ImmichToast.show(context: context, msg: 'scaffold_body_error_occurred'.tr(), toastType: ToastType.error); + return; + } + + if (result.count == 0) { ImmichToast.show( context: context, msg: 'add_to_album_bottom_sheet_already_exists'.tr(namedArgs: {'album': album.name}), @@ -159,7 +165,7 @@ class _AddActionButtonState extends ConsumerState { msg: 'add_to_album_bottom_sheet_added'.tr(namedArgs: {'album': album.name}), ); - // Invalidate using the asset's remote ID to refresh the "Appears in" list + // Refresh the "Appears in" list on the asset's info panel. ref.invalidate(albumsContainingAssetProvider(latest.remoteId!)); } diff --git a/mobile/lib/presentation/widgets/album/album_selector.widget.dart b/mobile/lib/presentation/widgets/album/album_selector.widget.dart index 6241623978..5a174bfc5e 100644 --- a/mobile/lib/presentation/widgets/album/album_selector.widget.dart +++ b/mobile/lib/presentation/widgets/album/album_selector.widget.dart @@ -7,7 +7,6 @@ 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/build_context_extensions.dart'; import 'package:immich_mobile/extensions/theme_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; @@ -15,12 +14,11 @@ import 'package:immich_mobile/models/albums/album_search.model.dart'; import 'package:immich_mobile/presentation/widgets/album/album_tile.dart'; import 'package:immich_mobile/presentation/widgets/album/new_album_name_modal.widget.dart'; import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart'; -import 'package:immich_mobile/domain/models/metadata_key.dart'; import 'package:immich_mobile/providers/album/album_sort_by_options.provider.dart'; import 'package:immich_mobile/providers/asset_viewer/asset_viewer.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/metadata.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/settings.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'; @@ -58,7 +56,7 @@ class _AlbumSelectorState extends ConsumerState { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { - final albumConfig = ref.read(metadataProvider).appConfig.album; + final albumConfig = ref.read(appConfigProvider).album; setState(() { sort = AlbumSort(mode: albumConfig.sortMode, isReverse: albumConfig.isReverse); @@ -94,7 +92,7 @@ class _AlbumSelectorState extends ConsumerState { setState(() { isGrid = !isGrid; }); - ref.read(metadataProvider).write(MetadataKey.albumIsGrid, isGrid); + ref.read(settingsProvider).write(.albumIsGrid, isGrid); } void changeFilter(QuickFilterMode mode) { @@ -110,9 +108,9 @@ class _AlbumSelectorState extends ConsumerState { this.sort = sort; }); - final metadata = ref.read(metadataProvider); - await metadata.write(MetadataKey.albumSortMode, sort.mode); - await metadata.write(MetadataKey.albumIsReverse, sort.isReverse); + final metadata = ref.read(settingsProvider); + await metadata.write(.albumSortMode, sort.mode); + await metadata.write(.albumIsReverse, sort.isReverse); await sortAlbums(); } @@ -747,12 +745,10 @@ class AddToAlbumHeader extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { Future onCreateAlbum() async { + final selectedAssets = ref.read(multiSelectProvider).selectedAssets; final newAlbum = await ref .read(remoteAlbumProvider.notifier) - .createAlbum( - title: "Untitled Album", - assetIds: ref.read(multiSelectProvider).selectedAssets.map((e) => (e as RemoteAsset).id).toList(), - ); + .createAlbumWithAssets(title: "Untitled Album", assets: selectedAssets); if (newAlbum == null) { ImmichToast.show(context: context, toastType: ToastType.error, msg: 'errors.failed_to_create_album'.tr()); diff --git a/mobile/lib/presentation/widgets/album/pending_uploads_banner.widget.dart b/mobile/lib/presentation/widgets/album/pending_uploads_banner.widget.dart index 397170ec54..2701316e75 100644 --- a/mobile/lib/presentation/widgets/album/pending_uploads_banner.widget.dart +++ b/mobile/lib/presentation/widgets/album/pending_uploads_banner.widget.dart @@ -5,6 +5,7 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart'; import 'package:immich_mobile/providers/album/pending_album_uploads.provider.dart'; +import 'package:immich_mobile/providers/backup/asset_upload_progress.provider.dart'; /// Pinned banner sliver that surfaces in-flight album uploads directly under /// the album app bar. Renders nothing while the queue is empty. Tapping the @@ -165,6 +166,8 @@ class _PendingUploadsSheet extends ConsumerWidget { } final failedCount = pending.where((p) => p.failed).length; + final inFlightCount = pending.length - failedCount; + final canAbort = inFlightCount > 0 && ref.watch(manualUploadCancelTokenProvider) != null; return SafeArea( child: Padding( @@ -183,7 +186,21 @@ class _PendingUploadsSheet extends ConsumerWidget { style: context.textTheme.titleMedium, ), ), - if (failedCount > 0) + if (canAbort) + TextButton.icon( + onPressed: () { + final cancelToken = ref.read(manualUploadCancelTokenProvider); + if (cancelToken != null && !cancelToken.isCompleted) { + cancelToken.complete(); + } + ref.read(manualUploadCancelTokenProvider.notifier).state = null; + ref.read(pendingAlbumUploadsProvider(albumId).notifier).clear(); + }, + icon: const Icon(Icons.stop_circle_outlined, size: 18), + label: Text('cancel'.t(context: context)), + style: TextButton.styleFrom(foregroundColor: context.colorScheme.error), + ) + else if (failedCount > 0) TextButton.icon( onPressed: () => ref.read(pendingAlbumUploadsProvider(albumId).notifier).clearFailed(), icon: const Icon(Icons.clear_rounded, size: 18), diff --git a/mobile/lib/presentation/widgets/asset_viewer/asset_page.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/asset_page.widget.dart index 0afc1b781c..84edc4df65 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/asset_page.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/asset_page.widget.dart @@ -19,7 +19,7 @@ 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/asset_viewer/asset_viewer.provider.dart'; import 'package:immich_mobile/providers/asset_viewer/is_motion_video_playing.provider.dart'; -import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/settings.provider.dart'; import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; import 'package:immich_mobile/widgets/common/immich_loading_indicator.dart'; import 'package:immich_mobile/widgets/photo_view/photo_view.dart'; @@ -241,7 +241,7 @@ class _AssetPageState extends ConsumerState { return; } - final tapToNavigate = ref.read(metadataProvider).appConfig.viewer.tapToNavigate; + final tapToNavigate = ref.read(appConfigProvider).viewer.tapToNavigate; if (!tapToNavigate) { _viewer.toggleControls(); return; diff --git a/mobile/lib/presentation/widgets/asset_viewer/sheet_tile.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/sheet_tile.widget.dart index 2af68e1ff0..69e84ee03d 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/sheet_tile.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/sheet_tile.widget.dart @@ -63,16 +63,19 @@ class SheetTile extends ConsumerWidget { subtitleWidget = null; } - return ListTile( - dense: true, - visualDensity: VisualDensity.compact, - 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, + return Material( + type: MaterialType.transparency, + child: ListTile( + dense: true, + visualDensity: VisualDensity.compact, + 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, + ), ); } } 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 97ca8ace10..c1e6fe10e6 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/video_viewer.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/video_viewer.widget.dart @@ -12,7 +12,7 @@ import 'package:immich_mobile/providers/asset_viewer/is_motion_video_playing.pro import 'package:immich_mobile/providers/asset_viewer/video_player_provider.dart'; import 'package:immich_mobile/providers/cast.provider.dart'; import 'package:immich_mobile/providers/infrastructure/asset.provider.dart'; -import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/settings.provider.dart'; import 'package:immich_mobile/services/api.service.dart'; import 'package:logging/logging.dart'; import 'package:native_video_player/native_video_player.dart'; @@ -128,7 +128,7 @@ class _NativeVideoViewerState extends ConsumerState with Widg final remoteId = (videoAsset as RemoteAsset).id; final serverEndpoint = Store.get(StoreKey.serverEndpoint); - final isOriginalVideo = ref.read(metadataProvider).appConfig.viewer.loadOriginalVideo; + final isOriginalVideo = ref.read(appConfigProvider).viewer.loadOriginalVideo; final String postfixUrl = isOriginalVideo ? 'original' : 'video/playback'; final String videoUrl = videoAsset.livePhotoVideoId != null ? '$serverEndpoint/assets/${videoAsset.livePhotoVideoId}/$postfixUrl' @@ -161,7 +161,7 @@ class _NativeVideoViewerState extends ConsumerState with Widg return; } - final autoPlayVideo = ref.read(metadataProvider).appConfig.viewer.autoPlayVideo; + final autoPlayVideo = ref.read(appConfigProvider).viewer.autoPlayVideo; if (autoPlayVideo || widget.asset.isMotionPhoto) { await _notifier.play(); } @@ -212,7 +212,7 @@ class _NativeVideoViewerState extends ConsumerState with Widg } await _notifier.load(source); - final loopVideo = ref.read(metadataProvider).appConfig.viewer.loopVideo; + final loopVideo = ref.read(appConfigProvider).viewer.loopVideo; await _notifier.setLoop(!widget.asset.isMotionPhoto && loopVideo); await _notifier.setVolume(1); } diff --git a/mobile/lib/presentation/widgets/asset_viewer/viewer_kebab_menu.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/viewer_kebab_menu.widget.dart index 5a79485daf..418d41e1f2 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/viewer_kebab_menu.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/viewer_kebab_menu.widget.dart @@ -2,7 +2,6 @@ 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/setting.model.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/providers/asset_viewer/asset_viewer.provider.dart'; import 'package:immich_mobile/providers/cast.provider.dart'; @@ -34,7 +33,7 @@ class ViewerKebabMenu extends ConsumerWidget { 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 advancedTroubleshooting = ref.watch(settingsProvider.notifier).get(.advancedTroubleshooting); final actionContext = ActionButtonContext( asset: asset, 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 708d3a9879..3bf48783ea 100644 --- a/mobile/lib/presentation/widgets/backup/backup_toggle_button.widget.dart +++ b/mobile/lib/presentation/widgets/backup/backup_toggle_button.widget.dart @@ -1,10 +1,9 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/domain/models/metadata_key.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/providers/infrastructure/metadata.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/settings.provider.dart'; class BackupToggleButton extends ConsumerStatefulWidget { final VoidCallback onStart; @@ -31,7 +30,7 @@ class BackupToggleButtonState extends ConsumerState with Sin end: 1, ).animate(CurvedAnimation(parent: _animationController, curve: Curves.easeInOut)); - _isEnabled = ref.read(metadataProvider).appConfig.backup.enabled; + _isEnabled = ref.read(appConfigProvider).backup.enabled; } @override @@ -41,7 +40,7 @@ class BackupToggleButtonState extends ConsumerState with Sin } Future _onToggle(bool value) async { - await ref.read(metadataProvider).write(MetadataKey.backupEnabled, value); + await ref.read(settingsProvider).write(.backupEnabled, value); setState(() { _isEnabled = value; 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 0bafacfe54..c3a569407a 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 @@ -3,9 +3,7 @@ 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/bulk_tag_assets_action_button.widget.dart'; @@ -25,7 +23,7 @@ import 'package:immich_mobile/presentation/widgets/action_buttons/unstack_action 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/action.provider.dart'; import 'package:immich_mobile/providers/infrastructure/setting.provider.dart'; import 'package:immich_mobile/providers/infrastructure/user_metadata.provider.dart'; import 'package:immich_mobile/providers/server_info.provider.dart'; @@ -63,37 +61,23 @@ class _GeneralBottomSheetState extends ConsumerState { userMetadataPreferencesProvider.select((value) => value.valueOrNull?.tagsEnabled ?? false), ); - Future addAssetsToAlbum(RemoteAlbum album) async { - final selectedAssets = multiselect.selectedAssets; - if (selectedAssets.isEmpty) { + Future addToAlbum(RemoteAlbum album) async { + final result = await ref.read(actionProvider.notifier).addToAlbum(ActionSource.timeline, album); + + if (!context.mounted) { 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 (!result.success) { + ImmichToast.show(context: context, msg: 'scaffold_body_error_occurred'.tr(), toastType: ToastType.error); + return; } - - 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(); + ImmichToast.show( + context: context, + msg: result.count == 0 + ? 'add_to_album_bottom_sheet_already_exists'.tr(namedArgs: {'album': album.name}) + : 'add_to_album_bottom_sheet_added'.tr(namedArgs: {'album': album.name}), + ); } Future onKeyboardExpand() { @@ -131,12 +115,10 @@ class _GeneralBottomSheetState extends ConsumerState { const DeleteLocalActionButton(source: ActionSource.timeline), if (multiselect.onlyLocal) const UploadActionButton(source: ActionSource.timeline), ], - slivers: multiselect.hasRemote - ? [ - const AddToAlbumHeader(), - AlbumSelector(onAlbumSelected: addAssetsToAlbum, onKeyboardExpanded: onKeyboardExpand), - ] - : [], + slivers: [ + const AddToAlbumHeader(), + AlbumSelector(onAlbumSelected: addToAlbum, 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 b1e87dfaea..ac8c77af03 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 @@ -1,25 +1,78 @@ +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/presentation/widgets/action_buttons/delete_local_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/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/action.provider.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; -class LocalAlbumBottomSheet extends ConsumerWidget { +class LocalAlbumBottomSheet extends ConsumerStatefulWidget { const LocalAlbumBottomSheet({super.key}); @override - Widget build(BuildContext context, WidgetRef ref) { - return const BaseBottomSheet( + ConsumerState createState() => _LocalAlbumBottomSheetState(); +} + +class _LocalAlbumBottomSheetState extends ConsumerState { + late final DraggableScrollableController sheetController; + + @override + void initState() { + super.initState(); + sheetController = DraggableScrollableController(); + } + + @override + void dispose() { + sheetController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + Future addToAlbum(RemoteAlbum album) async { + final result = await ref.read(actionProvider.notifier).addToAlbum(ActionSource.timeline, album); + + if (!context.mounted) { + return; + } + + if (!result.success) { + ImmichToast.show(context: context, msg: 'scaffold_body_error_occurred'.tr(), toastType: ToastType.error); + return; + } + + ImmichToast.show( + context: context, + msg: result.count == 0 + ? 'add_to_album_bottom_sheet_already_exists'.tr(namedArgs: {'album': album.name}) + : 'add_to_album_bottom_sheet_added'.tr(namedArgs: {'album': album.name}), + ); + } + + Future onKeyboardExpand() { + return sheetController.animateTo(0.85, duration: const Duration(milliseconds: 200), curve: Curves.easeInOut); + } + + return BaseBottomSheet( + controller: sheetController, initialChildSize: 0.25, - maxChildSize: 0.4, + maxChildSize: 0.85, shouldCloseOnMinExtent: false, - actions: [ + actions: const [ ShareActionButton(source: ActionSource.timeline), DeleteLocalActionButton(source: ActionSource.timeline), UploadActionButton(source: ActionSource.timeline), ], + slivers: [ + const AddToAlbumHeader(), + AlbumSelector(onAlbumSelected: addToAlbum, onKeyboardExpanded: onKeyboardExpand), + ], ); } } 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 6848a07bb8..6b914ed077 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,7 +2,6 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/domain/models/album/album.model.dart'; -import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/archive_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/delete_local_action_button.widget.dart'; @@ -21,7 +20,7 @@ import 'package:immich_mobile/presentation/widgets/action_buttons/trash_action_b import 'package:immich_mobile/presentation/widgets/action_buttons/unstack_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/action.provider.dart'; import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; @@ -56,29 +55,28 @@ class _RemoteAlbumBottomSheetState extends ConsumerState final isTrashEnable = ref.watch(serverInfoProvider.select((state) => state.serverFeatures.trash)); final ownsAlbum = ref.watch(currentUserProvider)?.id == widget.album.ownerId; - Future addAssetsToAlbum(RemoteAlbum album) async { - final selectedAssets = multiselect.selectedAssets; - if (selectedAssets.isEmpty) { + Future addToAlbum(RemoteAlbum album) async { + final result = await ref.read(actionProvider.notifier).addToAlbum(ActionSource.timeline, album); + + if (!context.mounted) { return; } - final addedCount = await ref - .read(remoteAlbumProvider.notifier) - .addAssets(album.id, selectedAssets.map((e) => (e as RemoteAsset).id).toList()); - - if (addedCount != selectedAssets.length) { + if (!result.success) { 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}), + msg: 'scaffold_body_error_occurred'.t(context: context), + toastType: ToastType.error, ); + return; } - ref.read(multiSelectProvider.notifier).reset(); + ImmichToast.show( + context: context, + msg: result.count == 0 + ? 'add_to_album_bottom_sheet_already_exists'.t(context: context, args: {"album": album.name}) + : 'add_to_album_bottom_sheet_added'.t(context: context, args: {"album": album.name}), + ); } Future onKeyboardExpand() { @@ -118,10 +116,7 @@ class _RemoteAlbumBottomSheetState extends ConsumerState SetAlbumCoverActionButton(source: ActionSource.timeline, albumId: widget.album.id), ], slivers: ownsAlbum - ? [ - const AddToAlbumHeader(), - AlbumSelector(onAlbumSelected: addAssetsToAlbum, onKeyboardExpanded: onKeyboardExpand), - ] + ? [const AddToAlbumHeader(), AlbumSelector(onAlbumSelected: addToAlbum, onKeyboardExpanded: onKeyboardExpand)] : null, ); } diff --git a/mobile/lib/presentation/widgets/images/image_provider.dart b/mobile/lib/presentation/widgets/images/image_provider.dart index 9364fdd091..36d9678277 100644 --- a/mobile/lib/presentation/widgets/images/image_provider.dart +++ b/mobile/lib/presentation/widgets/images/image_provider.dart @@ -4,7 +4,7 @@ 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/infrastructure/loaders/image_request.dart'; -import 'package:immich_mobile/infrastructure/repositories/metadata.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/settings.repository.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'; @@ -189,5 +189,5 @@ ImageProvider? getThumbnailImageProvider(BaseAsset asset, {Size size = kThumbnai bool _shouldUseLocalAsset(BaseAsset asset) => asset.hasLocal && - (!asset.hasRemote || !MetadataRepository.instance.appConfig.image.preferRemote) && + (!asset.hasRemote || !SettingsRepository.instance.appConfig.image.preferRemote) && !asset.isEdited; diff --git a/mobile/lib/presentation/widgets/images/local_image_provider.dart b/mobile/lib/presentation/widgets/images/local_image_provider.dart index 6376e07405..eba4f0a1cd 100644 --- a/mobile/lib/presentation/widgets/images/local_image_provider.dart +++ b/mobile/lib/presentation/widgets/images/local_image_provider.dart @@ -2,7 +2,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/infrastructure/loaders/image_request.dart'; -import 'package:immich_mobile/infrastructure/repositories/metadata.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/settings.repository.dart'; import 'package:immich_mobile/presentation/widgets/images/animated_image_stream_completer.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'; @@ -104,7 +104,7 @@ class LocalFullImageProvider extends CancellableImageProvider { } void switchFavoriteOnly(bool isFavoriteOnly) { - ref.read(metadataProvider).write(MetadataKey.mapShowFavoriteOnly, isFavoriteOnly); + ref.read(settingsProvider).write(.mapShowFavoriteOnly, isFavoriteOnly); state = state.copyWith(onlyFavorites: isFavoriteOnly); EventStream.shared.emit(const MapMarkerReloadEvent()); } void switchIncludeArchived(bool isIncludeArchived) { - ref.read(metadataProvider).write(MetadataKey.mapIncludeArchived, isIncludeArchived); + ref.read(settingsProvider).write(.mapIncludeArchived, isIncludeArchived); state = state.copyWith(includeArchived: isIncludeArchived); EventStream.shared.emit(const MapMarkerReloadEvent()); } void switchWithPartners(bool isWithPartners) { - ref.read(metadataProvider).write(MetadataKey.mapWithPartners, isWithPartners); + ref.read(settingsProvider).write(.mapWithPartners, isWithPartners); state = state.copyWith(withPartners: isWithPartners); EventStream.shared.emit(const MapMarkerReloadEvent()); } void setRelativeTime(int relativeDays) { - ref.read(metadataProvider).write(MetadataKey.mapRelativeDate, relativeDays); + ref.read(settingsProvider).write(.mapRelativeDate, relativeDays); state = state.copyWith(relativeDays: relativeDays); EventStream.shared.emit(const MapMarkerReloadEvent()); } diff --git a/mobile/lib/presentation/widgets/timeline/timeline.state.dart b/mobile/lib/presentation/widgets/timeline/timeline.state.dart index 7b88800f22..8dd87f9868 100644 --- a/mobile/lib/presentation/widgets/timeline/timeline.state.dart +++ b/mobile/lib/presentation/widgets/timeline/timeline.state.dart @@ -5,7 +5,7 @@ import 'package:immich_mobile/domain/models/timeline.model.dart'; import 'package:immich_mobile/presentation/widgets/timeline/constants.dart'; import 'package:immich_mobile/presentation/widgets/timeline/fixed/segment_builder.dart'; import 'package:immich_mobile/presentation/widgets/timeline/segment.model.dart'; -import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/settings.provider.dart'; import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; class TimelineArgs { diff --git a/mobile/lib/presentation/widgets/timeline/timeline.widget.dart b/mobile/lib/presentation/widgets/timeline/timeline.widget.dart index de52c047a2..eb7a31ac8b 100644 --- a/mobile/lib/presentation/widgets/timeline/timeline.widget.dart +++ b/mobile/lib/presentation/widgets/timeline/timeline.widget.dart @@ -10,7 +10,6 @@ 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/events.model.dart'; -import 'package:immich_mobile/domain/models/metadata_key.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'; @@ -22,7 +21,7 @@ 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/metadata.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/settings.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/timeline/multiselect.provider.dart'; @@ -459,7 +458,7 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> { _restoreAssetIndex = targetAssetIndex; }); - ref.read(metadataProvider).write(MetadataKey.timelineTilesPerRow, _perRow); + ref.read(settingsProvider).write(.timelineTilesPerRow, _perRow); } }; }, diff --git a/mobile/lib/providers/album/pending_album_uploads.provider.dart b/mobile/lib/providers/album/pending_album_uploads.provider.dart index db857ba3c0..7b1188891b 100644 --- a/mobile/lib/providers/album/pending_album_uploads.provider.dart +++ b/mobile/lib/providers/album/pending_album_uploads.provider.dart @@ -67,6 +67,11 @@ class AlbumPendingUploadsNotifier extends AutoDisposeFamilyNotifier { await Future.delayed(const Duration(milliseconds: 500)); final backgroundManager = _ref.read(backgroundSyncProvider); - final isAlbumLinkedSyncEnable = _ref.read(metadataProvider).appConfig.backup.syncAlbums; + final isAlbumLinkedSyncEnable = _ref.read(appConfigProvider).backup.syncAlbums; try { bool syncSuccess = false; @@ -137,7 +137,7 @@ class AppLifeCycleNotifier extends StateNotifier { } Future _resumeBackup() async { - final isEnableBackup = _ref.read(metadataProvider).appConfig.backup.enabled; + final isEnableBackup = _ref.read(appConfigProvider).backup.enabled; if (isEnableBackup) { final currentUser = Store.tryGet(StoreKey.currentUser); diff --git a/mobile/lib/providers/auth.provider.dart b/mobile/lib/providers/auth.provider.dart index 0bd5783fee..8a3f503553 100644 --- a/mobile/lib/providers/auth.provider.dart +++ b/mobile/lib/providers/auth.provider.dart @@ -3,7 +3,6 @@ import 'dart:convert'; import 'package:flutter_udid/flutter_udid.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/constants.dart'; -import 'package:immich_mobile/domain/models/metadata_key.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/domain/services/user.service.dart'; @@ -11,7 +10,7 @@ import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/models/auth/auth_state.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/infrastructure/metadata.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/settings.provider.dart'; 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'; @@ -130,7 +129,7 @@ class AuthNotifier extends StateNotifier { await _apiService.updateHeaders(); final serverEndpoint = Store.get(StoreKey.serverEndpoint); - final headerMap = _ref.read(metadataProvider).appConfig.network.customHeaders; + final headerMap = _ref.read(appConfigProvider).network.customHeaders; final customHeaders = headerMap.isEmpty ? null : jsonEncode(headerMap); await _widgetService.writeCredentials(serverEndpoint, accessToken, customHeaders); @@ -179,19 +178,19 @@ class AuthNotifier extends StateNotifier { } Future saveWifiName(String wifiName) async { - await _ref.read(metadataProvider).write(MetadataKey.networkPreferredWifiName, wifiName); + await _ref.read(settingsProvider).write(.networkPreferredWifiName, wifiName); } Future saveLocalEndpoint(String url) async { - await _ref.read(metadataProvider).write(MetadataKey.networkLocalEndpoint, url); + await _ref.read(settingsProvider).write(.networkLocalEndpoint, url); } String? getSavedWifiName() { - return _ref.read(metadataProvider).appConfig.network.preferredWifiName; + return _ref.read(appConfigProvider).network.preferredWifiName; } String? getSavedLocalEndpoint() { - return _ref.read(metadataProvider).appConfig.network.localEndpoint; + return _ref.read(appConfigProvider).network.localEndpoint; } /// Returns the current server endpoint (with /api) URL from the store diff --git a/mobile/lib/providers/cleanup.provider.dart b/mobile/lib/providers/cleanup.provider.dart index e4a3d10a15..378ceb010f 100644 --- a/mobile/lib/providers/cleanup.provider.dart +++ b/mobile/lib/providers/cleanup.provider.dart @@ -1,8 +1,8 @@ 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/metadata.repository.dart'; -import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart'; +import 'package:immich_mobile/infrastructure/repositories/settings.repository.dart'; +import 'package:immich_mobile/providers/infrastructure/settings.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/services/cleanup.service.dart'; @@ -54,21 +54,21 @@ final cleanupProvider = StateNotifierProvider((re return CleanupNotifier( ref.watch(cleanupServiceProvider), ref.watch(currentUserProvider)?.id, - ref.watch(metadataProvider), + ref.watch(settingsProvider), ); }); class CleanupNotifier extends StateNotifier { final CleanupService _cleanupService; final String? _userId; - final MetadataRepository _metadataRepository; + final SettingsRepository _settingsRepository; - CleanupNotifier(this._cleanupService, this._userId, this._metadataRepository) : super(const CleanupState()) { + CleanupNotifier(this._cleanupService, this._userId, this._settingsRepository) : super(const CleanupState()) { _loadPersistedSettings(); } void _loadPersistedSettings() { - final cleanup = _metadataRepository.appConfig.cleanup; + final cleanup = _settingsRepository.appConfig.cleanup; final keepFavorites = cleanup.keepFavorites; final keepMediaType = cleanup.keepMediaType; final keepAlbumIds = cleanup.keepAlbumIds.toSet(); @@ -87,18 +87,18 @@ class CleanupNotifier extends StateNotifier { state = state.copyWith(selectedDate: date, assetsToDelete: []); if (date != null) { final daysAgo = DateTime.now().difference(date).inDays; - _metadataRepository.write(.cleanupCutoffDaysAgo, daysAgo); + _settingsRepository.write(.cleanupCutoffDaysAgo, daysAgo); } } void setKeepMediaType(AssetKeepType keepMediaType) { state = state.copyWith(keepMediaType: keepMediaType, assetsToDelete: []); - _metadataRepository.write(.cleanupKeepMediaType, keepMediaType); + _settingsRepository.write(.cleanupKeepMediaType, keepMediaType); } void setKeepFavorites(bool keepFavorites) { state = state.copyWith(keepFavorites: keepFavorites, assetsToDelete: []); - _metadataRepository.write(.cleanupKeepFavorites, keepFavorites); + _settingsRepository.write(.cleanupKeepFavorites, keepFavorites); } void toggleKeepAlbum(String albumId) { @@ -118,7 +118,7 @@ class CleanupNotifier extends StateNotifier { } void _persistExcludedAlbumIds(Set albumIds) { - _metadataRepository.write(.cleanupKeepAlbumIds, albumIds.toList()); + _settingsRepository.write(.cleanupKeepAlbumIds, albumIds.toList()); } void cleanupStaleAlbumIds(Set existingAlbumIds) { @@ -131,7 +131,7 @@ class CleanupNotifier extends StateNotifier { } void applyDefaultAlbumSelections(List<(String id, String name)> albums) { - final isInitialized = _metadataRepository.appConfig.cleanup.defaultsInitialized; + final isInitialized = _settingsRepository.appConfig.cleanup.defaultsInitialized; if (isInitialized) { return; } @@ -144,7 +144,7 @@ class CleanupNotifier extends StateNotifier { _persistExcludedAlbumIds(keepAlbumIds); } - _metadataRepository.write(.cleanupDefaultsInitialized, true); + _settingsRepository.write(.cleanupDefaultsInitialized, true); } Future scanAssets() async { diff --git a/mobile/lib/providers/infrastructure/action.provider.dart b/mobile/lib/providers/infrastructure/action.provider.dart index 452217bfd6..aa734f56b8 100644 --- a/mobile/lib/providers/infrastructure/action.provider.dart +++ b/mobile/lib/providers/infrastructure/action.provider.dart @@ -5,12 +5,15 @@ import 'package:background_downloader/background_downloader.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/asset_edit.model.dart'; import 'package:immich_mobile/domain/services/asset.service.dart'; +import 'package:immich_mobile/domain/services/remote_album.service.dart'; import 'package:immich_mobile/models/download/livephotos_medatada.model.dart'; import 'package:immich_mobile/providers/asset_viewer/asset_viewer.provider.dart'; import 'package:immich_mobile/providers/backup/asset_upload_progress.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/asset_viewer/asset.provider.dart' show assetExifProvider; import 'package:immich_mobile/providers/infrastructure/tag.provider.dart'; @@ -373,6 +376,52 @@ class ActionNotifier extends Notifier { } } + Future addToAlbum(ActionSource source, RemoteAlbum album) async { + final selected = _getAssets(source).toList(growable: false); + if (selected.isEmpty) { + return const ActionResult(count: 0, success: true); + } + + final candidates = RemoteAlbumService.categorizeCandidates(selected); + final remoteIds = candidates.remoteAssetIds; + final localAssets = candidates.localAssetsToUpload; + final albumNotifier = ref.read(remoteAlbumProvider.notifier); + + int addedRemote = 0; + if (remoteIds.isNotEmpty) { + try { + addedRemote = await albumNotifier.addAssets(album.id, remoteIds); + } catch (error, stack) { + _logger.severe('Failed to add assets to album ${album.id}', error, stack); + return ActionResult(count: 0, success: false, error: error.toString()); + } + } + + // Keep the selection available for retry if the remote add fails. Once the + // album mutation succeeds, clear timeline selection so upload overlays can render. + if (source == ActionSource.timeline) { + ref.read(multiSelectProvider.notifier).reset(); + } + + if (localAssets.isEmpty) { + return ActionResult(count: addedRemote, success: true); + } + + final uploadResult = await upload( + source, + assets: localAssets, + onAssetUploaded: (asset, remoteId) async { + await albumNotifier.linkUploadedAssetToAlbum(album.id, asset, remoteId); + }, + ); + + return ActionResult( + count: addedRemote + uploadResult.count, + success: uploadResult.success, + error: uploadResult.error, + ); + } + Future removeFromAlbum(ActionSource source, String albumId) async { final ids = _getRemoteIdsForSource(source); try { @@ -495,8 +544,16 @@ class ActionNotifier extends Notifier { } } - Future upload(ActionSource source, {List? assets}) async { + Future upload( + ActionSource source, { + List? assets, + FutureOr Function(LocalAsset asset, String remoteId)? onAssetUploaded, + }) async { final assetsToUpload = assets ?? _getAssets(source).whereType().toList(); + final assetById = {for (final a in assetsToUpload) a.id: a}; + final uploadedAssetIds = {}; + final failedAssetIds = {}; + final postUploadTasks = >[]; final progressNotifier = ref.read(assetUploadProgressProvider.notifier); final cancelToken = Completer(); @@ -518,16 +575,43 @@ class ActionNotifier extends Notifier { }, onSuccess: (localAssetId, remoteAssetId) { progressNotifier.remove(localAssetId); + uploadedAssetIds.add(localAssetId); + final asset = assetById[localAssetId]; + final callback = onAssetUploaded; + if (asset != null && callback != null) { + postUploadTasks.add( + Future.sync(() => callback(asset, remoteAssetId)).catchError((Object error, StackTrace stack) { + failedAssetIds.add(localAssetId); + progressNotifier.setError(localAssetId); + _logger.warning('Post-upload callback failed for $localAssetId', error, stack); + }), + ); + } }, onError: (localAssetId, errorMessage) { + failedAssetIds.add(localAssetId); progressNotifier.setError(localAssetId); }, ), ); - return ActionResult(count: assetsToUpload.length, success: true); + + await Future.wait(postUploadTasks); + final successCount = uploadedAssetIds.difference(failedAssetIds).length; + final isSuccess = successCount == assetsToUpload.length && failedAssetIds.isEmpty; + + return ActionResult( + count: successCount, + success: isSuccess, + error: isSuccess ? null : 'Failed to upload ${assetsToUpload.length - successCount} assets', + ); } catch (error, stack) { _logger.severe('Failed manually upload assets', error, stack); - return ActionResult(count: assetsToUpload.length, success: false, error: error.toString()); + + return ActionResult( + count: uploadedAssetIds.difference(failedAssetIds).length, + success: false, + error: error.toString(), + ); } finally { ref.read(manualUploadCancelTokenProvider.notifier).state = null; Future.delayed(const Duration(seconds: 2), () { diff --git a/mobile/lib/providers/infrastructure/remote_album.provider.dart b/mobile/lib/providers/infrastructure/remote_album.provider.dart index 73a796bd31..a4bbbae818 100644 --- a/mobile/lib/providers/infrastructure/remote_album.provider.dart +++ b/mobile/lib/providers/infrastructure/remote_album.provider.dart @@ -9,6 +9,7 @@ import 'package:immich_mobile/domain/services/remote_album.service.dart'; import 'package:immich_mobile/models/albums/album_search.model.dart'; import 'package:immich_mobile/providers/album/album_sort_by_options.provider.dart'; import 'package:immich_mobile/providers/album/pending_album_uploads.provider.dart'; +import 'package:immich_mobile/providers/backup/asset_upload_progress.provider.dart'; import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/services/foreground_upload.service.dart'; @@ -207,6 +208,22 @@ class RemoteAlbumNotifier extends Notifier { return added; } + /// Links a freshly-uploaded local asset to an album using its new remote ID, + /// upserting a placeholder remote asset row so the local DB join survives + /// until the next sync catches up. + Future linkUploadedAssetToAlbum(String albumId, LocalAsset source, String remoteId) async { + final currentUser = ref.read(currentUserProvider); + if (currentUser == null) { + throw Exception('User not logged in'); + } + + final added = await _remoteAlbumService.linkUploadedAssetToAlbum(albumId, remoteId, currentUser, source); + if (added > 0) { + await _refreshAlbumInState(albumId); + } + return added; + } + /// Adds a heterogeneous asset selection to an album. Already-remote assets /// are linked immediately; local-only assets are queued in /// [pendingAlbumUploadsProvider] (so the album page can show them with @@ -221,11 +238,18 @@ class RemoteAlbumNotifier extends Notifier { final pendingNotifier = ref.read(pendingAlbumUploadsProvider(albumId).notifier); pendingNotifier.enqueue(candidates.localAssetsToUpload); + Completer? cancelToken; + if (candidates.localAssetsToUpload.isNotEmpty) { + cancelToken = Completer(); + ref.read(manualUploadCancelTokenProvider.notifier).state = cancelToken; + } + try { final added = await _remoteAlbumService.addAssetsToAlbum( albumId: albumId, uploader: currentUser, candidates: candidates, + cancelToken: cancelToken, uploadCallbacks: UploadCallbacks( onProgress: (localAssetId, _, bytes, totalBytes) { final progress = totalBytes > 0 ? bytes / totalBytes : 0.0; @@ -245,6 +269,15 @@ class RemoteAlbumNotifier extends Notifier { } _logger.severe('Failed to add assets to album $albumId', error, stack); rethrow; + } finally { + if (cancelToken != null) { + if (cancelToken.isCompleted) { + pendingNotifier.clear(); + } + if (ref.read(manualUploadCancelTokenProvider) == cancelToken) { + ref.read(manualUploadCancelTokenProvider.notifier).state = null; + } + } } } diff --git a/mobile/lib/providers/infrastructure/metadata.provider.dart b/mobile/lib/providers/infrastructure/settings.provider.dart similarity index 59% rename from mobile/lib/providers/infrastructure/metadata.provider.dart rename to mobile/lib/providers/infrastructure/settings.provider.dart index d9e6920d62..d2b9dce1d6 100644 --- a/mobile/lib/providers/infrastructure/metadata.provider.dart +++ b/mobile/lib/providers/infrastructure/settings.provider.dart @@ -1,11 +1,11 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/config/app_config.dart'; -import 'package:immich_mobile/infrastructure/repositories/metadata.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/settings.repository.dart'; -final metadataProvider = Provider.autoDispose((_) => MetadataRepository.instance); +final settingsProvider = Provider.autoDispose((_) => SettingsRepository.instance); final appConfigProvider = Provider.autoDispose((ref) { - final repo = ref.watch(metadataProvider); + final repo = ref.watch(settingsProvider); final subscription = repo.watchConfig().listen((event) => ref.state = event); ref.onDispose(subscription.cancel); return repo.appConfig; diff --git a/mobile/lib/providers/infrastructure/timeline.provider.dart b/mobile/lib/providers/infrastructure/timeline.provider.dart index 9f2fdec519..b22c693033 100644 --- a/mobile/lib/providers/infrastructure/timeline.provider.dart +++ b/mobile/lib/providers/infrastructure/timeline.provider.dart @@ -3,7 +3,7 @@ import 'package:immich_mobile/domain/services/timeline.service.dart'; import 'package:immich_mobile/infrastructure/repositories/timeline.repository.dart'; import 'package:immich_mobile/presentation/widgets/timeline/timeline.state.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; -import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/settings.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; final timelineRepositoryProvider = Provider( @@ -29,7 +29,7 @@ final timelineServiceProvider = Provider( final timelineFactoryProvider = Provider( (ref) => TimelineFactory( timelineRepository: ref.watch(timelineRepositoryProvider), - metadataRepository: ref.watch(metadataProvider), + settingsRepository: ref.watch(settingsProvider), ), ); diff --git a/mobile/lib/providers/map/map_state.provider.dart b/mobile/lib/providers/map/map_state.provider.dart index b0a59f6a1e..b643264dca 100644 --- a/mobile/lib/providers/map/map_state.provider.dart +++ b/mobile/lib/providers/map/map_state.provider.dart @@ -1,8 +1,7 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/domain/models/metadata_key.dart'; import 'package:immich_mobile/models/map/map_state.model.dart'; -import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/settings.provider.dart'; import 'package:immich_mobile/providers/server_info.provider.dart'; final mapStateNotifierProvider = NotifierProvider(MapStateNotifier.new); @@ -27,12 +26,12 @@ class MapStateNotifier extends Notifier { } void switchTheme(ThemeMode mode) { - ref.read(metadataProvider).write(MetadataKey.mapThemeMode, mode); + ref.read(settingsProvider).write(.mapThemeMode, mode); state = state.copyWith(themeMode: mode); } void switchFavoriteOnly(bool isFavoriteOnly) { - ref.read(metadataProvider).write(MetadataKey.mapShowFavoriteOnly, isFavoriteOnly); + ref.read(settingsProvider).write(.mapShowFavoriteOnly, isFavoriteOnly); state = state.copyWith(showFavoriteOnly: isFavoriteOnly, shouldRefetchMarkers: true); } @@ -41,17 +40,17 @@ class MapStateNotifier extends Notifier { } void switchIncludeArchived(bool isIncludeArchived) { - ref.read(metadataProvider).write(MetadataKey.mapIncludeArchived, isIncludeArchived); + ref.read(settingsProvider).write(.mapIncludeArchived, isIncludeArchived); state = state.copyWith(includeArchived: isIncludeArchived, shouldRefetchMarkers: true); } void switchWithPartners(bool isWithPartners) { - ref.read(metadataProvider).write(MetadataKey.mapWithPartners, isWithPartners); + ref.read(settingsProvider).write(.mapWithPartners, isWithPartners); state = state.copyWith(withPartners: isWithPartners, shouldRefetchMarkers: true); } void setRelativeTime(int relativeTime) { - ref.read(metadataProvider).write(MetadataKey.mapRelativeDate, relativeTime); + ref.read(settingsProvider).write(.mapRelativeDate, relativeTime); state = state.copyWith(relativeTime: relativeTime, shouldRefetchMarkers: true); } } diff --git a/mobile/lib/providers/theme.provider.dart b/mobile/lib/providers/theme.provider.dart index 909b8137c1..962781586e 100644 --- a/mobile/lib/providers/theme.provider.dart +++ b/mobile/lib/providers/theme.provider.dart @@ -1,5 +1,5 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/settings.provider.dart'; import 'package:immich_mobile/theme/color_scheme.dart'; import 'package:immich_mobile/theme/dynamic_theme.dart'; import 'package:immich_mobile/theme/theme_data.dart'; diff --git a/mobile/lib/providers/websocket.provider.dart b/mobile/lib/providers/websocket.provider.dart index a53f0aaaeb..8d9bd5bfe3 100644 --- a/mobile/lib/providers/websocket.provider.dart +++ b/mobile/lib/providers/websocket.provider.dart @@ -7,7 +7,7 @@ import 'package:immich_mobile/infrastructure/repositories/network.repository.dar import 'package:immich_mobile/models/server_info/server_version.model.dart'; import 'package:immich_mobile/providers/auth.provider.dart'; import 'package:immich_mobile/providers/background_sync.provider.dart'; -import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/settings.provider.dart'; import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/utils/debounce.dart'; import 'package:immich_mobile/utils/debug_print.dart'; @@ -193,7 +193,7 @@ class WebsocketNotifier extends StateNotifier { return; } - final isSyncAlbumEnabled = _ref.read(metadataProvider).appConfig.backup.syncAlbums; + final isSyncAlbumEnabled = _ref.read(appConfigProvider).backup.syncAlbums; try { unawaited( _ref.read(backgroundSyncProvider).syncWebsocketBatchV1(_batchedAssetUploadReady.toList()).then((_) { @@ -214,7 +214,7 @@ class WebsocketNotifier extends StateNotifier { return; } - final isSyncAlbumEnabled = _ref.read(metadataProvider).appConfig.backup.syncAlbums; + final isSyncAlbumEnabled = _ref.read(appConfigProvider).backup.syncAlbums; try { unawaited( _ref.read(backgroundSyncProvider).syncWebsocketBatchV2(_batchedAssetUploadReady.toList()).then((_) { diff --git a/mobile/lib/repositories/auth.repository.dart b/mobile/lib/repositories/auth.repository.dart index 5aca8e76ac..dd9aed3a03 100644 --- a/mobile/lib/repositories/auth.repository.dart +++ b/mobile/lib/repositories/auth.repository.dart @@ -1,38 +1,40 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/domain/models/config/app_config.dart'; import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/settings.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/infrastructure/db.provider.dart'; -import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/settings.provider.dart'; final authRepositoryProvider = Provider( - (ref) => AuthRepository(ref.watch(driftProvider), ref.watch(appConfigProvider)), + (ref) => AuthRepository(ref.watch(driftProvider), ref.watch(settingsProvider)), ); class AuthRepository { final Drift _drift; - final AppConfig _config; + final SettingsRepository _settings; - const AuthRepository(this._drift, this._config); + const AuthRepository(this._drift, this._settings); Future clearLocalData() async { await SyncStreamRepository(_drift).reset(); } bool getEndpointSwitchingFeature() { - return _config.network.autoEndpointSwitching; + return _settings.appConfig.network.autoEndpointSwitching; } String? getPreferredWifiName() { - return _config.network.preferredWifiName; + return _settings.appConfig.network.preferredWifiName; } String? getLocalEndpoint() { - return _config.network.localEndpoint; + return _settings.appConfig.network.localEndpoint; } List getExternalEndpointList() { - return _config.network.externalEndpointList.map((url) => AuxilaryEndpoint(url: url, status: .valid)).toList(); + return _settings.appConfig.network.externalEndpointList + .map((url) => AuxilaryEndpoint(url: url, status: .valid)) + .toList(); } } diff --git a/mobile/lib/services/api.service.dart b/mobile/lib/services/api.service.dart index 99f618b832..a0828927ce 100644 --- a/mobile/lib/services/api.service.dart +++ b/mobile/lib/services/api.service.dart @@ -5,7 +5,7 @@ import 'dart:io'; import 'package:device_info_plus/device_info_plus.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/entities/store.entity.dart'; -import 'package:immich_mobile/infrastructure/repositories/metadata.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/settings.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/network.repository.dart'; import 'package:immich_mobile/utils/debug_print.dart'; import 'package:immich_mobile/utils/url_helper.dart'; @@ -13,7 +13,7 @@ import 'package:logging/logging.dart'; import 'package:openapi/api.dart'; class ApiService { - late ApiClient _apiClient; + final ApiClient _apiClient = ApiClient(basePath: ''); late UsersApi usersApi; late AuthenticationApi authenticationApi; @@ -54,7 +54,7 @@ class ApiService { } setEndpoint(String endpoint) { - _apiClient = ApiClient(basePath: endpoint); + _apiClient.basePath = endpoint; _apiClient.client = NetworkRepository.client; usersApi = UsersApi(_apiClient); authenticationApi = AuthenticationApi(_apiClient); @@ -177,7 +177,7 @@ class ApiService { if (serverEndpoint != null && serverEndpoint.isNotEmpty) { urls.add(serverEndpoint); } - final network = MetadataRepository.instance.appConfig.network; + final network = SettingsRepository.instance.appConfig.network; final localEndpoint = network.localEndpoint; if (localEndpoint.isNotEmpty) { urls.add(localEndpoint); @@ -191,7 +191,7 @@ class ApiService { } static Map getRequestHeaders() { - return MetadataRepository.instance.appConfig.network.customHeaders; + return SettingsRepository.instance.appConfig.network.customHeaders; } ApiClient get apiClient => _apiClient; diff --git a/mobile/lib/services/auth.service.dart b/mobile/lib/services/auth.service.dart index 7d470ecd7a..0de22fd124 100644 --- a/mobile/lib/services/auth.service.dart +++ b/mobile/lib/services/auth.service.dart @@ -1,11 +1,11 @@ import 'dart:async'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/domain/models/metadata_key.dart'; +import 'package:immich_mobile/domain/models/settings_key.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/domain/utils/background_sync.dart'; import 'package:immich_mobile/entities/store.entity.dart'; -import 'package:immich_mobile/infrastructure/repositories/metadata.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/settings.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/network.repository.dart'; import 'package:immich_mobile/models/auth/auxilary_endpoint.model.dart'; import 'package:immich_mobile/models/auth/login_response.model.dart'; @@ -100,7 +100,7 @@ class AuthService { _log.severe("Error clearing local data", error, stackTrace); }); - await MetadataRepository.instance.write(MetadataKey.backupEnabled, false); + await SettingsRepository.instance.write(SettingsKey.backupEnabled, false); } } @@ -110,7 +110,7 @@ class AuthService { /// - Authentication repository data /// - Current user information /// - Access token - /// - Asset ETag + /// - Server-specific endpoint configuration /// /// All deletions are executed in parallel using [Future.wait]. Future clearLocalData() async { @@ -120,6 +120,12 @@ class AuthService { _authRepository.clearLocalData(), Store.delete(StoreKey.currentUser), Store.delete(StoreKey.accessToken), + SettingsRepository.instance.clear(const [ + .networkAutoEndpointSwitching, + .networkPreferredWifiName, + .networkLocalEndpoint, + .networkExternalEndpointList, + ]), ]); } diff --git a/mobile/lib/services/background_upload.service.dart b/mobile/lib/services/background_upload.service.dart index 37577e3666..903fd02395 100644 --- a/mobile/lib/services/background_upload.service.dart +++ b/mobile/lib/services/background_upload.service.dart @@ -13,7 +13,7 @@ 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/metadata.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/settings.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart'; import 'package:immich_mobile/providers/infrastructure/asset.provider.dart'; import 'package:immich_mobile/providers/infrastructure/storage.provider.dart'; @@ -359,7 +359,7 @@ class BackgroundUploadService { } bool _shouldRequireWiFi(LocalAsset asset) { - final backup = MetadataRepository.instance.appConfig.backup; + final backup = SettingsRepository.instance.appConfig.backup; if (asset.isVideo && backup.useCellularForVideos) { return false; } diff --git a/mobile/lib/services/foreground_upload.service.dart b/mobile/lib/services/foreground_upload.service.dart index 2fc1a92127..ef7f32d168 100644 --- a/mobile/lib/services/foreground_upload.service.dart +++ b/mobile/lib/services/foreground_upload.service.dart @@ -11,7 +11,7 @@ import 'package:immich_mobile/extensions/network_capability_extensions.dart'; import 'package:immich_mobile/extensions/platform_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/infrastructure/repositories/backup.repository.dart'; -import 'package:immich_mobile/infrastructure/repositories/metadata.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/settings.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart'; import 'package:immich_mobile/platform/connectivity_api.g.dart'; import 'package:immich_mobile/providers/infrastructure/platform.provider.dart'; @@ -451,7 +451,7 @@ class ForegroundUploadService { } bool _shouldRequireWiFi(LocalAsset asset) { - final backup = MetadataRepository.instance.appConfig.backup; + final backup = SettingsRepository.instance.appConfig.backup; if (asset.isVideo && backup.useCellularForVideos) { return false; } diff --git a/mobile/lib/utils/bootstrap.dart b/mobile/lib/utils/bootstrap.dart index 68ebfe9c9f..9bd652381a 100644 --- a/mobile/lib/utils/bootstrap.dart +++ b/mobile/lib/utils/bootstrap.dart @@ -6,7 +6,7 @@ import 'package:immich_mobile/extensions/translate_extensions.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/metadata.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/settings.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/network.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/store.repository.dart'; import 'package:photo_manager/photo_manager.dart'; @@ -49,11 +49,11 @@ abstract final class Bootstrap { await StoreService.init(storeRepository: storeRepo, listenUpdates: listenStoreUpdates); - final metadataRepo = await MetadataRepository.ensureInitialized(drift); + final settingsRepo = await SettingsRepository.ensureInitialized(drift); await LogService.init( logRepository: LogRepository(logDb), - metadataRepository: metadataRepo, + settingsRepository: settingsRepo, shouldBuffer: shouldBufferLogs, ); diff --git a/mobile/lib/utils/migration.dart b/mobile/lib/utils/migration.dart index e1f40c8751..3afa554e29 100644 --- a/mobile/lib/utils/migration.dart +++ b/mobile/lib/utils/migration.dart @@ -8,11 +8,11 @@ import 'package:immich_mobile/constants/colors.dart'; import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/domain/models/config/app_config.dart'; import 'package:immich_mobile/domain/models/log.model.dart'; -import 'package:immich_mobile/domain/models/metadata_key.dart'; +import 'package:immich_mobile/domain/models/settings_key.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/domain/models/timeline.model.dart'; import 'package:immich_mobile/entities/store.entity.dart'; -import 'package:immich_mobile/infrastructure/entities/metadata.entity.drift.dart'; +import 'package:immich_mobile/infrastructure/entities/settings.entity.drift.dart'; import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/network.repository.dart'; import 'package:immich_mobile/models/auth/auxilary_endpoint.model.dart'; @@ -74,65 +74,65 @@ Future _migrateTo25() async { Future _migrateTo26(Drift drift) async { final migrator = _StoreMigrator(drift); - await migrator.migrateEnumIndex(StoreKey.legacyLogLevel, MetadataKey.logLevel, LogLevel.values); + await migrator.migrateEnumIndex(StoreKey.legacyLogLevel, SettingsKey.logLevel, LogLevel.values); // Theme - await migrator.migrateEnumName(StoreKey.legacyThemeMode, MetadataKey.themeMode, ThemeMode.values); - await migrator.migrateEnumName(StoreKey.legacyPrimaryColor, MetadataKey.themePrimaryColor, ImmichColorPreset.values); - await migrator.migrateBool(StoreKey.legacyDynamicTheme, MetadataKey.themeDynamic); - await migrator.migrateBool(StoreKey.legacyColorfulInterface, MetadataKey.themeColorfulInterface); + await migrator.migrateEnumName(StoreKey.legacyThemeMode, SettingsKey.themeMode, ThemeMode.values); + await migrator.migrateEnumName(StoreKey.legacyPrimaryColor, SettingsKey.themePrimaryColor, ImmichColorPreset.values); + await migrator.migrateBool(StoreKey.legacyDynamicTheme, SettingsKey.themeDynamic); + await migrator.migrateBool(StoreKey.legacyColorfulInterface, SettingsKey.themeColorfulInterface); // Cleanup final cleanupKeepAlbumIds = await migrator.readLegacyStoreString(StoreKey.legacyCleanupKeepAlbumIds.id); if (cleanupKeepAlbumIds != null) { final ids = cleanupKeepAlbumIds.split(',').where((id) => id.isNotEmpty).toList(); - migrator.stage(StoreKey.legacyCleanupKeepAlbumIds, MetadataKey.cleanupKeepAlbumIds, ids); + migrator.stage(StoreKey.legacyCleanupKeepAlbumIds, SettingsKey.cleanupKeepAlbumIds, ids); } - await migrator.migrateBool(StoreKey.legacyCleanupKeepFavorites, MetadataKey.cleanupKeepFavorites); + await migrator.migrateBool(StoreKey.legacyCleanupKeepFavorites, SettingsKey.cleanupKeepFavorites); await migrator.migrateEnumIndex( StoreKey.legacyCleanupKeepMediaType, - MetadataKey.cleanupKeepMediaType, + SettingsKey.cleanupKeepMediaType, AssetKeepType.values, ); - await migrator.migrateInt(StoreKey.legacyCleanupCutoffDaysAgo, MetadataKey.cleanupCutoffDaysAgo); - await migrator.migrateBool(StoreKey.legacyCleanupDefaultsInitialized, MetadataKey.cleanupDefaultsInitialized); + await migrator.migrateInt(StoreKey.legacyCleanupCutoffDaysAgo, SettingsKey.cleanupCutoffDaysAgo); + await migrator.migrateBool(StoreKey.legacyCleanupDefaultsInitialized, SettingsKey.cleanupDefaultsInitialized); // Map - await migrator.migrateBool(StoreKey.legacyMapShowFavoriteOnly, MetadataKey.mapShowFavoriteOnly); - await migrator.migrateInt(StoreKey.legacyMapRelativeDate, MetadataKey.mapRelativeDate); - await migrator.migrateBool(StoreKey.legacyMapIncludeArchived, MetadataKey.mapIncludeArchived); - await migrator.migrateEnumIndex(StoreKey.legacyMapThemeMode, MetadataKey.mapThemeMode, ThemeMode.values); - await migrator.migrateBool(StoreKey.legacyMapwithPartners, MetadataKey.mapWithPartners); + await migrator.migrateBool(StoreKey.legacyMapShowFavoriteOnly, SettingsKey.mapShowFavoriteOnly); + await migrator.migrateInt(StoreKey.legacyMapRelativeDate, SettingsKey.mapRelativeDate); + await migrator.migrateBool(StoreKey.legacyMapIncludeArchived, SettingsKey.mapIncludeArchived); + await migrator.migrateEnumIndex(StoreKey.legacyMapThemeMode, SettingsKey.mapThemeMode, ThemeMode.values); + await migrator.migrateBool(StoreKey.legacyMapwithPartners, SettingsKey.mapWithPartners); // Timeline - await migrator.migrateInt(StoreKey.legacyTilesPerRow, MetadataKey.timelineTilesPerRow); + await migrator.migrateInt(StoreKey.legacyTilesPerRow, SettingsKey.timelineTilesPerRow); await migrator.migrateEnumIndex( StoreKey.legacyGroupAssetsBy, - MetadataKey.timelineGroupAssetsBy, + SettingsKey.timelineGroupAssetsBy, GroupAssetsBy.values, ); - await migrator.migrateBool(StoreKey.legacyStorageIndicator, MetadataKey.timelineStorageIndicator); + await migrator.migrateBool(StoreKey.legacyStorageIndicator, SettingsKey.timelineStorageIndicator); // Image - await migrator.migrateBool(StoreKey.legacyPreferRemoteImage, MetadataKey.imagePreferRemote); - await migrator.migrateBool(StoreKey.legacyLoadOriginal, MetadataKey.imageLoadOriginal); + await migrator.migrateBool(StoreKey.legacyPreferRemoteImage, SettingsKey.imagePreferRemote); + await migrator.migrateBool(StoreKey.legacyLoadOriginal, SettingsKey.imageLoadOriginal); // Viewer - await migrator.migrateBool(StoreKey.legacyLoopVideo, MetadataKey.viewerLoopVideo); - await migrator.migrateBool(StoreKey.legacyLoadOriginalVideo, MetadataKey.viewerLoadOriginalVideo); - await migrator.migrateBool(StoreKey.legacyAutoPlayVideo, MetadataKey.viewerAutoPlayVideo); - await migrator.migrateBool(StoreKey.legacyTapToNavigate, MetadataKey.viewerTapToNavigate); + await migrator.migrateBool(StoreKey.legacyLoopVideo, SettingsKey.viewerLoopVideo); + await migrator.migrateBool(StoreKey.legacyLoadOriginalVideo, SettingsKey.viewerLoadOriginalVideo); + await migrator.migrateBool(StoreKey.legacyAutoPlayVideo, SettingsKey.viewerAutoPlayVideo); + await migrator.migrateBool(StoreKey.legacyTapToNavigate, SettingsKey.viewerTapToNavigate); // Network - await migrator.migrateBool(StoreKey.legacyAutoEndpointSwitching, MetadataKey.networkAutoEndpointSwitching); - await migrator.migrateString(StoreKey.legacyPreferredWifiName, MetadataKey.networkPreferredWifiName); - await migrator.migrateString(StoreKey.legacyLocalEndpoint, MetadataKey.networkLocalEndpoint); + await migrator.migrateBool(StoreKey.legacyAutoEndpointSwitching, SettingsKey.networkAutoEndpointSwitching); + await migrator.migrateString(StoreKey.legacyPreferredWifiName, SettingsKey.networkPreferredWifiName); + await migrator.migrateString(StoreKey.legacyLocalEndpoint, SettingsKey.networkLocalEndpoint); await _migrateExternalEndpointList(migrator); await _migrateCustomHeaders(migrator); // Album await _migrateAlbumSortMode(migrator); - await migrator.migrateBool(StoreKey.legacySelectedAlbumSortReverse, MetadataKey.albumIsReverse); - await migrator.migrateBool(StoreKey.legacyAlbumGridView, MetadataKey.albumIsGrid); + await migrator.migrateBool(StoreKey.legacySelectedAlbumSortReverse, SettingsKey.albumIsReverse); + await migrator.migrateBool(StoreKey.legacyAlbumGridView, SettingsKey.albumIsGrid); // Backup - await migrator.migrateBool(StoreKey.legacyEnableBackup, MetadataKey.backupEnabled); - await migrator.migrateBool(StoreKey.legacyUseWifiForUploadVideos, MetadataKey.backupUseCellularForVideos); - await migrator.migrateBool(StoreKey.legacyUseWifiForUploadPhotos, MetadataKey.backupUseCellularForPhotos); - await migrator.migrateBool(StoreKey.legacyBackupRequireCharging, MetadataKey.backupRequireCharging); - await migrator.migrateInt(StoreKey.legacyBackupTriggerDelay, MetadataKey.backupTriggerDelay); - await migrator.migrateBool(StoreKey.legacySyncAlbums, MetadataKey.backupSyncAlbums); + await migrator.migrateBool(StoreKey.legacyEnableBackup, SettingsKey.backupEnabled); + await migrator.migrateBool(StoreKey.legacyUseWifiForUploadVideos, SettingsKey.backupUseCellularForVideos); + await migrator.migrateBool(StoreKey.legacyUseWifiForUploadPhotos, SettingsKey.backupUseCellularForPhotos); + await migrator.migrateBool(StoreKey.legacyBackupRequireCharging, SettingsKey.backupRequireCharging); + await migrator.migrateInt(StoreKey.legacyBackupTriggerDelay, SettingsKey.backupTriggerDelay); + await migrator.migrateBool(StoreKey.legacySyncAlbums, SettingsKey.backupSyncAlbums); await migrator.complete(); } @@ -143,7 +143,7 @@ Future _migrateAlbumSortMode(_StoreMigrator migrator) async { return; } - migrator.stage(StoreKey.legacySelectedAlbumSortOrder, MetadataKey.albumSortMode, mode); + migrator.stage(StoreKey.legacySelectedAlbumSortOrder, SettingsKey.albumSortMode, mode); } Future _migrateExternalEndpointList(_StoreMigrator migrator) async { @@ -167,7 +167,7 @@ Future _migrateExternalEndpointList(_StoreMigrator migrator) async { // ignore invalid entries } - migrator.stage(StoreKey.legacyExternalEndpointList, MetadataKey.networkExternalEndpointList, urls); + migrator.stage(StoreKey.legacyExternalEndpointList, SettingsKey.networkExternalEndpointList, urls); } Future _migrateCustomHeaders(_StoreMigrator migrator) async { @@ -190,17 +190,17 @@ Future _migrateCustomHeaders(_StoreMigrator migrator) async { // ignore invalid entries } - migrator.stage(StoreKey.legacyCustomHeaders, MetadataKey.networkCustomHeaders, headers); + migrator.stage(StoreKey.legacyCustomHeaders, SettingsKey.networkCustomHeaders, headers); } class _StoreMigrator { final Drift _db; - final Map, Object> _cache = {}; + final Map, Object> _cache = {}; final List _migratedStoreIds = []; _StoreMigrator(this._db); - Future migrateEnumIndex(StoreKey legacyKey, MetadataKey newKey, List values) async { + Future migrateEnumIndex(StoreKey legacyKey, SettingsKey newKey, List values) async { final index = await readLegacyStoreInt(legacyKey.id); if (index == null) { return; @@ -217,7 +217,7 @@ class _StoreMigrator { Future migrateEnumName( StoreKey legacyKey, - MetadataKey newKey, + SettingsKey newKey, List values, ) async { final name = await readLegacyStoreString(legacyKey.id); @@ -234,7 +234,7 @@ class _StoreMigrator { _migratedStoreIds.add(legacyKey.id); } - Future migrateBool(StoreKey legacyKey, MetadataKey newKey) async { + Future migrateBool(StoreKey legacyKey, SettingsKey newKey) async { final intValue = await readLegacyStoreInt(legacyKey.id); if (intValue == null) { return; @@ -245,7 +245,7 @@ class _StoreMigrator { _migratedStoreIds.add(legacyKey.id); } - Future migrateInt(StoreKey legacyKey, MetadataKey newKey) async { + Future migrateInt(StoreKey legacyKey, SettingsKey newKey) async { final intValue = await readLegacyStoreInt(legacyKey.id); if (intValue == null) { return; @@ -255,7 +255,7 @@ class _StoreMigrator { _migratedStoreIds.add(legacyKey.id); } - Future migrateString(StoreKey legacyKey, MetadataKey newKey) async { + Future migrateString(StoreKey legacyKey, SettingsKey newKey) async { final value = await readLegacyStoreString(legacyKey.id); if (value == null) { return; @@ -265,7 +265,7 @@ class _StoreMigrator { _migratedStoreIds.add(legacyKey.id); } - void stage(StoreKey legacyKey, MetadataKey newKey, T value) { + void stage(StoreKey legacyKey, SettingsKey newKey, T value) { _cache[newKey] = value; _migratedStoreIds.add(legacyKey.id); } @@ -277,8 +277,8 @@ class _StoreMigrator { continue; } batch.insert( - _db.metadataEntity, - MetadataEntityCompanion(key: Value(entry.key.name), value: Value(entry.key.encode(entry.value))), + _db.settingsEntity, + SettingsEntityCompanion(key: Value(entry.key.name), value: Value(entry.key.encode(entry.value))), mode: InsertMode.insertOrReplace, ); } diff --git a/mobile/lib/utils/openapi_patching.dart b/mobile/lib/utils/openapi_patching.dart index 38c805a42e..cbd6c2bcce 100644 --- a/mobile/lib/utils/openapi_patching.dart +++ b/mobile/lib/utils/openapi_patching.dart @@ -19,6 +19,7 @@ dynamic upgradeDto(dynamic value, String targetType) { 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, 'minFaces', 3); } case 'UserResponseDto': if (value is Map) { @@ -54,6 +55,7 @@ dynamic upgradeDto(dynamic value, String targetType) { case 'ServerFeaturesDto': if (value is Map) { addDefault(value, 'ocr', false); + addDefault(value, 'realtimeTranscoding', false); } break; case 'MemoriesResponse': diff --git a/mobile/lib/utils/semver.dart b/mobile/lib/utils/semver.dart index 06b186daa3..8080af00b0 100644 --- a/mobile/lib/utils/semver.dart +++ b/mobile/lib/utils/semver.dart @@ -1,36 +1,42 @@ -enum SemVerType { major, minor, patch } +enum SemVerType { major, minor, patch, prerelease } class SemVer { final int major; final int minor; final int patch; + final int? prerelease; - const SemVer({required this.major, required this.minor, required this.patch}); + const SemVer({required this.major, required this.minor, required this.patch, this.prerelease}); @override String toString() { - return '$major.$minor.$patch'; + return '$major.$minor.$patch${prerelease == null ? '' : '-rc.$prerelease'}'; } - SemVer copyWith({int? major, int? minor, int? patch}) { - return SemVer(major: major ?? this.major, minor: minor ?? this.minor, patch: patch ?? this.patch); + SemVer copyWith({int? major, int? minor, int? patch, int? prerelease}) { + return SemVer( + major: major ?? this.major, + minor: minor ?? this.minor, + patch: patch ?? this.patch, + prerelease: prerelease ?? this.prerelease, + ); } + static final _pattern = RegExp(r'^v?(\d+)\.(\d+)\.(\d+)(?:-rc\.(\d+))?(?:[-+].*)?$', caseSensitive: false); + factory SemVer.fromString(String version) { - if (version.toLowerCase().startsWith("v")) { - version = version.substring(1); - } - - final parts = version.split("-")[0].split('.'); - if (parts.length != 3) { + final match = _pattern.firstMatch(version); + if (match == null) { throw FormatException('Invalid semantic version string: $version'); } - try { - return SemVer(major: int.parse(parts[0]), minor: int.parse(parts[1]), patch: int.parse(parts[2])); - } catch (e) { - throw FormatException('Invalid semantic version string: $version'); - } + final prerelease = match.group(4); + return SemVer( + major: int.parse(match.group(1)!), + minor: int.parse(match.group(2)!), + patch: int.parse(match.group(3)!), + prerelease: prerelease == null ? null : int.parse(prerelease), + ); } bool operator >(SemVer other) { @@ -40,7 +46,10 @@ class SemVer { if (minor != other.minor) { return minor > other.minor; } - return patch > other.patch; + if (patch != other.patch) { + return patch > other.patch; + } + return _comparePrerelease(other) > 0; } bool operator <(SemVer other) { @@ -50,7 +59,23 @@ class SemVer { if (minor != other.minor) { return minor < other.minor; } - return patch < other.patch; + if (patch != other.patch) { + return patch < other.patch; + } + return _comparePrerelease(other) < 0; + } + + int _comparePrerelease(SemVer other) { + if (prerelease == other.prerelease) { + return 0; + } + if (prerelease == null) { + return 1; + } + if (other.prerelease == null) { + return -1; + } + return prerelease!.compareTo(other.prerelease!); } bool operator >=(SemVer other) { @@ -67,7 +92,11 @@ class SemVer { return true; } - return other is SemVer && other.major == major && other.minor == minor && other.patch == patch; + return other is SemVer && + other.major == major && + other.minor == minor && + other.patch == patch && + other.prerelease == prerelease; } SemVerType? differenceType(SemVer other) { @@ -80,10 +109,13 @@ class SemVer { if (patch != other.patch) { return SemVerType.patch; } + if (prerelease != other.prerelease) { + return SemVerType.prerelease; + } return null; } @override - int get hashCode => major.hashCode ^ minor.hashCode ^ patch.hashCode; + int get hashCode => major.hashCode ^ minor.hashCode ^ patch.hashCode ^ prerelease.hashCode; } 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 2809505c58..a209d280c3 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 @@ -50,9 +50,7 @@ class AppBarServerInfo extends HookConsumerWidget { divider, _ServerInfoItem( label: "server_version".tr(), - text: serverInfoState.serverVersion.major > 0 - ? "${serverInfoState.serverVersion.major}.${serverInfoState.serverVersion.minor}.${serverInfoState.serverVersion.patch}" - : "--", + text: serverInfoState.serverVersion.major > 0 ? "${serverInfoState.serverVersion}" : "--", ), divider, _ServerInfoItem(label: "server_info_box_server_url".tr(), text: getServerUrl() ?? '--', tooltip: true), @@ -60,9 +58,7 @@ class AppBarServerInfo extends HookConsumerWidget { divider, _ServerInfoItem( label: "latest_version".tr(), - text: serverInfoState.latestVersion!.major > 0 - ? "${serverInfoState.latestVersion!.major}.${serverInfoState.latestVersion!.minor}.${serverInfoState.latestVersion!.patch}" - : "--", + text: serverInfoState.latestVersion!.major > 0 ? "${serverInfoState.latestVersion!}" : "--", tooltip: true, icon: serverInfoState.versionStatus == VersionStatus.serverOutOfDate ? const Icon(Icons.info, color: Color.fromARGB(255, 243, 188, 106), size: 12) diff --git a/mobile/lib/widgets/common/immich_sliver_app_bar.dart b/mobile/lib/widgets/common/immich_sliver_app_bar.dart index 32aa766dec..a81082753a 100644 --- a/mobile/lib/widgets/common/immich_sliver_app_bar.dart +++ b/mobile/lib/widgets/common/immich_sliver_app_bar.dart @@ -10,7 +10,7 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/models/server_info/server_info.model.dart'; import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; import 'package:immich_mobile/providers/cast.provider.dart'; -import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/settings.provider.dart'; import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart'; import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/providers/sync_status.provider.dart'; diff --git a/mobile/lib/widgets/forms/login/login_form.dart b/mobile/lib/widgets/forms/login/login_form.dart index f64e7cc197..090c9bb2b8 100644 --- a/mobile/lib/widgets/forms/login/login_form.dart +++ b/mobile/lib/widgets/forms/login/login_form.dart @@ -15,7 +15,7 @@ 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/translate_extensions.dart'; -import 'package:immich_mobile/infrastructure/repositories/metadata.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/settings.repository.dart'; import 'package:immich_mobile/providers/auth.provider.dart'; import 'package:immich_mobile/providers/background_sync.provider.dart'; import 'package:immich_mobile/providers/gallery_permission.provider.dart'; @@ -187,7 +187,7 @@ class LoginForm extends HookConsumerWidget { await backgroundManager.syncRemote(); await backgroundManager.hashAssets(); - if (MetadataRepository.instance.appConfig.backup.syncAlbums) { + if (SettingsRepository.instance.appConfig.backup.syncAlbums) { await backgroundManager.syncLinkedAlbum(); } } diff --git a/mobile/lib/widgets/settings/advanced_settings.dart b/mobile/lib/widgets/settings/advanced_settings.dart index b4b24a43e6..542a7cc5e2 100644 --- a/mobile/lib/widgets/settings/advanced_settings.dart +++ b/mobile/lib/widgets/settings/advanced_settings.dart @@ -7,7 +7,7 @@ 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/extensions/build_context_extensions.dart'; -import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/settings.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/repositories/permission.repository.dart'; @@ -35,7 +35,7 @@ class AdvancedSettings extends HookConsumerWidget { final preferRemote = useState(ref.read(appConfigProvider).image.preferRemote); useValueChanged( preferRemote.value, - (_, __) => ref.read(metadataProvider).write(.imagePreferRemote, preferRemote.value), + (_, __) => ref.read(settingsProvider).write(.imagePreferRemote, preferRemote.value), ); final readonlyModeEnabled = useAppSettingsState(AppSettingsEnum.readonlyModeEnabled); 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 b9f81da79e..59adb335bb 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 @@ -3,11 +3,10 @@ import 'dart:async'; 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/metadata_key.dart'; import 'package:immich_mobile/domain/models/timeline.model.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/providers/app_settings.provider.dart'; -import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/settings.provider.dart'; import 'package:immich_mobile/widgets/settings/setting_group_title.dart'; import 'package:immich_mobile/widgets/settings/settings_radio_list_tile.dart'; @@ -19,7 +18,7 @@ class GroupSettings extends HookConsumerWidget { final groupBy = useValueNotifier(ref.watch(appConfigProvider.select((s) => s.timeline.groupAssetsBy))); Future updateAppSettings(GroupAssetsBy groupBy) async { - await ref.read(metadataProvider).write(MetadataKey.timelineGroupAssetsBy, groupBy); + await ref.read(settingsProvider).write(.timelineGroupAssetsBy, groupBy); ref.invalidate(appSettingsServiceProvider); } 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 20025286f4..f915df04f8 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 @@ -2,10 +2,9 @@ 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/metadata_key.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/providers/app_settings.provider.dart'; -import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/settings.provider.dart'; import 'package:immich_mobile/widgets/settings/setting_group_title.dart'; import 'package:immich_mobile/widgets/settings/settings_slider_list_tile.dart'; @@ -16,7 +15,7 @@ class LayoutSettings extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final tilesPerRow = useState(ref.read(appConfigProvider.select((s) => s.timeline.tilesPerRow))); useValueChanged(tilesPerRow.value, (_, __) { - ref.read(metadataProvider).write(MetadataKey.timelineTilesPerRow, tilesPerRow.value); + ref.read(settingsProvider).write(.timelineTilesPerRow, tilesPerRow.value); }); return Column( 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 21d751c26f..3ac72d6612 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,10 +2,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/metadata_key.dart'; import 'package:immich_mobile/providers/app_settings.provider.dart'; -import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart'; -import 'package:immich_mobile/providers/infrastructure/setting.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/settings.provider.dart'; import 'package:immich_mobile/widgets/settings/asset_list_settings/asset_list_group_settings.dart'; import 'package:immich_mobile/widgets/settings/asset_list_settings/asset_list_layout_settings.dart'; import 'package:immich_mobile/widgets/settings/settings_sub_page_scaffold.dart'; @@ -23,7 +21,7 @@ class AssetListSettings extends HookConsumerWidget { valueNotifier: storageIndicator, title: 'theme_setting_asset_list_storage_indicator_title'.tr(), onChanged: (value) { - ref.read(metadataProvider).write(MetadataKey.timelineStorageIndicator, value); + ref.read(settingsProvider).write(.timelineStorageIndicator, value); ref.invalidate(appSettingsServiceProvider); ref.invalidate(settingsProvider); }, 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 7858033401..f65af6af9d 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 @@ -3,7 +3,7 @@ import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/providers/app_settings.provider.dart'; -import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/settings.provider.dart'; import 'package:immich_mobile/widgets/settings/setting_group_title.dart'; import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; @@ -14,7 +14,7 @@ class ImageViewerQualitySetting extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final isOriginal = useState(ref.read(appConfigProvider).image.loadOriginal); useValueChanged(isOriginal.value, (_, __) { - ref.read(metadataProvider).write(.imageLoadOriginal, isOriginal.value); + ref.read(settingsProvider).write(.imageLoadOriginal, isOriginal.value); }); return Column( diff --git a/mobile/lib/widgets/settings/asset_viewer_settings/image_viewer_tap_to_navigate_setting.dart b/mobile/lib/widgets/settings/asset_viewer_settings/image_viewer_tap_to_navigate_setting.dart index 5af64b0be9..730521e3c1 100644 --- a/mobile/lib/widgets/settings/asset_viewer_settings/image_viewer_tap_to_navigate_setting.dart +++ b/mobile/lib/widgets/settings/asset_viewer_settings/image_viewer_tap_to_navigate_setting.dart @@ -2,7 +2,7 @@ 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/providers/infrastructure/metadata.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/settings.provider.dart'; import 'package:immich_mobile/widgets/settings/settings_sub_title.dart'; import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; @@ -13,7 +13,7 @@ class ImageViewerTapToNavigateSetting extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final tapToNavigate = useState(ref.read(appConfigProvider).viewer.tapToNavigate); useValueChanged(tapToNavigate.value, (_, __) { - ref.read(metadataProvider).write(.viewerTapToNavigate, tapToNavigate.value); + ref.read(settingsProvider).write(.viewerTapToNavigate, tapToNavigate.value); }); return Column( diff --git a/mobile/lib/widgets/settings/asset_viewer_settings/slideshow_settings.dart b/mobile/lib/widgets/settings/asset_viewer_settings/slideshow_settings.dart index 4e566e6065..5f93d429b0 100644 --- a/mobile/lib/widgets/settings/asset_viewer_settings/slideshow_settings.dart +++ b/mobile/lib/widgets/settings/asset_viewer_settings/slideshow_settings.dart @@ -3,7 +3,7 @@ import 'package:flutter_hooks/flutter_hooks.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/metadata.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/settings.provider.dart'; import 'package:immich_mobile/widgets/settings/setting_group_title.dart'; import 'package:immich_mobile/widgets/settings/settings_radio_list_tile.dart'; import 'package:immich_mobile/widgets/settings/settings_slider_list_tile.dart'; @@ -23,19 +23,19 @@ class SlideshowSettings extends HookConsumerWidget { final useDirection = useState(slideshow.direction); useValueChanged(useTransition.value, (_, __) { - ref.read(metadataProvider).write(.slideshowTransition, useTransition.value); + ref.read(settingsProvider).write(.slideshowTransition, useTransition.value); }); useValueChanged(useRepeat.value, (_, __) { - ref.read(metadataProvider).write(.slideshowRepeat, useRepeat.value); + ref.read(settingsProvider).write(.slideshowRepeat, useRepeat.value); }); useValueChanged(useDuration.value, (_, __) { - ref.read(metadataProvider).write(.slideshowDuration, useDuration.value); + ref.read(settingsProvider).write(.slideshowDuration, useDuration.value); }); useValueChanged(useLook.value, (_, __) { - ref.read(metadataProvider).write(.slideshowLook, useLook.value); + ref.read(settingsProvider).write(.slideshowLook, useLook.value); }); useValueChanged(useDirection.value, (_, __) { - ref.read(metadataProvider).write(.slideshowDirection, useDirection.value); + ref.read(settingsProvider).write(.slideshowDirection, useDirection.value); }); return Column( 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 8d62544dd4..81929d95b9 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 @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; -import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/settings.provider.dart'; import 'package:immich_mobile/widgets/settings/setting_group_title.dart'; import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; @@ -17,13 +17,13 @@ class VideoViewerSettings extends HookConsumerWidget { final useOriginalVideo = useState(viewer.loadOriginalVideo); useValueChanged(useAutoPlayVideo.value, (_, __) { - ref.read(metadataProvider).write(.viewerAutoPlayVideo, useAutoPlayVideo.value); + ref.read(settingsProvider).write(.viewerAutoPlayVideo, useAutoPlayVideo.value); }); useValueChanged(useLoopVideo.value, (_, __) { - ref.read(metadataProvider).write(.viewerLoopVideo, useLoopVideo.value); + ref.read(settingsProvider).write(.viewerLoopVideo, useLoopVideo.value); }); useValueChanged(useOriginalVideo.value, (_, __) { - ref.read(metadataProvider).write(.viewerLoadOriginalVideo, useOriginalVideo.value); + ref.read(settingsProvider).write(.viewerLoadOriginalVideo, useOriginalVideo.value); }); return Column( diff --git a/mobile/lib/widgets/settings/backup_settings/drift_backup_settings.dart b/mobile/lib/widgets/settings/backup_settings/drift_backup_settings.dart index 89d6f13f43..e5debb43fe 100644 --- a/mobile/lib/widgets/settings/backup_settings/drift_backup_settings.dart +++ b/mobile/lib/widgets/settings/backup_settings/drift_backup_settings.dart @@ -5,15 +5,15 @@ 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/config/app_config.dart'; -import 'package:immich_mobile/domain/models/metadata_key.dart'; +import 'package:immich_mobile/domain/models/settings_key.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/platform_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.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/metadata.provider.dart'; import 'package:immich_mobile/providers/infrastructure/platform.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/settings.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/widgets/settings/setting_group_title.dart'; import 'package:immich_mobile/widgets/settings/setting_list_tile.dart'; @@ -112,7 +112,7 @@ class _AlbumSyncActionButtonState extends ConsumerState<_AlbumSyncActionButton> trailing: Switch( value: albumSyncEnable, onChanged: (bool newValue) async { - await ref.read(metadataProvider).write(MetadataKey.backupSyncAlbums, newValue); + await ref.read(settingsProvider).write(.backupSyncAlbums, newValue); if (newValue == true) { await _manageLinkedAlbums(); @@ -158,7 +158,7 @@ class _AlbumSyncActionButtonState extends ConsumerState<_AlbumSyncActionButton> } class _BackupSwitchTile extends ConsumerWidget { - final MetadataKey metadataKey; + final SettingsKey metadataKey; final bool Function(AppConfig) selector; final String titleKey; final String subtitleKey; @@ -183,7 +183,7 @@ class _BackupSwitchTile extends ConsumerWidget { trailing: Switch( value: value, onChanged: (bool newValue) async { - await ref.read(metadataProvider).write(metadataKey, newValue); + await ref.read(settingsProvider).write(metadataKey, newValue); onChanged?.call(newValue); }, ), @@ -198,7 +198,7 @@ class _UseCellularForVideosButton extends StatelessWidget { @override Widget build(BuildContext context) { return _BackupSwitchTile( - metadataKey: MetadataKey.backupUseCellularForVideos, + metadataKey: SettingsKey.backupUseCellularForVideos, selector: (c) => c.backup.useCellularForVideos, titleKey: "videos", subtitleKey: "network_requirement_videos_upload", @@ -212,7 +212,7 @@ class _UseCellularForPhotosButton extends StatelessWidget { @override Widget build(BuildContext context) { return _BackupSwitchTile( - metadataKey: MetadataKey.backupUseCellularForPhotos, + metadataKey: SettingsKey.backupUseCellularForPhotos, selector: (c) => c.backup.useCellularForPhotos, titleKey: "photos", subtitleKey: "network_requirement_photos_upload", @@ -227,7 +227,7 @@ class _BackupOnlyWhenChargingButton extends ConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final fgService = ref.read(backgroundWorkerFgServiceProvider); return _BackupSwitchTile( - metadataKey: MetadataKey.backupRequireCharging, + metadataKey: SettingsKey.backupRequireCharging, selector: (c) => c.backup.requireCharging, titleKey: "charging", subtitleKey: "charging_requirement_mobile_backup", @@ -282,11 +282,11 @@ class _BackupDelaySlider extends ConsumerWidget { value: currentValue.toDouble(), onChanged: (double v) async { final seconds = backupDelayToSeconds(v.toInt()); - await ref.read(metadataProvider).write(MetadataKey.backupTriggerDelay, seconds); + await ref.read(settingsProvider).write(SettingsKey.backupTriggerDelay, seconds); }, onChangeEnd: (double v) async { final seconds = backupDelayToSeconds(v.toInt()); - await ref.read(metadataProvider).write(MetadataKey.backupTriggerDelay, seconds); + await ref.read(settingsProvider).write(SettingsKey.backupTriggerDelay, seconds); }, max: 3.0, min: 0.0, 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 02be04ac31..492a13de93 100644 --- a/mobile/lib/widgets/settings/networking_settings/external_network_preference.dart +++ b/mobile/lib/widgets/settings/networking_settings/external_network_preference.dart @@ -1,11 +1,11 @@ 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/metadata_key.dart'; +import 'package:immich_mobile/domain/models/settings_key.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/models/auth/auxilary_endpoint.model.dart'; -import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/settings.provider.dart'; import 'package:immich_mobile/widgets/settings/networking_settings/endpoint_input.dart'; class ExternalNetworkPreference extends HookConsumerWidget { @@ -26,7 +26,7 @@ class ExternalNetworkPreference extends HookConsumerWidget { .map((e) => e.url) .toList(); - ref.read(metadataProvider).write(MetadataKey.networkExternalEndpointList, urls); + ref.read(settingsProvider).write(SettingsKey.networkExternalEndpointList, urls); } updateValidationStatus(String url, int index, AuxCheckStatus status) { diff --git a/mobile/lib/widgets/settings/networking_settings/networking_settings.dart b/mobile/lib/widgets/settings/networking_settings/networking_settings.dart index 037d66b076..7e6e169de7 100644 --- a/mobile/lib/widgets/settings/networking_settings/networking_settings.dart +++ b/mobile/lib/widgets/settings/networking_settings/networking_settings.dart @@ -5,7 +5,7 @@ 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/models/auth/auxilary_endpoint.model.dart'; -import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/settings.provider.dart'; import 'package:immich_mobile/providers/network.provider.dart'; import 'package:immich_mobile/utils/url_helper.dart'; import 'package:immich_mobile/widgets/settings/networking_settings/external_network_preference.dart'; @@ -21,7 +21,7 @@ class NetworkingSettings extends HookConsumerWidget { final currentEndpoint = getServerUrl(); final featureEnabled = useState(ref.read(appConfigProvider).network.autoEndpointSwitching); useValueChanged(featureEnabled.value, (_, __) { - ref.read(metadataProvider).write(.networkAutoEndpointSwitching, featureEnabled.value); + ref.read(settingsProvider).write(.networkAutoEndpointSwitching, featureEnabled.value); }); Future checkWifiReadPermission() async { 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 330555ed54..48d0ca672b 100644 --- a/mobile/lib/widgets/settings/preference_settings/primary_color_setting.dart +++ b/mobile/lib/widgets/settings/preference_settings/primary_color_setting.dart @@ -4,7 +4,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/colors.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/theme_extensions.dart'; -import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/settings.provider.dart'; import 'package:immich_mobile/providers/theme.provider.dart'; import 'package:immich_mobile/theme/color_scheme.dart'; import 'package:immich_mobile/theme/dynamic_theme.dart'; @@ -26,16 +26,16 @@ class PrimaryColorSetting extends HookConsumerWidget { } onUseSystemColorChange(bool newValue) { - ref.read(metadataProvider).write(.themeDynamic, newValue); + ref.read(settingsProvider).write(.themeDynamic, newValue); popBottomSheet(); } onPrimaryColorChange(ImmichColorPreset colorPreset) { - ref.read(metadataProvider).write(.themePrimaryColor, colorPreset); + ref.read(settingsProvider).write(.themePrimaryColor, colorPreset); //turn off system color setting if (themeConfig.dynamicTheme) { - ref.read(metadataProvider).write(.themeDynamic, false); + ref.read(settingsProvider).write(.themeDynamic, false); } popBottomSheet(); } diff --git a/mobile/lib/widgets/settings/preference_settings/theme_setting.dart b/mobile/lib/widgets/settings/preference_settings/theme_setting.dart index d71842d786..ffeeceae02 100644 --- a/mobile/lib/widgets/settings/preference_settings/theme_setting.dart +++ b/mobile/lib/widgets/settings/preference_settings/theme_setting.dart @@ -3,7 +3,7 @@ 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'; -import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/settings.provider.dart'; import 'package:immich_mobile/widgets/settings/preference_settings/primary_color_setting.dart'; import 'package:immich_mobile/widgets/settings/setting_group_title.dart'; import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; @@ -22,7 +22,7 @@ class ThemeSetting extends HookConsumerWidget { void onThemeChange(bool isDark) { currentTheme.value = isDark ? ThemeMode.dark : ThemeMode.light; - ref.read(metadataProvider).write(.themeMode, currentTheme.value); + ref.read(settingsProvider).write(.themeMode, currentTheme.value); } void onSystemThemeChange(bool isSystem) { @@ -39,11 +39,11 @@ class ThemeSetting extends HookConsumerWidget { currentTheme.value = ThemeMode.dark; } } - ref.read(metadataProvider).write(.themeMode, currentTheme.value); + ref.read(settingsProvider).write(.themeMode, currentTheme.value); } void onSurfaceColorSettingChange(bool useColorfulInterface) { - ref.read(metadataProvider).write(.themeColorfulInterface, useColorfulInterface); + ref.read(settingsProvider).write(.themeColorfulInterface, useColorfulInterface); colorfulInterface.value = useColorfulInterface; } diff --git a/mobile/openapi/README.md b/mobile/openapi/README.md index 0ffd076e74..70a6fa8063 100644 --- a/mobile/openapi/README.md +++ b/mobile/openapi/README.md @@ -103,12 +103,16 @@ Class | Method | HTTP request | Description *AssetsApi* | [**deleteBulkAssetMetadata**](doc//AssetsApi.md#deletebulkassetmetadata) | **DELETE** /assets/metadata | Delete asset metadata *AssetsApi* | [**downloadAsset**](doc//AssetsApi.md#downloadasset) | **GET** /assets/{id}/original | Download original asset *AssetsApi* | [**editAsset**](doc//AssetsApi.md#editasset) | **PUT** /assets/{id}/edits | Apply edits to an existing asset +*AssetsApi* | [**endSession**](doc//AssetsApi.md#endsession) | **DELETE** /assets/{id}/video/stream/{sessionId} | End HLS streaming session *AssetsApi* | [**getAssetEdits**](doc//AssetsApi.md#getassetedits) | **GET** /assets/{id}/edits | Retrieve edits for an existing asset *AssetsApi* | [**getAssetInfo**](doc//AssetsApi.md#getassetinfo) | **GET** /assets/{id} | Retrieve an asset *AssetsApi* | [**getAssetMetadata**](doc//AssetsApi.md#getassetmetadata) | **GET** /assets/{id}/metadata | Get asset metadata *AssetsApi* | [**getAssetMetadataByKey**](doc//AssetsApi.md#getassetmetadatabykey) | **GET** /assets/{id}/metadata/{key} | Retrieve asset metadata by key *AssetsApi* | [**getAssetOcr**](doc//AssetsApi.md#getassetocr) | **GET** /assets/{id}/ocr | Retrieve asset OCR data *AssetsApi* | [**getAssetStatistics**](doc//AssetsApi.md#getassetstatistics) | **GET** /assets/statistics | Get asset statistics +*AssetsApi* | [**getMainPlaylist**](doc//AssetsApi.md#getmainplaylist) | **GET** /assets/{id}/video/stream/main.m3u8 | Get HLS main playlist +*AssetsApi* | [**getMediaPlaylist**](doc//AssetsApi.md#getmediaplaylist) | **GET** /assets/{id}/video/stream/{sessionId}/{variantIndex}/playlist.m3u8 | Get HLS media playlist +*AssetsApi* | [**getSegment**](doc//AssetsApi.md#getsegment) | **GET** /assets/{id}/video/stream/{sessionId}/{variantIndex}/{filename} | Get HLS segment or init file *AssetsApi* | [**playAssetVideo**](doc//AssetsApi.md#playassetvideo) | **GET** /assets/{id}/video/playback | Play asset video *AssetsApi* | [**removeAssetEdits**](doc//AssetsApi.md#removeassetedits) | **DELETE** /assets/{id}/edits | Remove edits from an existing asset *AssetsApi* | [**runAssetJobs**](doc//AssetsApi.md#runassetjobs) | **POST** /assets/jobs | Run an asset job @@ -514,6 +518,9 @@ Class | Method | HTTP request | Description - [RatingsUpdate](doc//RatingsUpdate.md) - [ReactionLevel](doc//ReactionLevel.md) - [ReactionType](doc//ReactionType.md) + - [ReleaseChannel](doc//ReleaseChannel.md) + - [ReleaseEventV1](doc//ReleaseEventV1.md) + - [ReleaseType](doc//ReleaseType.md) - [ReverseGeocodingStateResponseDto](doc//ReverseGeocodingStateResponseDto.md) - [RotateParameters](doc//RotateParameters.md) - [SearchAlbumResponseDto](doc//SearchAlbumResponseDto.md) @@ -598,6 +605,7 @@ Class | Method | HTTP request | Description - [SystemConfigBackupsDto](doc//SystemConfigBackupsDto.md) - [SystemConfigDto](doc//SystemConfigDto.md) - [SystemConfigFFmpegDto](doc//SystemConfigFFmpegDto.md) + - [SystemConfigFFmpegRealtimeDto](doc//SystemConfigFFmpegRealtimeDto.md) - [SystemConfigFacesDto](doc//SystemConfigFacesDto.md) - [SystemConfigGeneratedFullsizeImageDto](doc//SystemConfigGeneratedFullsizeImageDto.md) - [SystemConfigGeneratedImageDto](doc//SystemConfigGeneratedImageDto.md) diff --git a/mobile/openapi/lib/api.dart b/mobile/openapi/lib/api.dart index 3305710053..550089b09d 100644 --- a/mobile/openapi/lib/api.dart +++ b/mobile/openapi/lib/api.dart @@ -259,6 +259,9 @@ part 'model/ratings_response.dart'; part 'model/ratings_update.dart'; part 'model/reaction_level.dart'; part 'model/reaction_type.dart'; +part 'model/release_channel.dart'; +part 'model/release_event_v1.dart'; +part 'model/release_type.dart'; part 'model/reverse_geocoding_state_response_dto.dart'; part 'model/rotate_parameters.dart'; part 'model/search_album_response_dto.dart'; @@ -343,6 +346,7 @@ part 'model/sync_user_v1.dart'; part 'model/system_config_backups_dto.dart'; part 'model/system_config_dto.dart'; part 'model/system_config_f_fmpeg_dto.dart'; +part 'model/system_config_f_fmpeg_realtime_dto.dart'; part 'model/system_config_faces_dto.dart'; part 'model/system_config_generated_fullsize_image_dto.dart'; part 'model/system_config_generated_image_dto.dart'; diff --git a/mobile/openapi/lib/api/activities_api.dart b/mobile/openapi/lib/api/activities_api.dart index e0a393948c..490c418785 100644 --- a/mobile/openapi/lib/api/activities_api.dart +++ b/mobile/openapi/lib/api/activities_api.dart @@ -25,7 +25,7 @@ class ActivitiesApi { /// Parameters: /// /// * [ActivityCreateDto] activityCreateDto (required): - Future createActivityWithHttpInfo(ActivityCreateDto activityCreateDto,) async { + Future createActivityWithHttpInfo(ActivityCreateDto activityCreateDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/activities'; @@ -47,6 +47,7 @@ class ActivitiesApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -57,8 +58,8 @@ class ActivitiesApi { /// Parameters: /// /// * [ActivityCreateDto] activityCreateDto (required): - Future createActivity(ActivityCreateDto activityCreateDto,) async { - final response = await createActivityWithHttpInfo(activityCreateDto,); + Future createActivity(ActivityCreateDto activityCreateDto, { Future? abortTrigger, }) async { + final response = await createActivityWithHttpInfo(activityCreateDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -81,7 +82,7 @@ class ActivitiesApi { /// Parameters: /// /// * [String] id (required): - Future deleteActivityWithHttpInfo(String id,) async { + Future deleteActivityWithHttpInfo(String id, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/activities/{id}' .replaceAll('{id}', id); @@ -104,6 +105,7 @@ class ActivitiesApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -114,8 +116,8 @@ class ActivitiesApi { /// Parameters: /// /// * [String] id (required): - Future deleteActivity(String id,) async { - final response = await deleteActivityWithHttpInfo(id,); + Future deleteActivity(String id, { Future? abortTrigger, }) async { + final response = await deleteActivityWithHttpInfo(id, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -141,7 +143,7 @@ class ActivitiesApi { /// /// * [String] userId: /// Filter by user ID - Future getActivitiesWithHttpInfo(String albumId, { String? assetId, ReactionLevel? level, ReactionType? type, String? userId, }) async { + Future getActivitiesWithHttpInfo(String albumId, { String? assetId, ReactionLevel? level, ReactionType? type, String? userId, Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/activities'; @@ -177,6 +179,7 @@ class ActivitiesApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -198,8 +201,8 @@ class ActivitiesApi { /// /// * [String] userId: /// Filter by user ID - Future?> getActivities(String albumId, { String? assetId, ReactionLevel? level, ReactionType? type, String? userId, }) async { - final response = await getActivitiesWithHttpInfo(albumId, assetId: assetId, level: level, type: type, userId: userId, ); + Future?> getActivities(String albumId, { String? assetId, ReactionLevel? level, ReactionType? type, String? userId, Future? abortTrigger, }) async { + final response = await getActivitiesWithHttpInfo(albumId, assetId: assetId, level: level, type: type, userId: userId, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -229,7 +232,7 @@ class ActivitiesApi { /// /// * [String] assetId: /// Asset ID (if activity is for an asset) - Future getActivityStatisticsWithHttpInfo(String albumId, { String? assetId, }) async { + Future getActivityStatisticsWithHttpInfo(String albumId, { String? assetId, Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/activities/statistics'; @@ -256,6 +259,7 @@ class ActivitiesApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -270,8 +274,8 @@ class ActivitiesApi { /// /// * [String] assetId: /// Asset ID (if activity is for an asset) - Future getActivityStatistics(String albumId, { String? assetId, }) async { - final response = await getActivityStatisticsWithHttpInfo(albumId, assetId: assetId, ); + Future getActivityStatistics(String albumId, { String? assetId, Future? abortTrigger, }) async { + final response = await getActivityStatisticsWithHttpInfo(albumId, assetId: assetId, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api/albums_api.dart b/mobile/openapi/lib/api/albums_api.dart index e0fc383c1d..e6e7cdbf40 100644 --- a/mobile/openapi/lib/api/albums_api.dart +++ b/mobile/openapi/lib/api/albums_api.dart @@ -27,7 +27,7 @@ class AlbumsApi { /// * [String] id (required): /// /// * [BulkIdsDto] bulkIdsDto (required): - Future addAssetsToAlbumWithHttpInfo(String id, BulkIdsDto bulkIdsDto,) async { + Future addAssetsToAlbumWithHttpInfo(String id, BulkIdsDto bulkIdsDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/albums/{id}/assets' .replaceAll('{id}', id); @@ -50,6 +50,7 @@ class AlbumsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -62,8 +63,8 @@ class AlbumsApi { /// * [String] id (required): /// /// * [BulkIdsDto] bulkIdsDto (required): - Future?> addAssetsToAlbum(String id, BulkIdsDto bulkIdsDto,) async { - final response = await addAssetsToAlbumWithHttpInfo(id, bulkIdsDto,); + Future?> addAssetsToAlbum(String id, BulkIdsDto bulkIdsDto, { Future? abortTrigger, }) async { + final response = await addAssetsToAlbumWithHttpInfo(id, bulkIdsDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -89,7 +90,7 @@ class AlbumsApi { /// Parameters: /// /// * [AlbumsAddAssetsDto] albumsAddAssetsDto (required): - Future addAssetsToAlbumsWithHttpInfo(AlbumsAddAssetsDto albumsAddAssetsDto,) async { + Future addAssetsToAlbumsWithHttpInfo(AlbumsAddAssetsDto albumsAddAssetsDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/albums/assets'; @@ -111,6 +112,7 @@ class AlbumsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -121,8 +123,8 @@ class AlbumsApi { /// Parameters: /// /// * [AlbumsAddAssetsDto] albumsAddAssetsDto (required): - Future addAssetsToAlbums(AlbumsAddAssetsDto albumsAddAssetsDto,) async { - final response = await addAssetsToAlbumsWithHttpInfo(albumsAddAssetsDto,); + Future addAssetsToAlbums(AlbumsAddAssetsDto albumsAddAssetsDto, { Future? abortTrigger, }) async { + final response = await addAssetsToAlbumsWithHttpInfo(albumsAddAssetsDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -147,7 +149,7 @@ class AlbumsApi { /// * [String] id (required): /// /// * [AddUsersDto] addUsersDto (required): - Future addUsersToAlbumWithHttpInfo(String id, AddUsersDto addUsersDto,) async { + Future addUsersToAlbumWithHttpInfo(String id, AddUsersDto addUsersDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/albums/{id}/users' .replaceAll('{id}', id); @@ -170,6 +172,7 @@ class AlbumsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -182,8 +185,8 @@ class AlbumsApi { /// * [String] id (required): /// /// * [AddUsersDto] addUsersDto (required): - Future addUsersToAlbum(String id, AddUsersDto addUsersDto,) async { - final response = await addUsersToAlbumWithHttpInfo(id, addUsersDto,); + Future addUsersToAlbum(String id, AddUsersDto addUsersDto, { Future? abortTrigger, }) async { + final response = await addUsersToAlbumWithHttpInfo(id, addUsersDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -206,7 +209,7 @@ class AlbumsApi { /// Parameters: /// /// * [CreateAlbumDto] createAlbumDto (required): - Future createAlbumWithHttpInfo(CreateAlbumDto createAlbumDto,) async { + Future createAlbumWithHttpInfo(CreateAlbumDto createAlbumDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/albums'; @@ -228,6 +231,7 @@ class AlbumsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -238,8 +242,8 @@ class AlbumsApi { /// Parameters: /// /// * [CreateAlbumDto] createAlbumDto (required): - Future createAlbum(CreateAlbumDto createAlbumDto,) async { - final response = await createAlbumWithHttpInfo(createAlbumDto,); + Future createAlbum(CreateAlbumDto createAlbumDto, { Future? abortTrigger, }) async { + final response = await createAlbumWithHttpInfo(createAlbumDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -262,7 +266,7 @@ class AlbumsApi { /// Parameters: /// /// * [String] id (required): - Future deleteAlbumWithHttpInfo(String id,) async { + Future deleteAlbumWithHttpInfo(String id, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/albums/{id}' .replaceAll('{id}', id); @@ -285,6 +289,7 @@ class AlbumsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -295,8 +300,8 @@ class AlbumsApi { /// Parameters: /// /// * [String] id (required): - Future deleteAlbum(String id,) async { - final response = await deleteAlbumWithHttpInfo(id,); + Future deleteAlbum(String id, { Future? abortTrigger, }) async { + final response = await deleteAlbumWithHttpInfo(id, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -315,7 +320,7 @@ class AlbumsApi { /// * [String] key: /// /// * [String] slug: - Future getAlbumInfoWithHttpInfo(String id, { String? key, String? slug, }) async { + Future getAlbumInfoWithHttpInfo(String id, { String? key, String? slug, Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/albums/{id}' .replaceAll('{id}', id); @@ -345,6 +350,7 @@ class AlbumsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -359,8 +365,8 @@ class AlbumsApi { /// * [String] key: /// /// * [String] slug: - Future getAlbumInfo(String id, { String? key, String? slug, }) async { - final response = await getAlbumInfoWithHttpInfo(id, key: key, slug: slug, ); + Future getAlbumInfo(String id, { String? key, String? slug, Future? abortTrigger, }) async { + final response = await getAlbumInfoWithHttpInfo(id, key: key, slug: slug, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -387,7 +393,7 @@ class AlbumsApi { /// * [String] key: /// /// * [String] slug: - Future getAlbumMapMarkersWithHttpInfo(String id, { String? key, String? slug, }) async { + Future getAlbumMapMarkersWithHttpInfo(String id, { String? key, String? slug, Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/albums/{id}/map-markers' .replaceAll('{id}', id); @@ -417,6 +423,7 @@ class AlbumsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -431,8 +438,8 @@ class AlbumsApi { /// * [String] key: /// /// * [String] slug: - Future?> getAlbumMapMarkers(String id, { String? key, String? slug, }) async { - final response = await getAlbumMapMarkersWithHttpInfo(id, key: key, slug: slug, ); + Future?> getAlbumMapMarkers(String id, { String? key, String? slug, Future? abortTrigger, }) async { + final response = await getAlbumMapMarkersWithHttpInfo(id, key: key, slug: slug, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -454,7 +461,7 @@ class AlbumsApi { /// Returns statistics about the albums available to the authenticated user. /// /// Note: This method returns the HTTP [Response]. - Future getAlbumStatisticsWithHttpInfo() async { + Future getAlbumStatisticsWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/albums/statistics'; @@ -476,14 +483,15 @@ class AlbumsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// Retrieve album statistics /// /// Returns statistics about the albums available to the authenticated user. - Future getAlbumStatistics() async { - final response = await getAlbumStatisticsWithHttpInfo(); + Future getAlbumStatistics({ Future? abortTrigger, }) async { + final response = await getAlbumStatisticsWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -508,12 +516,18 @@ class AlbumsApi { /// * [String] assetId: /// Filter albums containing this asset ID (ignores other parameters) /// + /// * [String] id: + /// Album ID + /// /// * [bool] isOwned: /// Filter by ownership: true = only owned, false = only shared-with-me, undefined = no filter /// /// * [bool] isShared: /// Filter by shared status: true = only shared, false = not shared, undefined = no filter - Future getAllAlbumsWithHttpInfo({ String? assetId, bool? isOwned, bool? isShared, }) async { + /// + /// * [String] name: + /// Album name (exact match) + Future getAllAlbumsWithHttpInfo({ String? assetId, String? id, bool? isOwned, bool? isShared, String? name, Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/albums'; @@ -527,12 +541,18 @@ class AlbumsApi { if (assetId != null) { queryParams.addAll(_queryParams('', 'assetId', assetId)); } + if (id != null) { + queryParams.addAll(_queryParams('', 'id', id)); + } if (isOwned != null) { queryParams.addAll(_queryParams('', 'isOwned', isOwned)); } if (isShared != null) { queryParams.addAll(_queryParams('', 'isShared', isShared)); } + if (name != null) { + queryParams.addAll(_queryParams('', 'name', name)); + } const contentTypes = []; @@ -545,6 +565,7 @@ class AlbumsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -557,13 +578,19 @@ class AlbumsApi { /// * [String] assetId: /// Filter albums containing this asset ID (ignores other parameters) /// + /// * [String] id: + /// Album ID + /// /// * [bool] isOwned: /// Filter by ownership: true = only owned, false = only shared-with-me, undefined = no filter /// /// * [bool] isShared: /// Filter by shared status: true = only shared, false = not shared, undefined = no filter - Future?> getAllAlbums({ String? assetId, bool? isOwned, bool? isShared, }) async { - final response = await getAllAlbumsWithHttpInfo( assetId: assetId, isOwned: isOwned, isShared: isShared, ); + /// + /// * [String] name: + /// Album name (exact match) + Future?> getAllAlbums({ String? assetId, String? id, bool? isOwned, bool? isShared, String? name, Future? abortTrigger, }) async { + final response = await getAllAlbumsWithHttpInfo(assetId: assetId, id: id, isOwned: isOwned, isShared: isShared, name: name, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -591,7 +618,7 @@ class AlbumsApi { /// * [String] id (required): /// /// * [BulkIdsDto] bulkIdsDto (required): - Future removeAssetFromAlbumWithHttpInfo(String id, BulkIdsDto bulkIdsDto,) async { + Future removeAssetFromAlbumWithHttpInfo(String id, BulkIdsDto bulkIdsDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/albums/{id}/assets' .replaceAll('{id}', id); @@ -614,6 +641,7 @@ class AlbumsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -626,8 +654,8 @@ class AlbumsApi { /// * [String] id (required): /// /// * [BulkIdsDto] bulkIdsDto (required): - Future?> removeAssetFromAlbum(String id, BulkIdsDto bulkIdsDto,) async { - final response = await removeAssetFromAlbumWithHttpInfo(id, bulkIdsDto,); + Future?> removeAssetFromAlbum(String id, BulkIdsDto bulkIdsDto, { Future? abortTrigger, }) async { + final response = await removeAssetFromAlbumWithHttpInfo(id, bulkIdsDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -655,7 +683,7 @@ class AlbumsApi { /// * [String] id (required): /// /// * [String] userId (required): - Future removeUserFromAlbumWithHttpInfo(String id, String userId,) async { + Future removeUserFromAlbumWithHttpInfo(String id, String userId, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/albums/{id}/user/{userId}' .replaceAll('{id}', id) @@ -679,6 +707,7 @@ class AlbumsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -691,8 +720,8 @@ class AlbumsApi { /// * [String] id (required): /// /// * [String] userId (required): - Future removeUserFromAlbum(String id, String userId,) async { - final response = await removeUserFromAlbumWithHttpInfo(id, userId,); + Future removeUserFromAlbum(String id, String userId, { Future? abortTrigger, }) async { + final response = await removeUserFromAlbumWithHttpInfo(id, userId, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -709,7 +738,7 @@ class AlbumsApi { /// * [String] id (required): /// /// * [UpdateAlbumDto] updateAlbumDto (required): - Future updateAlbumInfoWithHttpInfo(String id, UpdateAlbumDto updateAlbumDto,) async { + Future updateAlbumInfoWithHttpInfo(String id, UpdateAlbumDto updateAlbumDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/albums/{id}' .replaceAll('{id}', id); @@ -732,6 +761,7 @@ class AlbumsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -744,8 +774,8 @@ class AlbumsApi { /// * [String] id (required): /// /// * [UpdateAlbumDto] updateAlbumDto (required): - Future updateAlbumInfo(String id, UpdateAlbumDto updateAlbumDto,) async { - final response = await updateAlbumInfoWithHttpInfo(id, updateAlbumDto,); + Future updateAlbumInfo(String id, UpdateAlbumDto updateAlbumDto, { Future? abortTrigger, }) async { + final response = await updateAlbumInfoWithHttpInfo(id, updateAlbumDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -772,7 +802,7 @@ class AlbumsApi { /// * [String] userId (required): /// /// * [UpdateAlbumUserDto] updateAlbumUserDto (required): - Future updateAlbumUserWithHttpInfo(String id, String userId, UpdateAlbumUserDto updateAlbumUserDto,) async { + Future updateAlbumUserWithHttpInfo(String id, String userId, UpdateAlbumUserDto updateAlbumUserDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/albums/{id}/user/{userId}' .replaceAll('{id}', id) @@ -796,6 +826,7 @@ class AlbumsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -810,8 +841,8 @@ class AlbumsApi { /// * [String] userId (required): /// /// * [UpdateAlbumUserDto] updateAlbumUserDto (required): - Future updateAlbumUser(String id, String userId, UpdateAlbumUserDto updateAlbumUserDto,) async { - final response = await updateAlbumUserWithHttpInfo(id, userId, updateAlbumUserDto,); + Future updateAlbumUser(String id, String userId, UpdateAlbumUserDto updateAlbumUserDto, { Future? abortTrigger, }) async { + final response = await updateAlbumUserWithHttpInfo(id, userId, updateAlbumUserDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api/api_keys_api.dart b/mobile/openapi/lib/api/api_keys_api.dart index 3ca85265c4..c26ddc263d 100644 --- a/mobile/openapi/lib/api/api_keys_api.dart +++ b/mobile/openapi/lib/api/api_keys_api.dart @@ -25,7 +25,7 @@ class APIKeysApi { /// Parameters: /// /// * [ApiKeyCreateDto] apiKeyCreateDto (required): - Future createApiKeyWithHttpInfo(ApiKeyCreateDto apiKeyCreateDto,) async { + Future createApiKeyWithHttpInfo(ApiKeyCreateDto apiKeyCreateDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/api-keys'; @@ -47,6 +47,7 @@ class APIKeysApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -57,8 +58,8 @@ class APIKeysApi { /// Parameters: /// /// * [ApiKeyCreateDto] apiKeyCreateDto (required): - Future createApiKey(ApiKeyCreateDto apiKeyCreateDto,) async { - final response = await createApiKeyWithHttpInfo(apiKeyCreateDto,); + Future createApiKey(ApiKeyCreateDto apiKeyCreateDto, { Future? abortTrigger, }) async { + final response = await createApiKeyWithHttpInfo(apiKeyCreateDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -81,7 +82,7 @@ class APIKeysApi { /// Parameters: /// /// * [String] id (required): - Future deleteApiKeyWithHttpInfo(String id,) async { + Future deleteApiKeyWithHttpInfo(String id, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/api-keys/{id}' .replaceAll('{id}', id); @@ -104,6 +105,7 @@ class APIKeysApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -114,8 +116,8 @@ class APIKeysApi { /// Parameters: /// /// * [String] id (required): - Future deleteApiKey(String id,) async { - final response = await deleteApiKeyWithHttpInfo(id,); + Future deleteApiKey(String id, { Future? abortTrigger, }) async { + final response = await deleteApiKeyWithHttpInfo(id, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -130,7 +132,7 @@ class APIKeysApi { /// Parameters: /// /// * [String] id (required): - Future getApiKeyWithHttpInfo(String id,) async { + Future getApiKeyWithHttpInfo(String id, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/api-keys/{id}' .replaceAll('{id}', id); @@ -153,6 +155,7 @@ class APIKeysApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -163,8 +166,8 @@ class APIKeysApi { /// Parameters: /// /// * [String] id (required): - Future getApiKey(String id,) async { - final response = await getApiKeyWithHttpInfo(id,); + Future getApiKey(String id, { Future? abortTrigger, }) async { + final response = await getApiKeyWithHttpInfo(id, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -183,7 +186,7 @@ class APIKeysApi { /// Retrieve all API keys of the current user. /// /// Note: This method returns the HTTP [Response]. - Future getApiKeysWithHttpInfo() async { + Future getApiKeysWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/api-keys'; @@ -205,14 +208,15 @@ class APIKeysApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// List all API keys /// /// Retrieve all API keys of the current user. - Future?> getApiKeys() async { - final response = await getApiKeysWithHttpInfo(); + Future?> getApiKeys({ Future? abortTrigger, }) async { + final response = await getApiKeysWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -234,7 +238,7 @@ class APIKeysApi { /// Retrieve the API key that is used to access this endpoint. /// /// Note: This method returns the HTTP [Response]. - Future getMyApiKeyWithHttpInfo() async { + Future getMyApiKeyWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/api-keys/me'; @@ -256,14 +260,15 @@ class APIKeysApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// Retrieve the current API key /// /// Retrieve the API key that is used to access this endpoint. - Future getMyApiKey() async { - final response = await getMyApiKeyWithHttpInfo(); + Future getMyApiKey({ Future? abortTrigger, }) async { + final response = await getMyApiKeyWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -288,7 +293,7 @@ class APIKeysApi { /// * [String] id (required): /// /// * [ApiKeyUpdateDto] apiKeyUpdateDto (required): - Future updateApiKeyWithHttpInfo(String id, ApiKeyUpdateDto apiKeyUpdateDto,) async { + Future updateApiKeyWithHttpInfo(String id, ApiKeyUpdateDto apiKeyUpdateDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/api-keys/{id}' .replaceAll('{id}', id); @@ -311,6 +316,7 @@ class APIKeysApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -323,8 +329,8 @@ class APIKeysApi { /// * [String] id (required): /// /// * [ApiKeyUpdateDto] apiKeyUpdateDto (required): - Future updateApiKey(String id, ApiKeyUpdateDto apiKeyUpdateDto,) async { - final response = await updateApiKeyWithHttpInfo(id, apiKeyUpdateDto,); + Future updateApiKey(String id, ApiKeyUpdateDto apiKeyUpdateDto, { Future? abortTrigger, }) async { + final response = await updateApiKeyWithHttpInfo(id, apiKeyUpdateDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api/assets_api.dart b/mobile/openapi/lib/api/assets_api.dart index 691c57cd3e..61d3f599cb 100644 --- a/mobile/openapi/lib/api/assets_api.dart +++ b/mobile/openapi/lib/api/assets_api.dart @@ -25,7 +25,7 @@ class AssetsApi { /// Parameters: /// /// * [AssetBulkUploadCheckDto] assetBulkUploadCheckDto (required): - Future checkBulkUploadWithHttpInfo(AssetBulkUploadCheckDto assetBulkUploadCheckDto,) async { + Future checkBulkUploadWithHttpInfo(AssetBulkUploadCheckDto assetBulkUploadCheckDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/assets/bulk-upload-check'; @@ -47,6 +47,7 @@ class AssetsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -57,8 +58,8 @@ class AssetsApi { /// Parameters: /// /// * [AssetBulkUploadCheckDto] assetBulkUploadCheckDto (required): - Future checkBulkUpload(AssetBulkUploadCheckDto assetBulkUploadCheckDto,) async { - final response = await checkBulkUploadWithHttpInfo(assetBulkUploadCheckDto,); + Future checkBulkUpload(AssetBulkUploadCheckDto assetBulkUploadCheckDto, { Future? abortTrigger, }) async { + final response = await checkBulkUploadWithHttpInfo(assetBulkUploadCheckDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -81,7 +82,7 @@ class AssetsApi { /// Parameters: /// /// * [AssetCopyDto] assetCopyDto (required): - Future copyAssetWithHttpInfo(AssetCopyDto assetCopyDto,) async { + Future copyAssetWithHttpInfo(AssetCopyDto assetCopyDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/assets/copy'; @@ -103,6 +104,7 @@ class AssetsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -113,8 +115,8 @@ class AssetsApi { /// Parameters: /// /// * [AssetCopyDto] assetCopyDto (required): - Future copyAsset(AssetCopyDto assetCopyDto,) async { - final response = await copyAssetWithHttpInfo(assetCopyDto,); + Future copyAsset(AssetCopyDto assetCopyDto, { Future? abortTrigger, }) async { + final response = await copyAssetWithHttpInfo(assetCopyDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -133,7 +135,7 @@ class AssetsApi { /// /// * [String] key (required): /// Metadata key - Future deleteAssetMetadataWithHttpInfo(String id, String key,) async { + Future deleteAssetMetadataWithHttpInfo(String id, String key, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/assets/{id}/metadata/{key}' .replaceAll('{id}', id) @@ -157,6 +159,7 @@ class AssetsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -171,8 +174,8 @@ class AssetsApi { /// /// * [String] key (required): /// Metadata key - Future deleteAssetMetadata(String id, String key,) async { - final response = await deleteAssetMetadataWithHttpInfo(id, key,); + Future deleteAssetMetadata(String id, String key, { Future? abortTrigger, }) async { + final response = await deleteAssetMetadataWithHttpInfo(id, key, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -187,7 +190,7 @@ class AssetsApi { /// Parameters: /// /// * [AssetBulkDeleteDto] assetBulkDeleteDto (required): - Future deleteAssetsWithHttpInfo(AssetBulkDeleteDto assetBulkDeleteDto,) async { + Future deleteAssetsWithHttpInfo(AssetBulkDeleteDto assetBulkDeleteDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/assets'; @@ -209,6 +212,7 @@ class AssetsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -219,8 +223,8 @@ class AssetsApi { /// Parameters: /// /// * [AssetBulkDeleteDto] assetBulkDeleteDto (required): - Future deleteAssets(AssetBulkDeleteDto assetBulkDeleteDto,) async { - final response = await deleteAssetsWithHttpInfo(assetBulkDeleteDto,); + Future deleteAssets(AssetBulkDeleteDto assetBulkDeleteDto, { Future? abortTrigger, }) async { + final response = await deleteAssetsWithHttpInfo(assetBulkDeleteDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -235,7 +239,7 @@ class AssetsApi { /// Parameters: /// /// * [AssetMetadataBulkDeleteDto] assetMetadataBulkDeleteDto (required): - Future deleteBulkAssetMetadataWithHttpInfo(AssetMetadataBulkDeleteDto assetMetadataBulkDeleteDto,) async { + Future deleteBulkAssetMetadataWithHttpInfo(AssetMetadataBulkDeleteDto assetMetadataBulkDeleteDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/assets/metadata'; @@ -257,6 +261,7 @@ class AssetsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -267,8 +272,8 @@ class AssetsApi { /// Parameters: /// /// * [AssetMetadataBulkDeleteDto] assetMetadataBulkDeleteDto (required): - Future deleteBulkAssetMetadata(AssetMetadataBulkDeleteDto assetMetadataBulkDeleteDto,) async { - final response = await deleteBulkAssetMetadataWithHttpInfo(assetMetadataBulkDeleteDto,); + Future deleteBulkAssetMetadata(AssetMetadataBulkDeleteDto assetMetadataBulkDeleteDto, { Future? abortTrigger, }) async { + final response = await deleteBulkAssetMetadataWithHttpInfo(assetMetadataBulkDeleteDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -290,7 +295,7 @@ class AssetsApi { /// * [String] key: /// /// * [String] slug: - Future downloadAssetWithHttpInfo(String id, { bool? edited, String? key, String? slug, }) async { + Future downloadAssetWithHttpInfo(String id, { bool? edited, String? key, String? slug, Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/assets/{id}/original' .replaceAll('{id}', id); @@ -323,6 +328,7 @@ class AssetsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -340,8 +346,8 @@ class AssetsApi { /// * [String] key: /// /// * [String] slug: - Future downloadAsset(String id, { bool? edited, String? key, String? slug, }) async { - final response = await downloadAssetWithHttpInfo(id, edited: edited, key: key, slug: slug, ); + Future downloadAsset(String id, { bool? edited, String? key, String? slug, Future? abortTrigger, }) async { + final response = await downloadAssetWithHttpInfo(id, edited: edited, key: key, slug: slug, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -366,7 +372,7 @@ class AssetsApi { /// * [String] id (required): /// /// * [AssetEditsCreateDto] assetEditsCreateDto (required): - Future editAssetWithHttpInfo(String id, AssetEditsCreateDto assetEditsCreateDto,) async { + Future editAssetWithHttpInfo(String id, AssetEditsCreateDto assetEditsCreateDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/assets/{id}/edits' .replaceAll('{id}', id); @@ -389,6 +395,7 @@ class AssetsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -401,8 +408,8 @@ class AssetsApi { /// * [String] id (required): /// /// * [AssetEditsCreateDto] assetEditsCreateDto (required): - Future editAsset(String id, AssetEditsCreateDto assetEditsCreateDto,) async { - final response = await editAssetWithHttpInfo(id, assetEditsCreateDto,); + Future editAsset(String id, AssetEditsCreateDto assetEditsCreateDto, { Future? abortTrigger, }) async { + final response = await editAssetWithHttpInfo(id, assetEditsCreateDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -416,6 +423,76 @@ class AssetsApi { return null; } + /// End HLS streaming session + /// + /// Releases server resources for the streaming session. + /// + /// Note: This method returns the HTTP [Response]. + /// + /// Parameters: + /// + /// * [String] id (required): + /// + /// * [String] sessionId (required): + /// + /// * [String] key: + /// + /// * [String] slug: + Future endSessionWithHttpInfo(String id, String sessionId, { String? key, String? slug, Future? abortTrigger, }) async { + // ignore: prefer_const_declarations + final apiPath = r'/assets/{id}/video/stream/{sessionId}' + .replaceAll('{id}', id) + .replaceAll('{sessionId}', sessionId); + + // 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 = []; + + + return apiClient.invokeAPI( + apiPath, + 'DELETE', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, + ); + } + + /// End HLS streaming session + /// + /// Releases server resources for the streaming session. + /// + /// Parameters: + /// + /// * [String] id (required): + /// + /// * [String] sessionId (required): + /// + /// * [String] key: + /// + /// * [String] slug: + Future endSession(String id, String sessionId, { String? key, String? slug, Future? abortTrigger, }) async { + final response = await endSessionWithHttpInfo(id, sessionId, key: key, slug: slug, abortTrigger: abortTrigger,); + if (response.statusCode >= HttpStatus.badRequest) { + throw ApiException(response.statusCode, await _decodeBodyBytes(response)); + } + } + /// Retrieve edits for an existing asset /// /// Retrieve a series of edit actions (crop, rotate, mirror) associated with the specified asset. @@ -425,7 +502,7 @@ class AssetsApi { /// Parameters: /// /// * [String] id (required): - Future getAssetEditsWithHttpInfo(String id,) async { + Future getAssetEditsWithHttpInfo(String id, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/assets/{id}/edits' .replaceAll('{id}', id); @@ -448,6 +525,7 @@ class AssetsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -458,8 +536,8 @@ class AssetsApi { /// Parameters: /// /// * [String] id (required): - Future getAssetEdits(String id,) async { - final response = await getAssetEditsWithHttpInfo(id,); + Future getAssetEdits(String id, { Future? abortTrigger, }) async { + final response = await getAssetEditsWithHttpInfo(id, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -486,7 +564,7 @@ class AssetsApi { /// * [String] key: /// /// * [String] slug: - Future getAssetInfoWithHttpInfo(String id, { String? key, String? slug, }) async { + Future getAssetInfoWithHttpInfo(String id, { String? key, String? slug, Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/assets/{id}' .replaceAll('{id}', id); @@ -516,6 +594,7 @@ class AssetsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -530,8 +609,8 @@ class AssetsApi { /// * [String] key: /// /// * [String] slug: - Future getAssetInfo(String id, { String? key, String? slug, }) async { - final response = await getAssetInfoWithHttpInfo(id, key: key, slug: slug, ); + Future getAssetInfo(String id, { String? key, String? slug, Future? abortTrigger, }) async { + final response = await getAssetInfoWithHttpInfo(id, key: key, slug: slug, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -554,7 +633,7 @@ class AssetsApi { /// Parameters: /// /// * [String] id (required): - Future getAssetMetadataWithHttpInfo(String id,) async { + Future getAssetMetadataWithHttpInfo(String id, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/assets/{id}/metadata' .replaceAll('{id}', id); @@ -577,6 +656,7 @@ class AssetsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -587,8 +667,8 @@ class AssetsApi { /// Parameters: /// /// * [String] id (required): - Future?> getAssetMetadata(String id,) async { - final response = await getAssetMetadataWithHttpInfo(id,); + Future?> getAssetMetadata(String id, { Future? abortTrigger, }) async { + final response = await getAssetMetadataWithHttpInfo(id, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -618,7 +698,7 @@ class AssetsApi { /// /// * [String] key (required): /// Metadata key - Future getAssetMetadataByKeyWithHttpInfo(String id, String key,) async { + Future getAssetMetadataByKeyWithHttpInfo(String id, String key, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/assets/{id}/metadata/{key}' .replaceAll('{id}', id) @@ -642,6 +722,7 @@ class AssetsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -656,8 +737,8 @@ class AssetsApi { /// /// * [String] key (required): /// Metadata key - Future getAssetMetadataByKey(String id, String key,) async { - final response = await getAssetMetadataByKeyWithHttpInfo(id, key,); + Future getAssetMetadataByKey(String id, String key, { Future? abortTrigger, }) async { + final response = await getAssetMetadataByKeyWithHttpInfo(id, key, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -680,7 +761,7 @@ class AssetsApi { /// Parameters: /// /// * [String] id (required): - Future getAssetOcrWithHttpInfo(String id,) async { + Future getAssetOcrWithHttpInfo(String id, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/assets/{id}/ocr' .replaceAll('{id}', id); @@ -703,6 +784,7 @@ class AssetsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -713,8 +795,8 @@ class AssetsApi { /// Parameters: /// /// * [String] id (required): - Future?> getAssetOcr(String id,) async { - final response = await getAssetOcrWithHttpInfo(id,); + Future?> getAssetOcr(String id, { Future? abortTrigger, }) async { + final response = await getAssetOcrWithHttpInfo(id, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -746,7 +828,7 @@ class AssetsApi { /// Filter by trash status /// /// * [AssetVisibility] visibility: - Future getAssetStatisticsWithHttpInfo({ bool? isFavorite, bool? isTrashed, AssetVisibility? visibility, }) async { + Future getAssetStatisticsWithHttpInfo({ bool? isFavorite, bool? isTrashed, AssetVisibility? visibility, Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/assets/statistics'; @@ -778,6 +860,7 @@ class AssetsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -794,8 +877,8 @@ class AssetsApi { /// Filter by trash status /// /// * [AssetVisibility] visibility: - Future getAssetStatistics({ bool? isFavorite, bool? isTrashed, AssetVisibility? visibility, }) async { - final response = await getAssetStatisticsWithHttpInfo( isFavorite: isFavorite, isTrashed: isTrashed, visibility: visibility, ); + Future getAssetStatistics({ bool? isFavorite, bool? isTrashed, AssetVisibility? visibility, Future? abortTrigger, }) async { + final response = await getAssetStatisticsWithHttpInfo(isFavorite: isFavorite, isTrashed: isTrashed, visibility: visibility, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -809,6 +892,250 @@ class AssetsApi { return null; } + /// Get HLS main playlist + /// + /// Returns an HLS main playlist with all available variants for the asset. + /// + /// Note: This method returns the HTTP [Response]. + /// + /// Parameters: + /// + /// * [String] id (required): + /// + /// * [String] key: + /// + /// * [String] slug: + Future getMainPlaylistWithHttpInfo(String id, { String? key, String? slug, Future? abortTrigger, }) async { + // ignore: prefer_const_declarations + final apiPath = r'/assets/{id}/video/stream/main.m3u8' + .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 = []; + + + return apiClient.invokeAPI( + apiPath, + 'GET', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, + ); + } + + /// Get HLS main playlist + /// + /// Returns an HLS main playlist with all available variants for the asset. + /// + /// Parameters: + /// + /// * [String] id (required): + /// + /// * [String] key: + /// + /// * [String] slug: + Future getMainPlaylist(String id, { String? key, String? slug, Future? abortTrigger, }) async { + final response = await getMainPlaylistWithHttpInfo(id, key: key, slug: slug, abortTrigger: abortTrigger,); + 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), 'String',) as String; + + } + return null; + } + + /// Get HLS media playlist + /// + /// Returns an HLS media playlist for one variant of the streaming session. + /// + /// Note: This method returns the HTTP [Response]. + /// + /// Parameters: + /// + /// * [String] id (required): + /// + /// * [String] sessionId (required): + /// + /// * [int] variantIndex (required): + /// + /// * [String] key: + /// + /// * [String] slug: + Future getMediaPlaylistWithHttpInfo(String id, String sessionId, int variantIndex, { String? key, String? slug, Future? abortTrigger, }) async { + // ignore: prefer_const_declarations + final apiPath = r'/assets/{id}/video/stream/{sessionId}/{variantIndex}/playlist.m3u8' + .replaceAll('{id}', id) + .replaceAll('{sessionId}', sessionId) + .replaceAll('{variantIndex}', variantIndex.toString()); + + // 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 = []; + + + return apiClient.invokeAPI( + apiPath, + 'GET', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, + ); + } + + /// Get HLS media playlist + /// + /// Returns an HLS media playlist for one variant of the streaming session. + /// + /// Parameters: + /// + /// * [String] id (required): + /// + /// * [String] sessionId (required): + /// + /// * [int] variantIndex (required): + /// + /// * [String] key: + /// + /// * [String] slug: + Future getMediaPlaylist(String id, String sessionId, int variantIndex, { String? key, String? slug, Future? abortTrigger, }) async { + final response = await getMediaPlaylistWithHttpInfo(id, sessionId, variantIndex, key: key, slug: slug, abortTrigger: abortTrigger,); + 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), 'String',) as String; + + } + return null; + } + + /// Get HLS segment or init file + /// + /// Streams an HLS init segment (init.mp4) or media segment (seg_N.m4s). + /// + /// Note: This method returns the HTTP [Response]. + /// + /// Parameters: + /// + /// * [String] filename (required): + /// + /// * [String] id (required): + /// + /// * [String] sessionId (required): + /// + /// * [int] variantIndex (required): + /// + /// * [String] key: + /// + /// * [String] slug: + Future getSegmentWithHttpInfo(String filename, String id, String sessionId, int variantIndex, { String? key, String? slug, Future? abortTrigger, }) async { + // ignore: prefer_const_declarations + final apiPath = r'/assets/{id}/video/stream/{sessionId}/{variantIndex}/{filename}' + .replaceAll('{filename}', filename) + .replaceAll('{id}', id) + .replaceAll('{sessionId}', sessionId) + .replaceAll('{variantIndex}', variantIndex.toString()); + + // 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 = []; + + + return apiClient.invokeAPI( + apiPath, + 'GET', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, + ); + } + + /// Get HLS segment or init file + /// + /// Streams an HLS init segment (init.mp4) or media segment (seg_N.m4s). + /// + /// Parameters: + /// + /// * [String] filename (required): + /// + /// * [String] id (required): + /// + /// * [String] sessionId (required): + /// + /// * [int] variantIndex (required): + /// + /// * [String] key: + /// + /// * [String] slug: + Future getSegment(String filename, String id, String sessionId, int variantIndex, { String? key, String? slug, Future? abortTrigger, }) async { + final response = await getSegmentWithHttpInfo(filename, id, sessionId, variantIndex, key: key, slug: slug, abortTrigger: abortTrigger,); + 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), 'MultipartFile',) as MultipartFile; + + } + return null; + } + /// Play asset video /// /// Streams the video file for the specified asset. This endpoint also supports byte range requests. @@ -822,7 +1149,7 @@ class AssetsApi { /// * [String] key: /// /// * [String] slug: - Future playAssetVideoWithHttpInfo(String id, { String? key, String? slug, }) async { + Future playAssetVideoWithHttpInfo(String id, { String? key, String? slug, Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/assets/{id}/video/playback' .replaceAll('{id}', id); @@ -852,6 +1179,7 @@ class AssetsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -866,8 +1194,8 @@ class AssetsApi { /// * [String] key: /// /// * [String] slug: - Future playAssetVideo(String id, { String? key, String? slug, }) async { - final response = await playAssetVideoWithHttpInfo(id, key: key, slug: slug, ); + Future playAssetVideo(String id, { String? key, String? slug, Future? abortTrigger, }) async { + final response = await playAssetVideoWithHttpInfo(id, key: key, slug: slug, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -890,7 +1218,7 @@ class AssetsApi { /// Parameters: /// /// * [String] id (required): - Future removeAssetEditsWithHttpInfo(String id,) async { + Future removeAssetEditsWithHttpInfo(String id, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/assets/{id}/edits' .replaceAll('{id}', id); @@ -913,6 +1241,7 @@ class AssetsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -923,8 +1252,8 @@ class AssetsApi { /// Parameters: /// /// * [String] id (required): - Future removeAssetEdits(String id,) async { - final response = await removeAssetEditsWithHttpInfo(id,); + Future removeAssetEdits(String id, { Future? abortTrigger, }) async { + final response = await removeAssetEditsWithHttpInfo(id, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -939,7 +1268,7 @@ class AssetsApi { /// Parameters: /// /// * [AssetJobsDto] assetJobsDto (required): - Future runAssetJobsWithHttpInfo(AssetJobsDto assetJobsDto,) async { + Future runAssetJobsWithHttpInfo(AssetJobsDto assetJobsDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/assets/jobs'; @@ -961,6 +1290,7 @@ class AssetsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -971,8 +1301,8 @@ class AssetsApi { /// Parameters: /// /// * [AssetJobsDto] assetJobsDto (required): - Future runAssetJobs(AssetJobsDto assetJobsDto,) async { - final response = await runAssetJobsWithHttpInfo(assetJobsDto,); + Future runAssetJobs(AssetJobsDto assetJobsDto, { Future? abortTrigger, }) async { + final response = await runAssetJobsWithHttpInfo(assetJobsDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -989,7 +1319,7 @@ class AssetsApi { /// * [String] id (required): /// /// * [UpdateAssetDto] updateAssetDto (required): - Future updateAssetWithHttpInfo(String id, UpdateAssetDto updateAssetDto,) async { + Future updateAssetWithHttpInfo(String id, UpdateAssetDto updateAssetDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/assets/{id}' .replaceAll('{id}', id); @@ -1012,6 +1342,7 @@ class AssetsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -1024,8 +1355,8 @@ class AssetsApi { /// * [String] id (required): /// /// * [UpdateAssetDto] updateAssetDto (required): - Future updateAsset(String id, UpdateAssetDto updateAssetDto,) async { - final response = await updateAssetWithHttpInfo(id, updateAssetDto,); + Future updateAsset(String id, UpdateAssetDto updateAssetDto, { Future? abortTrigger, }) async { + final response = await updateAssetWithHttpInfo(id, updateAssetDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -1050,7 +1381,7 @@ class AssetsApi { /// * [String] id (required): /// /// * [AssetMetadataUpsertDto] assetMetadataUpsertDto (required): - Future updateAssetMetadataWithHttpInfo(String id, AssetMetadataUpsertDto assetMetadataUpsertDto,) async { + Future updateAssetMetadataWithHttpInfo(String id, AssetMetadataUpsertDto assetMetadataUpsertDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/assets/{id}/metadata' .replaceAll('{id}', id); @@ -1073,6 +1404,7 @@ class AssetsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -1085,8 +1417,8 @@ class AssetsApi { /// * [String] id (required): /// /// * [AssetMetadataUpsertDto] assetMetadataUpsertDto (required): - Future?> updateAssetMetadata(String id, AssetMetadataUpsertDto assetMetadataUpsertDto,) async { - final response = await updateAssetMetadataWithHttpInfo(id, assetMetadataUpsertDto,); + Future?> updateAssetMetadata(String id, AssetMetadataUpsertDto assetMetadataUpsertDto, { Future? abortTrigger, }) async { + final response = await updateAssetMetadataWithHttpInfo(id, assetMetadataUpsertDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -1112,7 +1444,7 @@ class AssetsApi { /// Parameters: /// /// * [AssetBulkUpdateDto] assetBulkUpdateDto (required): - Future updateAssetsWithHttpInfo(AssetBulkUpdateDto assetBulkUpdateDto,) async { + Future updateAssetsWithHttpInfo(AssetBulkUpdateDto assetBulkUpdateDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/assets'; @@ -1134,6 +1466,7 @@ class AssetsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -1144,8 +1477,8 @@ class AssetsApi { /// Parameters: /// /// * [AssetBulkUpdateDto] assetBulkUpdateDto (required): - Future updateAssets(AssetBulkUpdateDto assetBulkUpdateDto,) async { - final response = await updateAssetsWithHttpInfo(assetBulkUpdateDto,); + Future updateAssets(AssetBulkUpdateDto assetBulkUpdateDto, { Future? abortTrigger, }) async { + final response = await updateAssetsWithHttpInfo(assetBulkUpdateDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -1160,7 +1493,7 @@ class AssetsApi { /// Parameters: /// /// * [AssetMetadataBulkUpsertDto] assetMetadataBulkUpsertDto (required): - Future updateBulkAssetMetadataWithHttpInfo(AssetMetadataBulkUpsertDto assetMetadataBulkUpsertDto,) async { + Future updateBulkAssetMetadataWithHttpInfo(AssetMetadataBulkUpsertDto assetMetadataBulkUpsertDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/assets/metadata'; @@ -1182,6 +1515,7 @@ class AssetsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -1192,8 +1526,8 @@ class AssetsApi { /// Parameters: /// /// * [AssetMetadataBulkUpsertDto] assetMetadataBulkUpsertDto (required): - Future?> updateBulkAssetMetadata(AssetMetadataBulkUpsertDto assetMetadataBulkUpsertDto,) async { - final response = await updateBulkAssetMetadataWithHttpInfo(assetMetadataBulkUpsertDto,); + Future?> updateBulkAssetMetadata(AssetMetadataBulkUpsertDto assetMetadataBulkUpsertDto, { Future? abortTrigger, }) async { + final response = await updateBulkAssetMetadataWithHttpInfo(assetMetadataBulkUpsertDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -1253,7 +1587,7 @@ class AssetsApi { /// Sidecar file data /// /// * [AssetVisibility] visibility: - Future uploadAssetWithHttpInfo(MultipartFile assetData, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? slug, String? xImmichChecksum, int? duration, String? filename, bool? isFavorite, String? livePhotoVideoId, List? metadata, MultipartFile? sidecarData, AssetVisibility? visibility, }) async { + Future uploadAssetWithHttpInfo(MultipartFile assetData, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? slug, String? xImmichChecksum, int? duration, String? filename, bool? isFavorite, String? livePhotoVideoId, List? metadata, MultipartFile? sidecarData, AssetVisibility? visibility, Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/assets'; @@ -1333,6 +1667,7 @@ class AssetsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -1377,8 +1712,8 @@ class AssetsApi { /// Sidecar file data /// /// * [AssetVisibility] visibility: - Future uploadAsset(MultipartFile assetData, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? slug, String? xImmichChecksum, int? duration, String? filename, bool? isFavorite, String? livePhotoVideoId, List? metadata, MultipartFile? sidecarData, AssetVisibility? visibility, }) async { - final response = await uploadAssetWithHttpInfo(assetData, fileCreatedAt, fileModifiedAt, key: key, slug: slug, xImmichChecksum: xImmichChecksum, duration: duration, filename: filename, isFavorite: isFavorite, livePhotoVideoId: livePhotoVideoId, metadata: metadata, sidecarData: sidecarData, visibility: visibility, ); + Future uploadAsset(MultipartFile assetData, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? slug, String? xImmichChecksum, int? duration, String? filename, bool? isFavorite, String? livePhotoVideoId, List? metadata, MultipartFile? sidecarData, AssetVisibility? visibility, Future? abortTrigger, }) async { + final response = await uploadAssetWithHttpInfo(assetData, fileCreatedAt, fileModifiedAt, key: key, slug: slug, xImmichChecksum: xImmichChecksum, duration: duration, filename: filename, isFavorite: isFavorite, livePhotoVideoId: livePhotoVideoId, metadata: metadata, sidecarData: sidecarData, visibility: visibility, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -1410,7 +1745,7 @@ class AssetsApi { /// * [AssetMediaSize] size: /// /// * [String] slug: - Future viewAssetWithHttpInfo(String id, { bool? edited, String? key, AssetMediaSize? size, String? slug, }) async { + Future viewAssetWithHttpInfo(String id, { bool? edited, String? key, AssetMediaSize? size, String? slug, Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/assets/{id}/thumbnail' .replaceAll('{id}', id); @@ -1446,6 +1781,7 @@ class AssetsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -1465,8 +1801,8 @@ class AssetsApi { /// * [AssetMediaSize] size: /// /// * [String] slug: - Future viewAsset(String id, { bool? edited, String? key, AssetMediaSize? size, String? slug, }) async { - final response = await viewAssetWithHttpInfo(id, edited: edited, key: key, size: size, slug: slug, ); + Future viewAsset(String id, { bool? edited, String? key, AssetMediaSize? size, String? slug, Future? abortTrigger, }) async { + final response = await viewAssetWithHttpInfo(id, edited: edited, key: key, size: size, slug: slug, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api/authentication_admin_api.dart b/mobile/openapi/lib/api/authentication_admin_api.dart index 0a4b91ebc3..2c107891b3 100644 --- a/mobile/openapi/lib/api/authentication_admin_api.dart +++ b/mobile/openapi/lib/api/authentication_admin_api.dart @@ -21,7 +21,7 @@ class AuthenticationAdminApi { /// Unlinks all OAuth accounts associated with user accounts in the system. /// /// Note: This method returns the HTTP [Response]. - Future unlinkAllOAuthAccountsAdminWithHttpInfo() async { + Future unlinkAllOAuthAccountsAdminWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/admin/auth/unlink-all'; @@ -43,14 +43,15 @@ class AuthenticationAdminApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// Unlink all OAuth accounts /// /// Unlinks all OAuth accounts associated with user accounts in the system. - Future unlinkAllOAuthAccountsAdmin() async { - final response = await unlinkAllOAuthAccountsAdminWithHttpInfo(); + Future unlinkAllOAuthAccountsAdmin({ Future? abortTrigger, }) async { + final response = await unlinkAllOAuthAccountsAdminWithHttpInfo(abortTrigger: abortTrigger,); 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 e1219f2c03..8e088d040b 100644 --- a/mobile/openapi/lib/api/authentication_api.dart +++ b/mobile/openapi/lib/api/authentication_api.dart @@ -25,7 +25,7 @@ class AuthenticationApi { /// Parameters: /// /// * [ChangePasswordDto] changePasswordDto (required): - Future changePasswordWithHttpInfo(ChangePasswordDto changePasswordDto,) async { + Future changePasswordWithHttpInfo(ChangePasswordDto changePasswordDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/auth/change-password'; @@ -47,6 +47,7 @@ class AuthenticationApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -57,8 +58,8 @@ class AuthenticationApi { /// Parameters: /// /// * [ChangePasswordDto] changePasswordDto (required): - Future changePassword(ChangePasswordDto changePasswordDto,) async { - final response = await changePasswordWithHttpInfo(changePasswordDto,); + Future changePassword(ChangePasswordDto changePasswordDto, { Future? abortTrigger, }) async { + final response = await changePasswordWithHttpInfo(changePasswordDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -81,7 +82,7 @@ class AuthenticationApi { /// Parameters: /// /// * [PinCodeChangeDto] pinCodeChangeDto (required): - Future changePinCodeWithHttpInfo(PinCodeChangeDto pinCodeChangeDto,) async { + Future changePinCodeWithHttpInfo(PinCodeChangeDto pinCodeChangeDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/auth/pin-code'; @@ -103,6 +104,7 @@ class AuthenticationApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -113,8 +115,8 @@ class AuthenticationApi { /// Parameters: /// /// * [PinCodeChangeDto] pinCodeChangeDto (required): - Future changePinCode(PinCodeChangeDto pinCodeChangeDto,) async { - final response = await changePinCodeWithHttpInfo(pinCodeChangeDto,); + Future changePinCode(PinCodeChangeDto pinCodeChangeDto, { Future? abortTrigger, }) async { + final response = await changePinCodeWithHttpInfo(pinCodeChangeDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -129,7 +131,7 @@ class AuthenticationApi { /// Parameters: /// /// * [OAuthCallbackDto] oAuthCallbackDto (required): - Future finishOAuthWithHttpInfo(OAuthCallbackDto oAuthCallbackDto,) async { + Future finishOAuthWithHttpInfo(OAuthCallbackDto oAuthCallbackDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/oauth/callback'; @@ -151,6 +153,7 @@ class AuthenticationApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -161,8 +164,8 @@ class AuthenticationApi { /// Parameters: /// /// * [OAuthCallbackDto] oAuthCallbackDto (required): - Future finishOAuth(OAuthCallbackDto oAuthCallbackDto,) async { - final response = await finishOAuthWithHttpInfo(oAuthCallbackDto,); + Future finishOAuth(OAuthCallbackDto oAuthCallbackDto, { Future? abortTrigger, }) async { + final response = await finishOAuthWithHttpInfo(oAuthCallbackDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -181,7 +184,7 @@ class AuthenticationApi { /// Get information about the current session, including whether the user has a password, and if the session can access locked assets. /// /// Note: This method returns the HTTP [Response]. - Future getAuthStatusWithHttpInfo() async { + Future getAuthStatusWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/auth/status'; @@ -203,14 +206,15 @@ class AuthenticationApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// Retrieve auth status /// /// Get information about the current session, including whether the user has a password, and if the session can access locked assets. - Future getAuthStatus() async { - final response = await getAuthStatusWithHttpInfo(); + Future getAuthStatus({ Future? abortTrigger, }) async { + final response = await getAuthStatusWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -233,7 +237,7 @@ class AuthenticationApi { /// Parameters: /// /// * [OAuthCallbackDto] oAuthCallbackDto (required): - Future linkOAuthAccountWithHttpInfo(OAuthCallbackDto oAuthCallbackDto,) async { + Future linkOAuthAccountWithHttpInfo(OAuthCallbackDto oAuthCallbackDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/oauth/link'; @@ -255,6 +259,7 @@ class AuthenticationApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -265,8 +270,8 @@ class AuthenticationApi { /// Parameters: /// /// * [OAuthCallbackDto] oAuthCallbackDto (required): - Future linkOAuthAccount(OAuthCallbackDto oAuthCallbackDto,) async { - final response = await linkOAuthAccountWithHttpInfo(oAuthCallbackDto,); + Future linkOAuthAccount(OAuthCallbackDto oAuthCallbackDto, { Future? abortTrigger, }) async { + final response = await linkOAuthAccountWithHttpInfo(oAuthCallbackDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -285,7 +290,7 @@ class AuthenticationApi { /// Remove elevated access to locked assets from the current session. /// /// Note: This method returns the HTTP [Response]. - Future lockAuthSessionWithHttpInfo() async { + Future lockAuthSessionWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/auth/session/lock'; @@ -307,14 +312,15 @@ class AuthenticationApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// Lock auth session /// /// Remove elevated access to locked assets from the current session. - Future lockAuthSession() async { - final response = await lockAuthSessionWithHttpInfo(); + Future lockAuthSession({ Future? abortTrigger, }) async { + final response = await lockAuthSessionWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -329,7 +335,7 @@ class AuthenticationApi { /// Parameters: /// /// * [LoginCredentialDto] loginCredentialDto (required): - Future loginWithHttpInfo(LoginCredentialDto loginCredentialDto,) async { + Future loginWithHttpInfo(LoginCredentialDto loginCredentialDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/auth/login'; @@ -351,6 +357,7 @@ class AuthenticationApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -361,8 +368,8 @@ class AuthenticationApi { /// Parameters: /// /// * [LoginCredentialDto] loginCredentialDto (required): - Future login(LoginCredentialDto loginCredentialDto,) async { - final response = await loginWithHttpInfo(loginCredentialDto,); + Future login(LoginCredentialDto loginCredentialDto, { Future? abortTrigger, }) async { + final response = await loginWithHttpInfo(loginCredentialDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -381,7 +388,7 @@ class AuthenticationApi { /// Logout the current user and invalidate the session token. /// /// Note: This method returns the HTTP [Response]. - Future logoutWithHttpInfo() async { + Future logoutWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/auth/logout'; @@ -403,14 +410,15 @@ class AuthenticationApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// Logout /// /// Logout the current user and invalidate the session token. - Future logout() async { - final response = await logoutWithHttpInfo(); + Future logout({ Future? abortTrigger, }) async { + final response = await logoutWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -434,7 +442,7 @@ class AuthenticationApi { /// /// * [String] logoutToken (required): /// OAuth logout token - Future logoutOAuthWithHttpInfo(String logoutToken,) async { + Future logoutOAuthWithHttpInfo(String logoutToken, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/oauth/backchannel-logout'; @@ -459,6 +467,7 @@ class AuthenticationApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -470,8 +479,8 @@ class AuthenticationApi { /// /// * [String] logoutToken (required): /// OAuth logout token - Future logoutOAuth(String logoutToken,) async { - final response = await logoutOAuthWithHttpInfo(logoutToken,); + Future logoutOAuth(String logoutToken, { Future? abortTrigger, }) async { + final response = await logoutOAuthWithHttpInfo(logoutToken, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -482,7 +491,7 @@ class AuthenticationApi { /// Requests to this URL are automatically forwarded to the mobile app, and is used in some cases for OAuth redirecting. /// /// Note: This method returns the HTTP [Response]. - Future redirectOAuthToMobileWithHttpInfo() async { + Future redirectOAuthToMobileWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/oauth/mobile-redirect'; @@ -504,14 +513,15 @@ class AuthenticationApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// Redirect OAuth to mobile /// /// Requests to this URL are automatically forwarded to the mobile app, and is used in some cases for OAuth redirecting. - Future redirectOAuthToMobile() async { - final response = await redirectOAuthToMobileWithHttpInfo(); + Future redirectOAuthToMobile({ Future? abortTrigger, }) async { + final response = await redirectOAuthToMobileWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -526,7 +536,7 @@ class AuthenticationApi { /// Parameters: /// /// * [PinCodeResetDto] pinCodeResetDto (required): - Future resetPinCodeWithHttpInfo(PinCodeResetDto pinCodeResetDto,) async { + Future resetPinCodeWithHttpInfo(PinCodeResetDto pinCodeResetDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/auth/pin-code'; @@ -548,6 +558,7 @@ class AuthenticationApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -558,8 +569,8 @@ class AuthenticationApi { /// Parameters: /// /// * [PinCodeResetDto] pinCodeResetDto (required): - Future resetPinCode(PinCodeResetDto pinCodeResetDto,) async { - final response = await resetPinCodeWithHttpInfo(pinCodeResetDto,); + Future resetPinCode(PinCodeResetDto pinCodeResetDto, { Future? abortTrigger, }) async { + final response = await resetPinCodeWithHttpInfo(pinCodeResetDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -574,7 +585,7 @@ class AuthenticationApi { /// Parameters: /// /// * [PinCodeSetupDto] pinCodeSetupDto (required): - Future setupPinCodeWithHttpInfo(PinCodeSetupDto pinCodeSetupDto,) async { + Future setupPinCodeWithHttpInfo(PinCodeSetupDto pinCodeSetupDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/auth/pin-code'; @@ -596,6 +607,7 @@ class AuthenticationApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -606,8 +618,8 @@ class AuthenticationApi { /// Parameters: /// /// * [PinCodeSetupDto] pinCodeSetupDto (required): - Future setupPinCode(PinCodeSetupDto pinCodeSetupDto,) async { - final response = await setupPinCodeWithHttpInfo(pinCodeSetupDto,); + Future setupPinCode(PinCodeSetupDto pinCodeSetupDto, { Future? abortTrigger, }) async { + final response = await setupPinCodeWithHttpInfo(pinCodeSetupDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -622,7 +634,7 @@ class AuthenticationApi { /// Parameters: /// /// * [SignUpDto] signUpDto (required): - Future signUpAdminWithHttpInfo(SignUpDto signUpDto,) async { + Future signUpAdminWithHttpInfo(SignUpDto signUpDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/auth/admin-sign-up'; @@ -644,6 +656,7 @@ class AuthenticationApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -654,8 +667,8 @@ class AuthenticationApi { /// Parameters: /// /// * [SignUpDto] signUpDto (required): - Future signUpAdmin(SignUpDto signUpDto,) async { - final response = await signUpAdminWithHttpInfo(signUpDto,); + Future signUpAdmin(SignUpDto signUpDto, { Future? abortTrigger, }) async { + final response = await signUpAdminWithHttpInfo(signUpDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -678,7 +691,7 @@ class AuthenticationApi { /// Parameters: /// /// * [OAuthConfigDto] oAuthConfigDto (required): - Future startOAuthWithHttpInfo(OAuthConfigDto oAuthConfigDto,) async { + Future startOAuthWithHttpInfo(OAuthConfigDto oAuthConfigDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/oauth/authorize'; @@ -700,6 +713,7 @@ class AuthenticationApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -710,8 +724,8 @@ class AuthenticationApi { /// Parameters: /// /// * [OAuthConfigDto] oAuthConfigDto (required): - Future startOAuth(OAuthConfigDto oAuthConfigDto,) async { - final response = await startOAuthWithHttpInfo(oAuthConfigDto,); + Future startOAuth(OAuthConfigDto oAuthConfigDto, { Future? abortTrigger, }) async { + final response = await startOAuthWithHttpInfo(oAuthConfigDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -730,7 +744,7 @@ class AuthenticationApi { /// Unlink the OAuth account from the authenticated user. /// /// Note: This method returns the HTTP [Response]. - Future unlinkOAuthAccountWithHttpInfo() async { + Future unlinkOAuthAccountWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/oauth/unlink'; @@ -752,14 +766,15 @@ class AuthenticationApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// Unlink OAuth account /// /// Unlink the OAuth account from the authenticated user. - Future unlinkOAuthAccount() async { - final response = await unlinkOAuthAccountWithHttpInfo(); + Future unlinkOAuthAccount({ Future? abortTrigger, }) async { + final response = await unlinkOAuthAccountWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -782,7 +797,7 @@ class AuthenticationApi { /// Parameters: /// /// * [SessionUnlockDto] sessionUnlockDto (required): - Future unlockAuthSessionWithHttpInfo(SessionUnlockDto sessionUnlockDto,) async { + Future unlockAuthSessionWithHttpInfo(SessionUnlockDto sessionUnlockDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/auth/session/unlock'; @@ -804,6 +819,7 @@ class AuthenticationApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -814,8 +830,8 @@ class AuthenticationApi { /// Parameters: /// /// * [SessionUnlockDto] sessionUnlockDto (required): - Future unlockAuthSession(SessionUnlockDto sessionUnlockDto,) async { - final response = await unlockAuthSessionWithHttpInfo(sessionUnlockDto,); + Future unlockAuthSession(SessionUnlockDto sessionUnlockDto, { Future? abortTrigger, }) async { + final response = await unlockAuthSessionWithHttpInfo(sessionUnlockDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -826,7 +842,7 @@ class AuthenticationApi { /// Validate the current authorization method is still valid. /// /// Note: This method returns the HTTP [Response]. - Future validateAccessTokenWithHttpInfo() async { + Future validateAccessTokenWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/auth/validateToken'; @@ -848,14 +864,15 @@ class AuthenticationApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// Validate access token /// /// Validate the current authorization method is still valid. - Future validateAccessToken() async { - final response = await validateAccessTokenWithHttpInfo(); + Future validateAccessToken({ Future? abortTrigger, }) async { + final response = await validateAccessTokenWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api/database_backups_admin_api.dart b/mobile/openapi/lib/api/database_backups_admin_api.dart index 768185db1e..ba393833b5 100644 --- a/mobile/openapi/lib/api/database_backups_admin_api.dart +++ b/mobile/openapi/lib/api/database_backups_admin_api.dart @@ -25,7 +25,7 @@ class DatabaseBackupsAdminApi { /// Parameters: /// /// * [DatabaseBackupDeleteDto] databaseBackupDeleteDto (required): - Future deleteDatabaseBackupWithHttpInfo(DatabaseBackupDeleteDto databaseBackupDeleteDto,) async { + Future deleteDatabaseBackupWithHttpInfo(DatabaseBackupDeleteDto databaseBackupDeleteDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/admin/database-backups'; @@ -47,6 +47,7 @@ class DatabaseBackupsAdminApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -57,8 +58,8 @@ class DatabaseBackupsAdminApi { /// Parameters: /// /// * [DatabaseBackupDeleteDto] databaseBackupDeleteDto (required): - Future deleteDatabaseBackup(DatabaseBackupDeleteDto databaseBackupDeleteDto,) async { - final response = await deleteDatabaseBackupWithHttpInfo(databaseBackupDeleteDto,); + Future deleteDatabaseBackup(DatabaseBackupDeleteDto databaseBackupDeleteDto, { Future? abortTrigger, }) async { + final response = await deleteDatabaseBackupWithHttpInfo(databaseBackupDeleteDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -73,7 +74,7 @@ class DatabaseBackupsAdminApi { /// Parameters: /// /// * [String] filename (required): - Future downloadDatabaseBackupWithHttpInfo(String filename,) async { + Future downloadDatabaseBackupWithHttpInfo(String filename, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/admin/database-backups/{filename}' .replaceAll('{filename}', filename); @@ -96,6 +97,7 @@ class DatabaseBackupsAdminApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -106,8 +108,8 @@ class DatabaseBackupsAdminApi { /// Parameters: /// /// * [String] filename (required): - Future downloadDatabaseBackup(String filename,) async { - final response = await downloadDatabaseBackupWithHttpInfo(filename,); + Future downloadDatabaseBackup(String filename, { Future? abortTrigger, }) async { + final response = await downloadDatabaseBackupWithHttpInfo(filename, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -126,7 +128,7 @@ class DatabaseBackupsAdminApi { /// Get the list of the successful and failed backups /// /// Note: This method returns the HTTP [Response]. - Future listDatabaseBackupsWithHttpInfo() async { + Future listDatabaseBackupsWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/admin/database-backups'; @@ -148,14 +150,15 @@ class DatabaseBackupsAdminApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// List database backups /// /// Get the list of the successful and failed backups - Future listDatabaseBackups() async { - final response = await listDatabaseBackupsWithHttpInfo(); + Future listDatabaseBackups({ Future? abortTrigger, }) async { + final response = await listDatabaseBackupsWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -174,7 +177,7 @@ class DatabaseBackupsAdminApi { /// Put Immich into maintenance mode to restore a backup (Immich must not be configured) /// /// Note: This method returns the HTTP [Response]. - Future startDatabaseRestoreFlowWithHttpInfo() async { + Future startDatabaseRestoreFlowWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/admin/database-backups/start-restore'; @@ -196,14 +199,15 @@ class DatabaseBackupsAdminApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// Start database backup restore flow /// /// Put Immich into maintenance mode to restore a backup (Immich must not be configured) - Future startDatabaseRestoreFlow() async { - final response = await startDatabaseRestoreFlowWithHttpInfo(); + Future startDatabaseRestoreFlow({ Future? abortTrigger, }) async { + final response = await startDatabaseRestoreFlowWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -219,7 +223,7 @@ class DatabaseBackupsAdminApi { /// /// * [MultipartFile] file: /// Database backup file - Future uploadDatabaseBackupWithHttpInfo({ MultipartFile? file, }) async { + Future uploadDatabaseBackupWithHttpInfo({ MultipartFile? file, Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/admin/database-backups/upload'; @@ -251,6 +255,7 @@ class DatabaseBackupsAdminApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -262,8 +267,8 @@ class DatabaseBackupsAdminApi { /// /// * [MultipartFile] file: /// Database backup file - Future uploadDatabaseBackup({ MultipartFile? file, }) async { - final response = await uploadDatabaseBackupWithHttpInfo( file: file, ); + Future uploadDatabaseBackup({ MultipartFile? file, Future? abortTrigger, }) async { + final response = await uploadDatabaseBackupWithHttpInfo(file: file, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api/deprecated_api.dart b/mobile/openapi/lib/api/deprecated_api.dart index a437cd5837..a8e2deab44 100644 --- a/mobile/openapi/lib/api/deprecated_api.dart +++ b/mobile/openapi/lib/api/deprecated_api.dart @@ -25,7 +25,7 @@ class DeprecatedApi { /// Parameters: /// /// * [String] id (required): - Future createPartnerDeprecatedWithHttpInfo(String id,) async { + Future createPartnerDeprecatedWithHttpInfo(String id, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/partners/{id}' .replaceAll('{id}', id); @@ -48,6 +48,7 @@ class DeprecatedApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -58,8 +59,8 @@ class DeprecatedApi { /// Parameters: /// /// * [String] id (required): - Future createPartnerDeprecated(String id,) async { - final response = await createPartnerDeprecatedWithHttpInfo(id,); + Future createPartnerDeprecated(String id, { Future? abortTrigger, }) async { + final response = await createPartnerDeprecatedWithHttpInfo(id, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -78,7 +79,7 @@ class DeprecatedApi { /// Retrieve the counts of the current queue, as well as the current status. /// /// Note: This method returns the HTTP [Response]. - Future getQueuesLegacyWithHttpInfo() async { + Future getQueuesLegacyWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/jobs'; @@ -100,14 +101,15 @@ class DeprecatedApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// Retrieve queue counts and status /// /// Retrieve the counts of the current queue, as well as the current status. - Future getQueuesLegacy() async { - final response = await getQueuesLegacyWithHttpInfo(); + Future getQueuesLegacy({ Future? abortTrigger, }) async { + final response = await getQueuesLegacyWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -132,7 +134,7 @@ class DeprecatedApi { /// * [QueueName] name (required): /// /// * [QueueCommandDto] queueCommandDto (required): - Future runQueueCommandLegacyWithHttpInfo(QueueName name, QueueCommandDto queueCommandDto,) async { + Future runQueueCommandLegacyWithHttpInfo(QueueName name, QueueCommandDto queueCommandDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/jobs/{name}' .replaceAll('{name}', name.toString()); @@ -155,6 +157,7 @@ class DeprecatedApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -167,8 +170,8 @@ class DeprecatedApi { /// * [QueueName] name (required): /// /// * [QueueCommandDto] queueCommandDto (required): - Future runQueueCommandLegacy(QueueName name, QueueCommandDto queueCommandDto,) async { - final response = await runQueueCommandLegacyWithHttpInfo(name, queueCommandDto,); + Future runQueueCommandLegacy(QueueName name, QueueCommandDto queueCommandDto, { Future? abortTrigger, }) async { + final response = await runQueueCommandLegacyWithHttpInfo(name, queueCommandDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api/download_api.dart b/mobile/openapi/lib/api/download_api.dart index 4d0c5c8165..ac26259277 100644 --- a/mobile/openapi/lib/api/download_api.dart +++ b/mobile/openapi/lib/api/download_api.dart @@ -29,7 +29,7 @@ class DownloadApi { /// * [String] key: /// /// * [String] slug: - Future downloadArchiveWithHttpInfo(DownloadArchiveDto downloadArchiveDto, { String? key, String? slug, }) async { + Future downloadArchiveWithHttpInfo(DownloadArchiveDto downloadArchiveDto, { String? key, String? slug, Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/download/archive'; @@ -58,6 +58,7 @@ class DownloadApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -72,8 +73,8 @@ class DownloadApi { /// * [String] key: /// /// * [String] slug: - Future downloadArchive(DownloadArchiveDto downloadArchiveDto, { String? key, String? slug, }) async { - final response = await downloadArchiveWithHttpInfo(downloadArchiveDto, key: key, slug: slug, ); + Future downloadArchive(DownloadArchiveDto downloadArchiveDto, { String? key, String? slug, Future? abortTrigger, }) async { + final response = await downloadArchiveWithHttpInfo(downloadArchiveDto, key: key, slug: slug, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -100,7 +101,7 @@ class DownloadApi { /// * [String] key: /// /// * [String] slug: - Future getDownloadInfoWithHttpInfo(DownloadInfoDto downloadInfoDto, { String? key, String? slug, }) async { + Future getDownloadInfoWithHttpInfo(DownloadInfoDto downloadInfoDto, { String? key, String? slug, Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/download/info'; @@ -129,6 +130,7 @@ class DownloadApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -143,8 +145,8 @@ class DownloadApi { /// * [String] key: /// /// * [String] slug: - Future getDownloadInfo(DownloadInfoDto downloadInfoDto, { String? key, String? slug, }) async { - final response = await getDownloadInfoWithHttpInfo(downloadInfoDto, key: key, slug: slug, ); + Future getDownloadInfo(DownloadInfoDto downloadInfoDto, { String? key, String? slug, Future? abortTrigger, }) async { + final response = await getDownloadInfoWithHttpInfo(downloadInfoDto, key: key, slug: slug, abortTrigger: abortTrigger,); 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 9bd01281b3..357947b889 100644 --- a/mobile/openapi/lib/api/duplicates_api.dart +++ b/mobile/openapi/lib/api/duplicates_api.dart @@ -25,7 +25,7 @@ class DuplicatesApi { /// Parameters: /// /// * [String] id (required): - Future deleteDuplicateWithHttpInfo(String id,) async { + Future deleteDuplicateWithHttpInfo(String id, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/duplicates/{id}' .replaceAll('{id}', id); @@ -48,6 +48,7 @@ class DuplicatesApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -58,8 +59,8 @@ class DuplicatesApi { /// Parameters: /// /// * [String] id (required): - Future deleteDuplicate(String id,) async { - final response = await deleteDuplicateWithHttpInfo(id,); + Future deleteDuplicate(String id, { Future? abortTrigger, }) async { + final response = await deleteDuplicateWithHttpInfo(id, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -74,7 +75,7 @@ class DuplicatesApi { /// Parameters: /// /// * [BulkIdsDto] bulkIdsDto (required): - Future deleteDuplicatesWithHttpInfo(BulkIdsDto bulkIdsDto,) async { + Future deleteDuplicatesWithHttpInfo(BulkIdsDto bulkIdsDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/duplicates'; @@ -96,6 +97,7 @@ class DuplicatesApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -106,8 +108,8 @@ class DuplicatesApi { /// Parameters: /// /// * [BulkIdsDto] bulkIdsDto (required): - Future deleteDuplicates(BulkIdsDto bulkIdsDto,) async { - final response = await deleteDuplicatesWithHttpInfo(bulkIdsDto,); + Future deleteDuplicates(BulkIdsDto bulkIdsDto, { Future? abortTrigger, }) async { + final response = await deleteDuplicatesWithHttpInfo(bulkIdsDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -118,7 +120,7 @@ class DuplicatesApi { /// Retrieve a list of duplicate assets available to the authenticated user. /// /// Note: This method returns the HTTP [Response]. - Future getAssetDuplicatesWithHttpInfo() async { + Future getAssetDuplicatesWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/duplicates'; @@ -140,14 +142,15 @@ class DuplicatesApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// Retrieve duplicates /// /// Retrieve a list of duplicate assets available to the authenticated user. - Future?> getAssetDuplicates() async { - final response = await getAssetDuplicatesWithHttpInfo(); + Future?> getAssetDuplicates({ Future? abortTrigger, }) async { + final response = await getAssetDuplicatesWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -173,7 +176,7 @@ class DuplicatesApi { /// Parameters: /// /// * [DuplicateResolveDto] duplicateResolveDto (required): - Future resolveDuplicatesWithHttpInfo(DuplicateResolveDto duplicateResolveDto,) async { + Future resolveDuplicatesWithHttpInfo(DuplicateResolveDto duplicateResolveDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/duplicates/resolve'; @@ -195,6 +198,7 @@ class DuplicatesApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -205,8 +209,8 @@ class DuplicatesApi { /// Parameters: /// /// * [DuplicateResolveDto] duplicateResolveDto (required): - Future?> resolveDuplicates(DuplicateResolveDto duplicateResolveDto,) async { - final response = await resolveDuplicatesWithHttpInfo(duplicateResolveDto,); + Future?> resolveDuplicates(DuplicateResolveDto duplicateResolveDto, { Future? abortTrigger, }) async { + final response = await resolveDuplicatesWithHttpInfo(duplicateResolveDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api/faces_api.dart b/mobile/openapi/lib/api/faces_api.dart index 43d63b47b9..2a71bbbaca 100644 --- a/mobile/openapi/lib/api/faces_api.dart +++ b/mobile/openapi/lib/api/faces_api.dart @@ -25,7 +25,7 @@ class FacesApi { /// Parameters: /// /// * [AssetFaceCreateDto] assetFaceCreateDto (required): - Future createFaceWithHttpInfo(AssetFaceCreateDto assetFaceCreateDto,) async { + Future createFaceWithHttpInfo(AssetFaceCreateDto assetFaceCreateDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/faces'; @@ -47,6 +47,7 @@ class FacesApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -57,8 +58,8 @@ class FacesApi { /// Parameters: /// /// * [AssetFaceCreateDto] assetFaceCreateDto (required): - Future createFace(AssetFaceCreateDto assetFaceCreateDto,) async { - final response = await createFaceWithHttpInfo(assetFaceCreateDto,); + Future createFace(AssetFaceCreateDto assetFaceCreateDto, { Future? abortTrigger, }) async { + final response = await createFaceWithHttpInfo(assetFaceCreateDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -75,7 +76,7 @@ class FacesApi { /// * [String] id (required): /// /// * [AssetFaceDeleteDto] assetFaceDeleteDto (required): - Future deleteFaceWithHttpInfo(String id, AssetFaceDeleteDto assetFaceDeleteDto,) async { + Future deleteFaceWithHttpInfo(String id, AssetFaceDeleteDto assetFaceDeleteDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/faces/{id}' .replaceAll('{id}', id); @@ -98,6 +99,7 @@ class FacesApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -110,8 +112,8 @@ class FacesApi { /// * [String] id (required): /// /// * [AssetFaceDeleteDto] assetFaceDeleteDto (required): - Future deleteFace(String id, AssetFaceDeleteDto assetFaceDeleteDto,) async { - final response = await deleteFaceWithHttpInfo(id, assetFaceDeleteDto,); + Future deleteFace(String id, AssetFaceDeleteDto assetFaceDeleteDto, { Future? abortTrigger, }) async { + final response = await deleteFaceWithHttpInfo(id, assetFaceDeleteDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -127,7 +129,7 @@ class FacesApi { /// /// * [String] id (required): /// Face ID - Future getFacesWithHttpInfo(String id,) async { + Future getFacesWithHttpInfo(String id, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/faces'; @@ -151,6 +153,7 @@ class FacesApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -162,8 +165,8 @@ class FacesApi { /// /// * [String] id (required): /// Face ID - Future?> getFaces(String id,) async { - final response = await getFacesWithHttpInfo(id,); + Future?> getFaces(String id, { Future? abortTrigger, }) async { + final response = await getFacesWithHttpInfo(id, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -191,7 +194,7 @@ class FacesApi { /// * [String] id (required): /// /// * [FaceDto] faceDto (required): - Future reassignFacesByIdWithHttpInfo(String id, FaceDto faceDto,) async { + Future reassignFacesByIdWithHttpInfo(String id, FaceDto faceDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/faces/{id}' .replaceAll('{id}', id); @@ -214,6 +217,7 @@ class FacesApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -226,8 +230,8 @@ class FacesApi { /// * [String] id (required): /// /// * [FaceDto] faceDto (required): - Future reassignFacesById(String id, FaceDto faceDto,) async { - final response = await reassignFacesByIdWithHttpInfo(id, faceDto,); + Future reassignFacesById(String id, FaceDto faceDto, { Future? abortTrigger, }) async { + final response = await reassignFacesByIdWithHttpInfo(id, faceDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api/jobs_api.dart b/mobile/openapi/lib/api/jobs_api.dart index 9dda59a883..287432ad9a 100644 --- a/mobile/openapi/lib/api/jobs_api.dart +++ b/mobile/openapi/lib/api/jobs_api.dart @@ -25,7 +25,7 @@ class JobsApi { /// Parameters: /// /// * [JobCreateDto] jobCreateDto (required): - Future createJobWithHttpInfo(JobCreateDto jobCreateDto,) async { + Future createJobWithHttpInfo(JobCreateDto jobCreateDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/jobs'; @@ -47,6 +47,7 @@ class JobsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -57,8 +58,8 @@ class JobsApi { /// Parameters: /// /// * [JobCreateDto] jobCreateDto (required): - Future createJob(JobCreateDto jobCreateDto,) async { - final response = await createJobWithHttpInfo(jobCreateDto,); + Future createJob(JobCreateDto jobCreateDto, { Future? abortTrigger, }) async { + final response = await createJobWithHttpInfo(jobCreateDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -69,7 +70,7 @@ class JobsApi { /// Retrieve the counts of the current queue, as well as the current status. /// /// Note: This method returns the HTTP [Response]. - Future getQueuesLegacyWithHttpInfo() async { + Future getQueuesLegacyWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/jobs'; @@ -91,14 +92,15 @@ class JobsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// Retrieve queue counts and status /// /// Retrieve the counts of the current queue, as well as the current status. - Future getQueuesLegacy() async { - final response = await getQueuesLegacyWithHttpInfo(); + Future getQueuesLegacy({ Future? abortTrigger, }) async { + final response = await getQueuesLegacyWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -123,7 +125,7 @@ class JobsApi { /// * [QueueName] name (required): /// /// * [QueueCommandDto] queueCommandDto (required): - Future runQueueCommandLegacyWithHttpInfo(QueueName name, QueueCommandDto queueCommandDto,) async { + Future runQueueCommandLegacyWithHttpInfo(QueueName name, QueueCommandDto queueCommandDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/jobs/{name}' .replaceAll('{name}', name.toString()); @@ -146,6 +148,7 @@ class JobsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -158,8 +161,8 @@ class JobsApi { /// * [QueueName] name (required): /// /// * [QueueCommandDto] queueCommandDto (required): - Future runQueueCommandLegacy(QueueName name, QueueCommandDto queueCommandDto,) async { - final response = await runQueueCommandLegacyWithHttpInfo(name, queueCommandDto,); + Future runQueueCommandLegacy(QueueName name, QueueCommandDto queueCommandDto, { Future? abortTrigger, }) async { + final response = await runQueueCommandLegacyWithHttpInfo(name, queueCommandDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api/libraries_api.dart b/mobile/openapi/lib/api/libraries_api.dart index ca59f823fe..a3b3086994 100644 --- a/mobile/openapi/lib/api/libraries_api.dart +++ b/mobile/openapi/lib/api/libraries_api.dart @@ -25,7 +25,7 @@ class LibrariesApi { /// Parameters: /// /// * [CreateLibraryDto] createLibraryDto (required): - Future createLibraryWithHttpInfo(CreateLibraryDto createLibraryDto,) async { + Future createLibraryWithHttpInfo(CreateLibraryDto createLibraryDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/libraries'; @@ -47,6 +47,7 @@ class LibrariesApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -57,8 +58,8 @@ class LibrariesApi { /// Parameters: /// /// * [CreateLibraryDto] createLibraryDto (required): - Future createLibrary(CreateLibraryDto createLibraryDto,) async { - final response = await createLibraryWithHttpInfo(createLibraryDto,); + Future createLibrary(CreateLibraryDto createLibraryDto, { Future? abortTrigger, }) async { + final response = await createLibraryWithHttpInfo(createLibraryDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -81,7 +82,7 @@ class LibrariesApi { /// Parameters: /// /// * [String] id (required): - Future deleteLibraryWithHttpInfo(String id,) async { + Future deleteLibraryWithHttpInfo(String id, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/libraries/{id}' .replaceAll('{id}', id); @@ -104,6 +105,7 @@ class LibrariesApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -114,8 +116,8 @@ class LibrariesApi { /// Parameters: /// /// * [String] id (required): - Future deleteLibrary(String id,) async { - final response = await deleteLibraryWithHttpInfo(id,); + Future deleteLibrary(String id, { Future? abortTrigger, }) async { + final response = await deleteLibraryWithHttpInfo(id, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -126,7 +128,7 @@ class LibrariesApi { /// Retrieve a list of external libraries. /// /// Note: This method returns the HTTP [Response]. - Future getAllLibrariesWithHttpInfo() async { + Future getAllLibrariesWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/libraries'; @@ -148,14 +150,15 @@ class LibrariesApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// Retrieve libraries /// /// Retrieve a list of external libraries. - Future?> getAllLibraries() async { - final response = await getAllLibrariesWithHttpInfo(); + Future?> getAllLibraries({ Future? abortTrigger, }) async { + final response = await getAllLibrariesWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -181,7 +184,7 @@ class LibrariesApi { /// Parameters: /// /// * [String] id (required): - Future getLibraryWithHttpInfo(String id,) async { + Future getLibraryWithHttpInfo(String id, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/libraries/{id}' .replaceAll('{id}', id); @@ -204,6 +207,7 @@ class LibrariesApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -214,8 +218,8 @@ class LibrariesApi { /// Parameters: /// /// * [String] id (required): - Future getLibrary(String id,) async { - final response = await getLibraryWithHttpInfo(id,); + Future getLibrary(String id, { Future? abortTrigger, }) async { + final response = await getLibraryWithHttpInfo(id, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -238,7 +242,7 @@ class LibrariesApi { /// Parameters: /// /// * [String] id (required): - Future getLibraryStatisticsWithHttpInfo(String id,) async { + Future getLibraryStatisticsWithHttpInfo(String id, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/libraries/{id}/statistics' .replaceAll('{id}', id); @@ -261,6 +265,7 @@ class LibrariesApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -271,8 +276,8 @@ class LibrariesApi { /// Parameters: /// /// * [String] id (required): - Future getLibraryStatistics(String id,) async { - final response = await getLibraryStatisticsWithHttpInfo(id,); + Future getLibraryStatistics(String id, { Future? abortTrigger, }) async { + final response = await getLibraryStatisticsWithHttpInfo(id, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -295,7 +300,7 @@ class LibrariesApi { /// Parameters: /// /// * [String] id (required): - Future scanLibraryWithHttpInfo(String id,) async { + Future scanLibraryWithHttpInfo(String id, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/libraries/{id}/scan' .replaceAll('{id}', id); @@ -318,6 +323,7 @@ class LibrariesApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -328,8 +334,8 @@ class LibrariesApi { /// Parameters: /// /// * [String] id (required): - Future scanLibrary(String id,) async { - final response = await scanLibraryWithHttpInfo(id,); + Future scanLibrary(String id, { Future? abortTrigger, }) async { + final response = await scanLibraryWithHttpInfo(id, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -346,7 +352,7 @@ class LibrariesApi { /// * [String] id (required): /// /// * [UpdateLibraryDto] updateLibraryDto (required): - Future updateLibraryWithHttpInfo(String id, UpdateLibraryDto updateLibraryDto,) async { + Future updateLibraryWithHttpInfo(String id, UpdateLibraryDto updateLibraryDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/libraries/{id}' .replaceAll('{id}', id); @@ -369,6 +375,7 @@ class LibrariesApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -381,8 +388,8 @@ class LibrariesApi { /// * [String] id (required): /// /// * [UpdateLibraryDto] updateLibraryDto (required): - Future updateLibrary(String id, UpdateLibraryDto updateLibraryDto,) async { - final response = await updateLibraryWithHttpInfo(id, updateLibraryDto,); + Future updateLibrary(String id, UpdateLibraryDto updateLibraryDto, { Future? abortTrigger, }) async { + final response = await updateLibraryWithHttpInfo(id, updateLibraryDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -407,7 +414,7 @@ class LibrariesApi { /// * [String] id (required): /// /// * [ValidateLibraryDto] validateLibraryDto (required): - Future validateWithHttpInfo(String id, ValidateLibraryDto validateLibraryDto,) async { + Future validateWithHttpInfo(String id, ValidateLibraryDto validateLibraryDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/libraries/{id}/validate' .replaceAll('{id}', id); @@ -430,6 +437,7 @@ class LibrariesApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -442,8 +450,8 @@ class LibrariesApi { /// * [String] id (required): /// /// * [ValidateLibraryDto] validateLibraryDto (required): - Future validate(String id, ValidateLibraryDto validateLibraryDto,) async { - final response = await validateWithHttpInfo(id, validateLibraryDto,); + Future validate(String id, ValidateLibraryDto validateLibraryDto, { Future? abortTrigger, }) async { + final response = await validateWithHttpInfo(id, validateLibraryDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api/maintenance_admin_api.dart b/mobile/openapi/lib/api/maintenance_admin_api.dart index 0f953f1634..8bab193ddf 100644 --- a/mobile/openapi/lib/api/maintenance_admin_api.dart +++ b/mobile/openapi/lib/api/maintenance_admin_api.dart @@ -21,7 +21,7 @@ class MaintenanceAdminApi { /// Collect integrity checks and other heuristics about local data. /// /// Note: This method returns the HTTP [Response]. - Future detectPriorInstallWithHttpInfo() async { + Future detectPriorInstallWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/admin/maintenance/detect-install'; @@ -43,14 +43,15 @@ class MaintenanceAdminApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// Detect existing install /// /// Collect integrity checks and other heuristics about local data. - Future detectPriorInstall() async { - final response = await detectPriorInstallWithHttpInfo(); + Future detectPriorInstall({ Future? abortTrigger, }) async { + final response = await detectPriorInstallWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -69,7 +70,7 @@ class MaintenanceAdminApi { /// Fetch information about the currently running maintenance action. /// /// Note: This method returns the HTTP [Response]. - Future getMaintenanceStatusWithHttpInfo() async { + Future getMaintenanceStatusWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/admin/maintenance/status'; @@ -91,14 +92,15 @@ class MaintenanceAdminApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// Get maintenance mode status /// /// Fetch information about the currently running maintenance action. - Future getMaintenanceStatus() async { - final response = await getMaintenanceStatusWithHttpInfo(); + Future getMaintenanceStatus({ Future? abortTrigger, }) async { + final response = await getMaintenanceStatusWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -121,7 +123,7 @@ class MaintenanceAdminApi { /// Parameters: /// /// * [MaintenanceLoginDto] maintenanceLoginDto (required): - Future maintenanceLoginWithHttpInfo(MaintenanceLoginDto maintenanceLoginDto,) async { + Future maintenanceLoginWithHttpInfo(MaintenanceLoginDto maintenanceLoginDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/admin/maintenance/login'; @@ -143,6 +145,7 @@ class MaintenanceAdminApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -153,8 +156,8 @@ class MaintenanceAdminApi { /// Parameters: /// /// * [MaintenanceLoginDto] maintenanceLoginDto (required): - Future maintenanceLogin(MaintenanceLoginDto maintenanceLoginDto,) async { - final response = await maintenanceLoginWithHttpInfo(maintenanceLoginDto,); + Future maintenanceLogin(MaintenanceLoginDto maintenanceLoginDto, { Future? abortTrigger, }) async { + final response = await maintenanceLoginWithHttpInfo(maintenanceLoginDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -177,7 +180,7 @@ class MaintenanceAdminApi { /// Parameters: /// /// * [SetMaintenanceModeDto] setMaintenanceModeDto (required): - Future setMaintenanceModeWithHttpInfo(SetMaintenanceModeDto setMaintenanceModeDto,) async { + Future setMaintenanceModeWithHttpInfo(SetMaintenanceModeDto setMaintenanceModeDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/admin/maintenance'; @@ -199,6 +202,7 @@ class MaintenanceAdminApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -209,8 +213,8 @@ class MaintenanceAdminApi { /// Parameters: /// /// * [SetMaintenanceModeDto] setMaintenanceModeDto (required): - Future setMaintenanceMode(SetMaintenanceModeDto setMaintenanceModeDto,) async { - final response = await setMaintenanceModeWithHttpInfo(setMaintenanceModeDto,); + Future setMaintenanceMode(SetMaintenanceModeDto setMaintenanceModeDto, { Future? abortTrigger, }) async { + final response = await setMaintenanceModeWithHttpInfo(setMaintenanceModeDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api/map_api.dart b/mobile/openapi/lib/api/map_api.dart index 4ce62bd96c..7e1618a875 100644 --- a/mobile/openapi/lib/api/map_api.dart +++ b/mobile/openapi/lib/api/map_api.dart @@ -41,7 +41,7 @@ class MapApi { /// /// * [bool] withSharedAlbums: /// Include shared album assets - Future getMapMarkersWithHttpInfo({ DateTime? fileCreatedAfter, DateTime? fileCreatedBefore, bool? isArchived, bool? isFavorite, bool? withPartners, bool? withSharedAlbums, }) async { + Future getMapMarkersWithHttpInfo({ DateTime? fileCreatedAfter, DateTime? fileCreatedBefore, bool? isArchived, bool? isFavorite, bool? withPartners, bool? withSharedAlbums, Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/map/markers'; @@ -82,6 +82,7 @@ class MapApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -108,8 +109,8 @@ class MapApi { /// /// * [bool] withSharedAlbums: /// Include shared album assets - 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({ DateTime? fileCreatedAfter, DateTime? fileCreatedBefore, bool? isArchived, bool? isFavorite, bool? withPartners, bool? withSharedAlbums, Future? abortTrigger, }) async { + final response = await getMapMarkersWithHttpInfo(fileCreatedAfter: fileCreatedAfter, fileCreatedBefore: fileCreatedBefore, isArchived: isArchived, isFavorite: isFavorite, withPartners: withPartners, withSharedAlbums: withSharedAlbums, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -139,7 +140,7 @@ class MapApi { /// /// * [double] lon (required): /// Longitude (-180 to 180) - Future reverseGeocodeWithHttpInfo(double lat, double lon,) async { + Future reverseGeocodeWithHttpInfo(double lat, double lon, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/map/reverse-geocode'; @@ -164,6 +165,7 @@ class MapApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -178,8 +180,8 @@ class MapApi { /// /// * [double] lon (required): /// Longitude (-180 to 180) - Future?> reverseGeocode(double lat, double lon,) async { - final response = await reverseGeocodeWithHttpInfo(lat, lon,); + Future?> reverseGeocode(double lat, double lon, { Future? abortTrigger, }) async { + final response = await reverseGeocodeWithHttpInfo(lat, lon, abortTrigger: abortTrigger,); 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 b6b769404c..5c0c25ae69 100644 --- a/mobile/openapi/lib/api/memories_api.dart +++ b/mobile/openapi/lib/api/memories_api.dart @@ -27,7 +27,7 @@ class MemoriesApi { /// * [String] id (required): /// /// * [BulkIdsDto] bulkIdsDto (required): - Future addMemoryAssetsWithHttpInfo(String id, BulkIdsDto bulkIdsDto,) async { + Future addMemoryAssetsWithHttpInfo(String id, BulkIdsDto bulkIdsDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/memories/{id}/assets' .replaceAll('{id}', id); @@ -50,6 +50,7 @@ class MemoriesApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -62,8 +63,8 @@ class MemoriesApi { /// * [String] id (required): /// /// * [BulkIdsDto] bulkIdsDto (required): - Future?> addMemoryAssets(String id, BulkIdsDto bulkIdsDto,) async { - final response = await addMemoryAssetsWithHttpInfo(id, bulkIdsDto,); + Future?> addMemoryAssets(String id, BulkIdsDto bulkIdsDto, { Future? abortTrigger, }) async { + final response = await addMemoryAssetsWithHttpInfo(id, bulkIdsDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -89,7 +90,7 @@ class MemoriesApi { /// Parameters: /// /// * [MemoryCreateDto] memoryCreateDto (required): - Future createMemoryWithHttpInfo(MemoryCreateDto memoryCreateDto,) async { + Future createMemoryWithHttpInfo(MemoryCreateDto memoryCreateDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/memories'; @@ -111,6 +112,7 @@ class MemoriesApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -121,8 +123,8 @@ class MemoriesApi { /// Parameters: /// /// * [MemoryCreateDto] memoryCreateDto (required): - Future createMemory(MemoryCreateDto memoryCreateDto,) async { - final response = await createMemoryWithHttpInfo(memoryCreateDto,); + Future createMemory(MemoryCreateDto memoryCreateDto, { Future? abortTrigger, }) async { + final response = await createMemoryWithHttpInfo(memoryCreateDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -145,7 +147,7 @@ class MemoriesApi { /// Parameters: /// /// * [String] id (required): - Future deleteMemoryWithHttpInfo(String id,) async { + Future deleteMemoryWithHttpInfo(String id, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/memories/{id}' .replaceAll('{id}', id); @@ -168,6 +170,7 @@ class MemoriesApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -178,8 +181,8 @@ class MemoriesApi { /// Parameters: /// /// * [String] id (required): - Future deleteMemory(String id,) async { - final response = await deleteMemoryWithHttpInfo(id,); + Future deleteMemory(String id, { Future? abortTrigger, }) async { + final response = await deleteMemoryWithHttpInfo(id, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -194,7 +197,7 @@ class MemoriesApi { /// Parameters: /// /// * [String] id (required): - Future getMemoryWithHttpInfo(String id,) async { + Future getMemoryWithHttpInfo(String id, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/memories/{id}' .replaceAll('{id}', id); @@ -217,6 +220,7 @@ class MemoriesApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -227,8 +231,8 @@ class MemoriesApi { /// Parameters: /// /// * [String] id (required): - Future getMemory(String id,) async { - final response = await getMemoryWithHttpInfo(id,); + Future getMemory(String id, { Future? abortTrigger, }) async { + final response = await getMemoryWithHttpInfo(id, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -268,7 +272,7 @@ class MemoriesApi { /// Number of memories to return /// /// * [MemoryType] type: - Future memoriesStatisticsWithHttpInfo({ DateTime? for_, bool? isSaved, bool? isTrashed, MemorySearchOrder? order, int? page, int? size, MemoryType? type, }) async { + Future memoriesStatisticsWithHttpInfo({ DateTime? for_, bool? isSaved, bool? isTrashed, MemorySearchOrder? order, int? size, MemoryType? type, Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/memories/statistics'; @@ -312,6 +316,7 @@ class MemoriesApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -339,8 +344,8 @@ class MemoriesApi { /// Number of memories to return /// /// * [MemoryType] type: - Future memoriesStatistics({ DateTime? for_, bool? isSaved, bool? isTrashed, MemorySearchOrder? order, int? page, int? size, MemoryType? type, }) async { - final response = await memoriesStatisticsWithHttpInfo( for_: for_, isSaved: isSaved, isTrashed: isTrashed, order: order, page: page, size: size, type: type, ); + Future memoriesStatistics({ DateTime? for_, bool? isSaved, bool? isTrashed, MemorySearchOrder? order, int? size, MemoryType? type, Future? abortTrigger, }) async { + final response = await memoriesStatisticsWithHttpInfo(for_: for_, isSaved: isSaved, isTrashed: isTrashed, order: order, size: size, type: type, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -365,7 +370,7 @@ class MemoriesApi { /// * [String] id (required): /// /// * [BulkIdsDto] bulkIdsDto (required): - Future removeMemoryAssetsWithHttpInfo(String id, BulkIdsDto bulkIdsDto,) async { + Future removeMemoryAssetsWithHttpInfo(String id, BulkIdsDto bulkIdsDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/memories/{id}/assets' .replaceAll('{id}', id); @@ -388,6 +393,7 @@ class MemoriesApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -400,8 +406,8 @@ class MemoriesApi { /// * [String] id (required): /// /// * [BulkIdsDto] bulkIdsDto (required): - Future?> removeMemoryAssets(String id, BulkIdsDto bulkIdsDto,) async { - final response = await removeMemoryAssetsWithHttpInfo(id, bulkIdsDto,); + Future?> removeMemoryAssets(String id, BulkIdsDto bulkIdsDto, { Future? abortTrigger, }) async { + final response = await removeMemoryAssetsWithHttpInfo(id, bulkIdsDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -444,7 +450,7 @@ class MemoriesApi { /// Number of memories to return /// /// * [MemoryType] type: - Future searchMemoriesWithHttpInfo({ DateTime? for_, bool? isSaved, bool? isTrashed, MemorySearchOrder? order, int? page, int? size, MemoryType? type, }) async { + Future searchMemoriesWithHttpInfo({ DateTime? for_, bool? isSaved, bool? isTrashed, MemorySearchOrder? order, int? size, MemoryType? type, Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/memories'; @@ -488,6 +494,7 @@ class MemoriesApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -515,8 +522,8 @@ class MemoriesApi { /// Number of memories to return /// /// * [MemoryType] type: - Future searchMemories({ DateTime? for_, bool? isSaved, bool? isTrashed, MemorySearchOrder? order, int? page, int? size, MemoryType? type, }) async { - final response = await searchMemoriesWithHttpInfo( for_: for_, isSaved: isSaved, isTrashed: isTrashed, order: order, page: page, size: size, type: type, ); + Future?> searchMemories({ DateTime? for_, bool? isSaved, bool? isTrashed, MemorySearchOrder? order, int? size, MemoryType? type, Future? abortTrigger, }) async { + final response = await searchMemoriesWithHttpInfo(for_: for_, isSaved: isSaved, isTrashed: isTrashed, order: order, size: size, type: type, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -541,7 +548,7 @@ class MemoriesApi { /// * [String] id (required): /// /// * [MemoryUpdateDto] memoryUpdateDto (required): - Future updateMemoryWithHttpInfo(String id, MemoryUpdateDto memoryUpdateDto,) async { + Future updateMemoryWithHttpInfo(String id, MemoryUpdateDto memoryUpdateDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/memories/{id}' .replaceAll('{id}', id); @@ -564,6 +571,7 @@ class MemoriesApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -576,8 +584,8 @@ class MemoriesApi { /// * [String] id (required): /// /// * [MemoryUpdateDto] memoryUpdateDto (required): - Future updateMemory(String id, MemoryUpdateDto memoryUpdateDto,) async { - final response = await updateMemoryWithHttpInfo(id, memoryUpdateDto,); + Future updateMemory(String id, MemoryUpdateDto memoryUpdateDto, { Future? abortTrigger, }) async { + final response = await updateMemoryWithHttpInfo(id, memoryUpdateDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api/notifications_admin_api.dart b/mobile/openapi/lib/api/notifications_admin_api.dart index 7821553d30..e9e18e791e 100644 --- a/mobile/openapi/lib/api/notifications_admin_api.dart +++ b/mobile/openapi/lib/api/notifications_admin_api.dart @@ -25,7 +25,7 @@ class NotificationsAdminApi { /// Parameters: /// /// * [NotificationCreateDto] notificationCreateDto (required): - Future createNotificationWithHttpInfo(NotificationCreateDto notificationCreateDto,) async { + Future createNotificationWithHttpInfo(NotificationCreateDto notificationCreateDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/admin/notifications'; @@ -47,6 +47,7 @@ class NotificationsAdminApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -57,8 +58,8 @@ class NotificationsAdminApi { /// Parameters: /// /// * [NotificationCreateDto] notificationCreateDto (required): - Future createNotification(NotificationCreateDto notificationCreateDto,) async { - final response = await createNotificationWithHttpInfo(notificationCreateDto,); + Future createNotification(NotificationCreateDto notificationCreateDto, { Future? abortTrigger, }) async { + final response = await createNotificationWithHttpInfo(notificationCreateDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -83,7 +84,7 @@ class NotificationsAdminApi { /// * [String] name (required): /// /// * [TemplateDto] templateDto (required): - Future getNotificationTemplateAdminWithHttpInfo(String name, TemplateDto templateDto,) async { + Future getNotificationTemplateAdminWithHttpInfo(String name, TemplateDto templateDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/admin/notifications/templates/{name}' .replaceAll('{name}', name); @@ -106,6 +107,7 @@ class NotificationsAdminApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -118,8 +120,8 @@ class NotificationsAdminApi { /// * [String] name (required): /// /// * [TemplateDto] templateDto (required): - Future getNotificationTemplateAdmin(String name, TemplateDto templateDto,) async { - final response = await getNotificationTemplateAdminWithHttpInfo(name, templateDto,); + Future getNotificationTemplateAdmin(String name, TemplateDto templateDto, { Future? abortTrigger, }) async { + final response = await getNotificationTemplateAdminWithHttpInfo(name, templateDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -142,7 +144,7 @@ class NotificationsAdminApi { /// Parameters: /// /// * [SystemConfigSmtpDto] systemConfigSmtpDto (required): - Future sendTestEmailAdminWithHttpInfo(SystemConfigSmtpDto systemConfigSmtpDto,) async { + Future sendTestEmailAdminWithHttpInfo(SystemConfigSmtpDto systemConfigSmtpDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/admin/notifications/test-email'; @@ -164,6 +166,7 @@ class NotificationsAdminApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -174,8 +177,8 @@ class NotificationsAdminApi { /// Parameters: /// /// * [SystemConfigSmtpDto] systemConfigSmtpDto (required): - Future sendTestEmailAdmin(SystemConfigSmtpDto systemConfigSmtpDto,) async { - final response = await sendTestEmailAdminWithHttpInfo(systemConfigSmtpDto,); + Future sendTestEmailAdmin(SystemConfigSmtpDto systemConfigSmtpDto, { Future? abortTrigger, }) async { + final response = await sendTestEmailAdminWithHttpInfo(systemConfigSmtpDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api/notifications_api.dart b/mobile/openapi/lib/api/notifications_api.dart index ab0be3e8f3..6b4f213bcd 100644 --- a/mobile/openapi/lib/api/notifications_api.dart +++ b/mobile/openapi/lib/api/notifications_api.dart @@ -25,7 +25,7 @@ class NotificationsApi { /// Parameters: /// /// * [String] id (required): - Future deleteNotificationWithHttpInfo(String id,) async { + Future deleteNotificationWithHttpInfo(String id, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/notifications/{id}' .replaceAll('{id}', id); @@ -48,6 +48,7 @@ class NotificationsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -58,8 +59,8 @@ class NotificationsApi { /// Parameters: /// /// * [String] id (required): - Future deleteNotification(String id,) async { - final response = await deleteNotificationWithHttpInfo(id,); + Future deleteNotification(String id, { Future? abortTrigger, }) async { + final response = await deleteNotificationWithHttpInfo(id, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -74,7 +75,7 @@ class NotificationsApi { /// Parameters: /// /// * [NotificationDeleteAllDto] notificationDeleteAllDto (required): - Future deleteNotificationsWithHttpInfo(NotificationDeleteAllDto notificationDeleteAllDto,) async { + Future deleteNotificationsWithHttpInfo(NotificationDeleteAllDto notificationDeleteAllDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/notifications'; @@ -96,6 +97,7 @@ class NotificationsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -106,8 +108,8 @@ class NotificationsApi { /// Parameters: /// /// * [NotificationDeleteAllDto] notificationDeleteAllDto (required): - Future deleteNotifications(NotificationDeleteAllDto notificationDeleteAllDto,) async { - final response = await deleteNotificationsWithHttpInfo(notificationDeleteAllDto,); + Future deleteNotifications(NotificationDeleteAllDto notificationDeleteAllDto, { Future? abortTrigger, }) async { + final response = await deleteNotificationsWithHttpInfo(notificationDeleteAllDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -122,7 +124,7 @@ class NotificationsApi { /// Parameters: /// /// * [String] id (required): - Future getNotificationWithHttpInfo(String id,) async { + Future getNotificationWithHttpInfo(String id, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/notifications/{id}' .replaceAll('{id}', id); @@ -145,6 +147,7 @@ class NotificationsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -155,8 +158,8 @@ class NotificationsApi { /// Parameters: /// /// * [String] id (required): - Future getNotification(String id,) async { - final response = await getNotificationWithHttpInfo(id,); + Future getNotification(String id, { Future? abortTrigger, }) async { + final response = await getNotificationWithHttpInfo(id, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -187,7 +190,7 @@ class NotificationsApi { /// /// * [bool] unread: /// Filter by unread status - Future getNotificationsWithHttpInfo({ String? id, NotificationLevel? level, NotificationType? type, bool? unread, }) async { + Future getNotificationsWithHttpInfo({ String? id, NotificationLevel? level, NotificationType? type, bool? unread, Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/notifications'; @@ -222,6 +225,7 @@ class NotificationsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -240,8 +244,8 @@ class NotificationsApi { /// /// * [bool] unread: /// Filter by unread status - Future?> getNotifications({ String? id, NotificationLevel? level, NotificationType? type, bool? unread, }) async { - final response = await getNotificationsWithHttpInfo( id: id, level: level, type: type, unread: unread, ); + Future?> getNotifications({ String? id, NotificationLevel? level, NotificationType? type, bool? unread, Future? abortTrigger, }) async { + final response = await getNotificationsWithHttpInfo(id: id, level: level, type: type, unread: unread, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -269,7 +273,7 @@ class NotificationsApi { /// * [String] id (required): /// /// * [NotificationUpdateDto] notificationUpdateDto (required): - Future updateNotificationWithHttpInfo(String id, NotificationUpdateDto notificationUpdateDto,) async { + Future updateNotificationWithHttpInfo(String id, NotificationUpdateDto notificationUpdateDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/notifications/{id}' .replaceAll('{id}', id); @@ -292,6 +296,7 @@ class NotificationsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -304,8 +309,8 @@ class NotificationsApi { /// * [String] id (required): /// /// * [NotificationUpdateDto] notificationUpdateDto (required): - Future updateNotification(String id, NotificationUpdateDto notificationUpdateDto,) async { - final response = await updateNotificationWithHttpInfo(id, notificationUpdateDto,); + Future updateNotification(String id, NotificationUpdateDto notificationUpdateDto, { Future? abortTrigger, }) async { + final response = await updateNotificationWithHttpInfo(id, notificationUpdateDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -328,7 +333,7 @@ class NotificationsApi { /// Parameters: /// /// * [NotificationUpdateAllDto] notificationUpdateAllDto (required): - Future updateNotificationsWithHttpInfo(NotificationUpdateAllDto notificationUpdateAllDto,) async { + Future updateNotificationsWithHttpInfo(NotificationUpdateAllDto notificationUpdateAllDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/notifications'; @@ -350,6 +355,7 @@ class NotificationsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -360,8 +366,8 @@ class NotificationsApi { /// Parameters: /// /// * [NotificationUpdateAllDto] notificationUpdateAllDto (required): - Future updateNotifications(NotificationUpdateAllDto notificationUpdateAllDto,) async { - final response = await updateNotificationsWithHttpInfo(notificationUpdateAllDto,); + Future updateNotifications(NotificationUpdateAllDto notificationUpdateAllDto, { Future? abortTrigger, }) async { + final response = await updateNotificationsWithHttpInfo(notificationUpdateAllDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api/partners_api.dart b/mobile/openapi/lib/api/partners_api.dart index 7d18f6d867..45bcdcd085 100644 --- a/mobile/openapi/lib/api/partners_api.dart +++ b/mobile/openapi/lib/api/partners_api.dart @@ -25,7 +25,7 @@ class PartnersApi { /// Parameters: /// /// * [PartnerCreateDto] partnerCreateDto (required): - Future createPartnerWithHttpInfo(PartnerCreateDto partnerCreateDto,) async { + Future createPartnerWithHttpInfo(PartnerCreateDto partnerCreateDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/partners'; @@ -47,6 +47,7 @@ class PartnersApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -57,8 +58,8 @@ class PartnersApi { /// Parameters: /// /// * [PartnerCreateDto] partnerCreateDto (required): - Future createPartner(PartnerCreateDto partnerCreateDto,) async { - final response = await createPartnerWithHttpInfo(partnerCreateDto,); + Future createPartner(PartnerCreateDto partnerCreateDto, { Future? abortTrigger, }) async { + final response = await createPartnerWithHttpInfo(partnerCreateDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -81,7 +82,7 @@ class PartnersApi { /// Parameters: /// /// * [String] id (required): - Future createPartnerDeprecatedWithHttpInfo(String id,) async { + Future createPartnerDeprecatedWithHttpInfo(String id, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/partners/{id}' .replaceAll('{id}', id); @@ -104,6 +105,7 @@ class PartnersApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -114,8 +116,8 @@ class PartnersApi { /// Parameters: /// /// * [String] id (required): - Future createPartnerDeprecated(String id,) async { - final response = await createPartnerDeprecatedWithHttpInfo(id,); + Future createPartnerDeprecated(String id, { Future? abortTrigger, }) async { + final response = await createPartnerDeprecatedWithHttpInfo(id, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -138,7 +140,7 @@ class PartnersApi { /// Parameters: /// /// * [PartnerDirection] direction (required): - Future getPartnersWithHttpInfo(PartnerDirection direction,) async { + Future getPartnersWithHttpInfo(PartnerDirection direction, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/partners'; @@ -162,6 +164,7 @@ class PartnersApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -172,8 +175,8 @@ class PartnersApi { /// Parameters: /// /// * [PartnerDirection] direction (required): - Future?> getPartners(PartnerDirection direction,) async { - final response = await getPartnersWithHttpInfo(direction,); + Future?> getPartners(PartnerDirection direction, { Future? abortTrigger, }) async { + final response = await getPartnersWithHttpInfo(direction, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -199,7 +202,7 @@ class PartnersApi { /// Parameters: /// /// * [String] id (required): - Future removePartnerWithHttpInfo(String id,) async { + Future removePartnerWithHttpInfo(String id, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/partners/{id}' .replaceAll('{id}', id); @@ -222,6 +225,7 @@ class PartnersApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -232,8 +236,8 @@ class PartnersApi { /// Parameters: /// /// * [String] id (required): - Future removePartner(String id,) async { - final response = await removePartnerWithHttpInfo(id,); + Future removePartner(String id, { Future? abortTrigger, }) async { + final response = await removePartnerWithHttpInfo(id, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -250,7 +254,7 @@ class PartnersApi { /// * [String] id (required): /// /// * [PartnerUpdateDto] partnerUpdateDto (required): - Future updatePartnerWithHttpInfo(String id, PartnerUpdateDto partnerUpdateDto,) async { + Future updatePartnerWithHttpInfo(String id, PartnerUpdateDto partnerUpdateDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/partners/{id}' .replaceAll('{id}', id); @@ -273,6 +277,7 @@ class PartnersApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -285,8 +290,8 @@ class PartnersApi { /// * [String] id (required): /// /// * [PartnerUpdateDto] partnerUpdateDto (required): - Future updatePartner(String id, PartnerUpdateDto partnerUpdateDto,) async { - final response = await updatePartnerWithHttpInfo(id, partnerUpdateDto,); + Future updatePartner(String id, PartnerUpdateDto partnerUpdateDto, { Future? abortTrigger, }) async { + final response = await updatePartnerWithHttpInfo(id, partnerUpdateDto, abortTrigger: abortTrigger,); 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 99821f31aa..c35491e110 100644 --- a/mobile/openapi/lib/api/people_api.dart +++ b/mobile/openapi/lib/api/people_api.dart @@ -25,7 +25,7 @@ class PeopleApi { /// Parameters: /// /// * [PersonCreateDto] personCreateDto (required): - Future createPersonWithHttpInfo(PersonCreateDto personCreateDto,) async { + Future createPersonWithHttpInfo(PersonCreateDto personCreateDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/people'; @@ -47,6 +47,7 @@ class PeopleApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -57,8 +58,8 @@ class PeopleApi { /// Parameters: /// /// * [PersonCreateDto] personCreateDto (required): - Future createPerson(PersonCreateDto personCreateDto,) async { - final response = await createPersonWithHttpInfo(personCreateDto,); + Future createPerson(PersonCreateDto personCreateDto, { Future? abortTrigger, }) async { + final response = await createPersonWithHttpInfo(personCreateDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -81,7 +82,7 @@ class PeopleApi { /// Parameters: /// /// * [BulkIdsDto] bulkIdsDto (required): - Future deletePeopleWithHttpInfo(BulkIdsDto bulkIdsDto,) async { + Future deletePeopleWithHttpInfo(BulkIdsDto bulkIdsDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/people'; @@ -103,6 +104,7 @@ class PeopleApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -113,8 +115,8 @@ class PeopleApi { /// Parameters: /// /// * [BulkIdsDto] bulkIdsDto (required): - Future deletePeople(BulkIdsDto bulkIdsDto,) async { - final response = await deletePeopleWithHttpInfo(bulkIdsDto,); + Future deletePeople(BulkIdsDto bulkIdsDto, { Future? abortTrigger, }) async { + final response = await deletePeopleWithHttpInfo(bulkIdsDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -129,7 +131,7 @@ class PeopleApi { /// Parameters: /// /// * [String] id (required): - Future deletePersonWithHttpInfo(String id,) async { + Future deletePersonWithHttpInfo(String id, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/people/{id}' .replaceAll('{id}', id); @@ -152,6 +154,7 @@ class PeopleApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -162,8 +165,8 @@ class PeopleApi { /// Parameters: /// /// * [String] id (required): - Future deletePerson(String id,) async { - final response = await deletePersonWithHttpInfo(id,); + Future deletePerson(String id, { Future? abortTrigger, }) async { + final response = await deletePersonWithHttpInfo(id, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -191,7 +194,7 @@ class PeopleApi { /// /// * [bool] withHidden: /// Include hidden people - Future getAllPeopleWithHttpInfo({ String? closestAssetId, String? closestPersonId, int? page, int? size, bool? withHidden, }) async { + Future getAllPeopleWithHttpInfo({ String? closestAssetId, String? closestPersonId, int? page, int? size, bool? withHidden, Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/people'; @@ -229,6 +232,7 @@ class PeopleApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -252,8 +256,8 @@ class PeopleApi { /// /// * [bool] withHidden: /// Include hidden people - Future getAllPeople({ String? closestAssetId, String? closestPersonId, int? page, int? size, bool? withHidden, }) async { - final response = await getAllPeopleWithHttpInfo( closestAssetId: closestAssetId, closestPersonId: closestPersonId, page: page, size: size, withHidden: withHidden, ); + Future getAllPeople({ String? closestAssetId, String? closestPersonId, int? page, int? size, bool? withHidden, Future? abortTrigger, }) async { + final response = await getAllPeopleWithHttpInfo(closestAssetId: closestAssetId, closestPersonId: closestPersonId, page: page, size: size, withHidden: withHidden, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -276,7 +280,7 @@ class PeopleApi { /// Parameters: /// /// * [String] id (required): - Future getPersonWithHttpInfo(String id,) async { + Future getPersonWithHttpInfo(String id, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/people/{id}' .replaceAll('{id}', id); @@ -299,6 +303,7 @@ class PeopleApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -309,8 +314,8 @@ class PeopleApi { /// Parameters: /// /// * [String] id (required): - Future getPerson(String id,) async { - final response = await getPersonWithHttpInfo(id,); + Future getPerson(String id, { Future? abortTrigger, }) async { + final response = await getPersonWithHttpInfo(id, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -333,7 +338,7 @@ class PeopleApi { /// Parameters: /// /// * [String] id (required): - Future getPersonStatisticsWithHttpInfo(String id,) async { + Future getPersonStatisticsWithHttpInfo(String id, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/people/{id}/statistics' .replaceAll('{id}', id); @@ -356,6 +361,7 @@ class PeopleApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -366,8 +372,8 @@ class PeopleApi { /// Parameters: /// /// * [String] id (required): - Future getPersonStatistics(String id,) async { - final response = await getPersonStatisticsWithHttpInfo(id,); + Future getPersonStatistics(String id, { Future? abortTrigger, }) async { + final response = await getPersonStatisticsWithHttpInfo(id, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -390,7 +396,7 @@ class PeopleApi { /// Parameters: /// /// * [String] id (required): - Future getPersonThumbnailWithHttpInfo(String id,) async { + Future getPersonThumbnailWithHttpInfo(String id, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/people/{id}/thumbnail' .replaceAll('{id}', id); @@ -413,6 +419,7 @@ class PeopleApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -423,8 +430,8 @@ class PeopleApi { /// Parameters: /// /// * [String] id (required): - Future getPersonThumbnail(String id,) async { - final response = await getPersonThumbnailWithHttpInfo(id,); + Future getPersonThumbnail(String id, { Future? abortTrigger, }) async { + final response = await getPersonThumbnailWithHttpInfo(id, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -449,7 +456,7 @@ class PeopleApi { /// * [String] id (required): /// /// * [MergePersonDto] mergePersonDto (required): - Future mergePersonWithHttpInfo(String id, MergePersonDto mergePersonDto,) async { + Future mergePersonWithHttpInfo(String id, MergePersonDto mergePersonDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/people/{id}/merge' .replaceAll('{id}', id); @@ -472,6 +479,7 @@ class PeopleApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -484,8 +492,8 @@ class PeopleApi { /// * [String] id (required): /// /// * [MergePersonDto] mergePersonDto (required): - Future?> mergePerson(String id, MergePersonDto mergePersonDto,) async { - final response = await mergePersonWithHttpInfo(id, mergePersonDto,); + Future?> mergePerson(String id, MergePersonDto mergePersonDto, { Future? abortTrigger, }) async { + final response = await mergePersonWithHttpInfo(id, mergePersonDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -513,7 +521,7 @@ class PeopleApi { /// * [String] id (required): /// /// * [AssetFaceUpdateDto] assetFaceUpdateDto (required): - Future reassignFacesWithHttpInfo(String id, AssetFaceUpdateDto assetFaceUpdateDto,) async { + Future reassignFacesWithHttpInfo(String id, AssetFaceUpdateDto assetFaceUpdateDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/people/{id}/reassign' .replaceAll('{id}', id); @@ -536,6 +544,7 @@ class PeopleApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -548,8 +557,8 @@ class PeopleApi { /// * [String] id (required): /// /// * [AssetFaceUpdateDto] assetFaceUpdateDto (required): - Future?> reassignFaces(String id, AssetFaceUpdateDto assetFaceUpdateDto,) async { - final response = await reassignFacesWithHttpInfo(id, assetFaceUpdateDto,); + Future?> reassignFaces(String id, AssetFaceUpdateDto assetFaceUpdateDto, { Future? abortTrigger, }) async { + final response = await reassignFacesWithHttpInfo(id, assetFaceUpdateDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -575,7 +584,7 @@ class PeopleApi { /// Parameters: /// /// * [PeopleUpdateDto] peopleUpdateDto (required): - Future updatePeopleWithHttpInfo(PeopleUpdateDto peopleUpdateDto,) async { + Future updatePeopleWithHttpInfo(PeopleUpdateDto peopleUpdateDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/people'; @@ -597,6 +606,7 @@ class PeopleApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -607,8 +617,8 @@ class PeopleApi { /// Parameters: /// /// * [PeopleUpdateDto] peopleUpdateDto (required): - Future?> updatePeople(PeopleUpdateDto peopleUpdateDto,) async { - final response = await updatePeopleWithHttpInfo(peopleUpdateDto,); + Future?> updatePeople(PeopleUpdateDto peopleUpdateDto, { Future? abortTrigger, }) async { + final response = await updatePeopleWithHttpInfo(peopleUpdateDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -636,7 +646,7 @@ class PeopleApi { /// * [String] id (required): /// /// * [PersonUpdateDto] personUpdateDto (required): - Future updatePersonWithHttpInfo(String id, PersonUpdateDto personUpdateDto,) async { + Future updatePersonWithHttpInfo(String id, PersonUpdateDto personUpdateDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/people/{id}' .replaceAll('{id}', id); @@ -659,6 +669,7 @@ class PeopleApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -671,8 +682,8 @@ class PeopleApi { /// * [String] id (required): /// /// * [PersonUpdateDto] personUpdateDto (required): - Future updatePerson(String id, PersonUpdateDto personUpdateDto,) async { - final response = await updatePersonWithHttpInfo(id, personUpdateDto,); + Future updatePerson(String id, PersonUpdateDto personUpdateDto, { Future? abortTrigger, }) async { + final response = await updatePersonWithHttpInfo(id, personUpdateDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api/plugins_api.dart b/mobile/openapi/lib/api/plugins_api.dart index a29f597dc4..40892b8a67 100644 --- a/mobile/openapi/lib/api/plugins_api.dart +++ b/mobile/openapi/lib/api/plugins_api.dart @@ -25,7 +25,7 @@ class PluginsApi { /// Parameters: /// /// * [String] id (required): - Future getPluginWithHttpInfo(String id,) async { + Future getPluginWithHttpInfo(String id, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/plugins/{id}' .replaceAll('{id}', id); @@ -48,6 +48,7 @@ class PluginsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -58,8 +59,8 @@ class PluginsApi { /// Parameters: /// /// * [String] id (required): - Future getPlugin(String id,) async { - final response = await getPluginWithHttpInfo(id,); + Future getPlugin(String id, { Future? abortTrigger, }) async { + final response = await getPluginWithHttpInfo(id, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -104,7 +105,7 @@ class PluginsApi { /// /// * [WorkflowType] type: /// Workflow types - Future searchPluginMethodsWithHttpInfo({ String? description, bool? enabled, String? id, String? name, String? pluginName, String? pluginVersion, String? title, WorkflowTrigger? trigger, WorkflowType? type, }) async { + Future searchPluginMethodsWithHttpInfo({ String? description, bool? enabled, String? id, String? name, String? pluginName, String? pluginVersion, String? title, WorkflowTrigger? trigger, WorkflowType? type, Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/plugins/methods'; @@ -154,6 +155,7 @@ class PluginsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -186,8 +188,8 @@ class PluginsApi { /// /// * [WorkflowType] type: /// Workflow types - Future?> searchPluginMethods({ String? description, bool? enabled, String? id, String? name, String? pluginName, String? pluginVersion, String? title, WorkflowTrigger? trigger, WorkflowType? type, }) async { - final response = await searchPluginMethodsWithHttpInfo( description: description, enabled: enabled, id: id, name: name, pluginName: pluginName, pluginVersion: pluginVersion, title: title, trigger: trigger, type: type, ); + Future?> searchPluginMethods({ String? description, bool? enabled, String? id, String? name, String? pluginName, String? pluginVersion, String? title, WorkflowTrigger? trigger, WorkflowType? type, Future? abortTrigger, }) async { + final response = await searchPluginMethodsWithHttpInfo(description: description, enabled: enabled, id: id, name: name, pluginName: pluginName, pluginVersion: pluginVersion, title: title, trigger: trigger, type: type, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -209,7 +211,7 @@ class PluginsApi { /// Retrieve workflow templates provided by installed plugins /// /// Note: This method returns the HTTP [Response]. - Future searchPluginTemplatesWithHttpInfo() async { + Future searchPluginTemplatesWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/plugins/templates'; @@ -231,14 +233,15 @@ class PluginsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// Retrieve workflow templates /// /// Retrieve workflow templates provided by installed plugins - Future?> searchPluginTemplates() async { - final response = await searchPluginTemplatesWithHttpInfo(); + Future?> searchPluginTemplates({ Future? abortTrigger, }) async { + final response = await searchPluginTemplatesWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -276,7 +279,7 @@ class PluginsApi { /// * [String] title: /// /// * [String] version: - Future searchPluginsWithHttpInfo({ String? description, bool? enabled, String? id, String? name, String? title, String? version, }) async { + Future searchPluginsWithHttpInfo({ String? description, bool? enabled, String? id, String? name, String? title, String? version, Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/plugins'; @@ -317,6 +320,7 @@ class PluginsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -339,8 +343,8 @@ class PluginsApi { /// * [String] title: /// /// * [String] version: - Future?> searchPlugins({ String? description, bool? enabled, String? id, String? name, String? title, String? version, }) async { - final response = await searchPluginsWithHttpInfo( description: description, enabled: enabled, id: id, name: name, title: title, version: version, ); + Future?> searchPlugins({ String? description, bool? enabled, String? id, String? name, String? title, String? version, Future? abortTrigger, }) async { + final response = await searchPluginsWithHttpInfo(description: description, enabled: enabled, id: id, name: name, title: title, version: version, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api/queues_api.dart b/mobile/openapi/lib/api/queues_api.dart index 1312cb5952..39386c23f9 100644 --- a/mobile/openapi/lib/api/queues_api.dart +++ b/mobile/openapi/lib/api/queues_api.dart @@ -27,7 +27,7 @@ class QueuesApi { /// * [QueueName] name (required): /// /// * [QueueDeleteDto] queueDeleteDto (required): - Future emptyQueueWithHttpInfo(QueueName name, QueueDeleteDto queueDeleteDto,) async { + Future emptyQueueWithHttpInfo(QueueName name, QueueDeleteDto queueDeleteDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/queues/{name}/jobs' .replaceAll('{name}', name.toString()); @@ -50,6 +50,7 @@ class QueuesApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -62,8 +63,8 @@ class QueuesApi { /// * [QueueName] name (required): /// /// * [QueueDeleteDto] queueDeleteDto (required): - Future emptyQueue(QueueName name, QueueDeleteDto queueDeleteDto,) async { - final response = await emptyQueueWithHttpInfo(name, queueDeleteDto,); + Future emptyQueue(QueueName name, QueueDeleteDto queueDeleteDto, { Future? abortTrigger, }) async { + final response = await emptyQueueWithHttpInfo(name, queueDeleteDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -78,7 +79,7 @@ class QueuesApi { /// Parameters: /// /// * [QueueName] name (required): - Future getQueueWithHttpInfo(QueueName name,) async { + Future getQueueWithHttpInfo(QueueName name, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/queues/{name}' .replaceAll('{name}', name.toString()); @@ -101,6 +102,7 @@ class QueuesApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -111,8 +113,8 @@ class QueuesApi { /// Parameters: /// /// * [QueueName] name (required): - Future getQueue(QueueName name,) async { - final response = await getQueueWithHttpInfo(name,); + Future getQueue(QueueName name, { Future? abortTrigger, }) async { + final response = await getQueueWithHttpInfo(name, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -138,7 +140,7 @@ class QueuesApi { /// /// * [List] status: /// Filter jobs by status - Future getQueueJobsWithHttpInfo(QueueName name, { List? status, }) async { + Future getQueueJobsWithHttpInfo(QueueName name, { List? status, Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/queues/{name}/jobs' .replaceAll('{name}', name.toString()); @@ -165,6 +167,7 @@ class QueuesApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -178,8 +181,8 @@ class QueuesApi { /// /// * [List] status: /// Filter jobs by status - Future?> getQueueJobs(QueueName name, { List? status, }) async { - final response = await getQueueJobsWithHttpInfo(name, status: status, ); + Future?> getQueueJobs(QueueName name, { List? status, Future? abortTrigger, }) async { + final response = await getQueueJobsWithHttpInfo(name, status: status, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -201,7 +204,7 @@ class QueuesApi { /// Retrieves a list of queues. /// /// Note: This method returns the HTTP [Response]. - Future getQueuesWithHttpInfo() async { + Future getQueuesWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/queues'; @@ -223,14 +226,15 @@ class QueuesApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// List all queues /// /// Retrieves a list of queues. - Future?> getQueues() async { - final response = await getQueuesWithHttpInfo(); + Future?> getQueues({ Future? abortTrigger, }) async { + final response = await getQueuesWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -258,7 +262,7 @@ class QueuesApi { /// * [QueueName] name (required): /// /// * [QueueUpdateDto] queueUpdateDto (required): - Future updateQueueWithHttpInfo(QueueName name, QueueUpdateDto queueUpdateDto,) async { + Future updateQueueWithHttpInfo(QueueName name, QueueUpdateDto queueUpdateDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/queues/{name}' .replaceAll('{name}', name.toString()); @@ -281,6 +285,7 @@ class QueuesApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -293,8 +298,8 @@ class QueuesApi { /// * [QueueName] name (required): /// /// * [QueueUpdateDto] queueUpdateDto (required): - Future updateQueue(QueueName name, QueueUpdateDto queueUpdateDto,) async { - final response = await updateQueueWithHttpInfo(name, queueUpdateDto,); + Future updateQueue(QueueName name, QueueUpdateDto queueUpdateDto, { Future? abortTrigger, }) async { + final response = await updateQueueWithHttpInfo(name, queueUpdateDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api/search_api.dart b/mobile/openapi/lib/api/search_api.dart index 6f8a4df902..0118cabdba 100644 --- a/mobile/openapi/lib/api/search_api.dart +++ b/mobile/openapi/lib/api/search_api.dart @@ -21,7 +21,7 @@ class SearchApi { /// Retrieve a list of assets with each asset belonging to a different city. This endpoint is used on the places pages to show a single thumbnail for each city the user has assets in. /// /// Note: This method returns the HTTP [Response]. - Future getAssetsByCityWithHttpInfo() async { + Future getAssetsByCityWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/search/cities'; @@ -43,14 +43,15 @@ class SearchApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// Retrieve assets by city /// /// Retrieve a list of assets with each asset belonging to a different city. This endpoint is used on the places pages to show a single thumbnail for each city the user has assets in. - Future?> getAssetsByCity() async { - final response = await getAssetsByCityWithHttpInfo(); + Future?> getAssetsByCity({ Future? abortTrigger, }) async { + final response = await getAssetsByCityWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -72,7 +73,7 @@ class SearchApi { /// Retrieve data for the explore section, such as popular people and places. /// /// Note: This method returns the HTTP [Response]. - Future getExploreDataWithHttpInfo() async { + Future getExploreDataWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/search/explore'; @@ -94,14 +95,15 @@ class SearchApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// Retrieve explore data /// /// Retrieve data for the explore section, such as popular people and places. - Future?> getExploreData() async { - final response = await getExploreDataWithHttpInfo(); + Future?> getExploreData({ Future? abortTrigger, }) async { + final response = await getExploreDataWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -145,7 +147,7 @@ class SearchApi { /// /// * [String] state: /// Filter by state/province - Future getSearchSuggestionsWithHttpInfo(SearchSuggestionType type, { String? country, bool? includeNull, String? lensModel, String? make, String? model, String? state, }) async { + Future getSearchSuggestionsWithHttpInfo(SearchSuggestionType type, { String? country, bool? includeNull, String? lensModel, String? make, String? model, String? state, Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/search/suggestions'; @@ -187,6 +189,7 @@ class SearchApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -215,8 +218,8 @@ class SearchApi { /// /// * [String] state: /// Filter by state/province - Future?> getSearchSuggestions(SearchSuggestionType type, { String? country, bool? includeNull, String? lensModel, String? make, String? model, String? state, }) async { - final response = await getSearchSuggestionsWithHttpInfo(type, country: country, includeNull: includeNull, lensModel: lensModel, make: make, model: model, state: state, ); + Future?> getSearchSuggestions(SearchSuggestionType type, { String? country, bool? includeNull, String? lensModel, String? make, String? model, String? state, Future? abortTrigger, }) async { + final response = await getSearchSuggestionsWithHttpInfo(type, country: country, includeNull: includeNull, lensModel: lensModel, make: make, model: model, state: state, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -242,7 +245,7 @@ class SearchApi { /// Parameters: /// /// * [StatisticsSearchDto] statisticsSearchDto (required): - Future searchAssetStatisticsWithHttpInfo(StatisticsSearchDto statisticsSearchDto,) async { + Future searchAssetStatisticsWithHttpInfo(StatisticsSearchDto statisticsSearchDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/search/statistics'; @@ -264,6 +267,7 @@ class SearchApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -274,8 +278,8 @@ class SearchApi { /// Parameters: /// /// * [StatisticsSearchDto] statisticsSearchDto (required): - Future searchAssetStatistics(StatisticsSearchDto statisticsSearchDto,) async { - final response = await searchAssetStatisticsWithHttpInfo(statisticsSearchDto,); + Future searchAssetStatistics(StatisticsSearchDto statisticsSearchDto, { Future? abortTrigger, }) async { + final response = await searchAssetStatisticsWithHttpInfo(statisticsSearchDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -298,7 +302,7 @@ class SearchApi { /// Parameters: /// /// * [MetadataSearchDto] metadataSearchDto (required): - Future searchAssetsWithHttpInfo(MetadataSearchDto metadataSearchDto,) async { + Future searchAssetsWithHttpInfo(MetadataSearchDto metadataSearchDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/search/metadata'; @@ -320,6 +324,7 @@ class SearchApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -330,8 +335,8 @@ class SearchApi { /// Parameters: /// /// * [MetadataSearchDto] metadataSearchDto (required): - Future searchAssets(MetadataSearchDto metadataSearchDto,) async { - final response = await searchAssetsWithHttpInfo(metadataSearchDto,); + Future searchAssets(MetadataSearchDto metadataSearchDto, { Future? abortTrigger, }) async { + final response = await searchAssetsWithHttpInfo(metadataSearchDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -443,7 +448,7 @@ class SearchApi { /// /// * [bool] withExif: /// Include EXIF data in response - Future searchLargeAssetsWithHttpInfo({ List? albumIds, String? city, String? country, DateTime? createdAfter, DateTime? createdBefore, bool? isEncoded, bool? isFavorite, bool? isMotion, bool? isNotInAlbum, bool? isOffline, String? lensModel, String? libraryId, String? make, int? minFileSize, String? model, String? ocr, List? personIds, int? rating, int? 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 { + Future searchLargeAssetsWithHttpInfo({ List? albumIds, String? city, String? country, DateTime? createdAfter, DateTime? createdBefore, bool? isEncoded, bool? isFavorite, bool? isMotion, bool? isNotInAlbum, bool? isOffline, String? lensModel, String? libraryId, String? make, int? minFileSize, String? model, String? ocr, List? personIds, int? rating, int? 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? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/search/large-assets'; @@ -559,6 +564,7 @@ class SearchApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -658,8 +664,8 @@ class SearchApi { /// /// * [bool] withExif: /// Include EXIF data in response - Future?> searchLargeAssets({ List? albumIds, String? city, String? country, DateTime? createdAfter, DateTime? createdBefore, bool? isEncoded, bool? isFavorite, bool? isMotion, bool? isNotInAlbum, bool? isOffline, String? lensModel, String? libraryId, String? make, int? minFileSize, String? model, String? ocr, List? personIds, int? rating, int? 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, isEncoded: isEncoded, isFavorite: isFavorite, isMotion: isMotion, isNotInAlbum: isNotInAlbum, isOffline: isOffline, lensModel: lensModel, libraryId: libraryId, make: make, minFileSize: minFileSize, model: model, ocr: ocr, 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, ); + Future?> searchLargeAssets({ List? albumIds, String? city, String? country, DateTime? createdAfter, DateTime? createdBefore, bool? isEncoded, bool? isFavorite, bool? isMotion, bool? isNotInAlbum, bool? isOffline, String? lensModel, String? libraryId, String? make, int? minFileSize, String? model, String? ocr, List? personIds, int? rating, int? 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? abortTrigger, }) async { + final response = await searchLargeAssetsWithHttpInfo(albumIds: albumIds, city: city, country: country, createdAfter: createdAfter, createdBefore: createdBefore, isEncoded: isEncoded, isFavorite: isFavorite, isMotion: isMotion, isNotInAlbum: isNotInAlbum, isOffline: isOffline, lensModel: lensModel, libraryId: libraryId, make: make, minFileSize: minFileSize, model: model, ocr: ocr, 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, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -689,7 +695,7 @@ class SearchApi { /// /// * [bool] withHidden: /// Include hidden people - Future searchPersonWithHttpInfo(String name, { bool? withHidden, }) async { + Future searchPersonWithHttpInfo(String name, { bool? withHidden, Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/search/person'; @@ -716,6 +722,7 @@ class SearchApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -730,8 +737,8 @@ class SearchApi { /// /// * [bool] withHidden: /// Include hidden people - Future?> searchPerson(String name, { bool? withHidden, }) async { - final response = await searchPersonWithHttpInfo(name, withHidden: withHidden, ); + Future?> searchPerson(String name, { bool? withHidden, Future? abortTrigger, }) async { + final response = await searchPersonWithHttpInfo(name, withHidden: withHidden, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -758,7 +765,7 @@ class SearchApi { /// /// * [String] name (required): /// Place name to search for - Future searchPlacesWithHttpInfo(String name,) async { + Future searchPlacesWithHttpInfo(String name, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/search/places'; @@ -782,6 +789,7 @@ class SearchApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -793,8 +801,8 @@ class SearchApi { /// /// * [String] name (required): /// Place name to search for - Future?> searchPlaces(String name,) async { - final response = await searchPlacesWithHttpInfo(name,); + Future?> searchPlaces(String name, { Future? abortTrigger, }) async { + final response = await searchPlacesWithHttpInfo(name, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -820,7 +828,7 @@ class SearchApi { /// Parameters: /// /// * [RandomSearchDto] randomSearchDto (required): - Future searchRandomWithHttpInfo(RandomSearchDto randomSearchDto,) async { + Future searchRandomWithHttpInfo(RandomSearchDto randomSearchDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/search/random'; @@ -842,6 +850,7 @@ class SearchApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -852,8 +861,8 @@ class SearchApi { /// Parameters: /// /// * [RandomSearchDto] randomSearchDto (required): - Future?> searchRandom(RandomSearchDto randomSearchDto,) async { - final response = await searchRandomWithHttpInfo(randomSearchDto,); + Future?> searchRandom(RandomSearchDto randomSearchDto, { Future? abortTrigger, }) async { + final response = await searchRandomWithHttpInfo(randomSearchDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -879,7 +888,7 @@ class SearchApi { /// Parameters: /// /// * [SmartSearchDto] smartSearchDto (required): - Future searchSmartWithHttpInfo(SmartSearchDto smartSearchDto,) async { + Future searchSmartWithHttpInfo(SmartSearchDto smartSearchDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/search/smart'; @@ -901,6 +910,7 @@ class SearchApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -911,8 +921,8 @@ class SearchApi { /// Parameters: /// /// * [SmartSearchDto] smartSearchDto (required): - Future searchSmart(SmartSearchDto smartSearchDto,) async { - final response = await searchSmartWithHttpInfo(smartSearchDto,); + Future searchSmart(SmartSearchDto smartSearchDto, { Future? abortTrigger, }) async { + final response = await searchSmartWithHttpInfo(smartSearchDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api/server_api.dart b/mobile/openapi/lib/api/server_api.dart index dd38ade167..1a46a86188 100644 --- a/mobile/openapi/lib/api/server_api.dart +++ b/mobile/openapi/lib/api/server_api.dart @@ -21,7 +21,7 @@ class ServerApi { /// Delete the currently set server product key. /// /// Note: This method returns the HTTP [Response]. - Future deleteServerLicenseWithHttpInfo() async { + Future deleteServerLicenseWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/server/license'; @@ -43,14 +43,15 @@ class ServerApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// Delete server product key /// /// Delete the currently set server product key. - Future deleteServerLicense() async { - final response = await deleteServerLicenseWithHttpInfo(); + Future deleteServerLicense({ Future? abortTrigger, }) async { + final response = await deleteServerLicenseWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -61,7 +62,7 @@ class ServerApi { /// Retrieve a list of information about the server. /// /// Note: This method returns the HTTP [Response]. - Future getAboutInfoWithHttpInfo() async { + Future getAboutInfoWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/server/about'; @@ -83,14 +84,15 @@ class ServerApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// Get server information /// /// Retrieve a list of information about the server. - Future getAboutInfo() async { - final response = await getAboutInfoWithHttpInfo(); + Future getAboutInfo({ Future? abortTrigger, }) async { + final response = await getAboutInfoWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -109,7 +111,7 @@ class ServerApi { /// Retrieve links to the APKs for the current server version. /// /// Note: This method returns the HTTP [Response]. - Future getApkLinksWithHttpInfo() async { + Future getApkLinksWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/server/apk-links'; @@ -131,14 +133,15 @@ class ServerApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// Get APK links /// /// Retrieve links to the APKs for the current server version. - Future getApkLinks() async { - final response = await getApkLinksWithHttpInfo(); + Future getApkLinks({ Future? abortTrigger, }) async { + final response = await getApkLinksWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -157,7 +160,7 @@ class ServerApi { /// Retrieve the current server configuration. /// /// Note: This method returns the HTTP [Response]. - Future getServerConfigWithHttpInfo() async { + Future getServerConfigWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/server/config'; @@ -179,14 +182,15 @@ class ServerApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// Get config /// /// Retrieve the current server configuration. - Future getServerConfig() async { - final response = await getServerConfigWithHttpInfo(); + Future getServerConfig({ Future? abortTrigger, }) async { + final response = await getServerConfigWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -205,7 +209,7 @@ class ServerApi { /// Retrieve available features supported by this server. /// /// Note: This method returns the HTTP [Response]. - Future getServerFeaturesWithHttpInfo() async { + Future getServerFeaturesWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/server/features'; @@ -227,14 +231,15 @@ class ServerApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// Get features /// /// Retrieve available features supported by this server. - Future getServerFeatures() async { - final response = await getServerFeaturesWithHttpInfo(); + Future getServerFeatures({ Future? abortTrigger, }) async { + final response = await getServerFeaturesWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -253,7 +258,7 @@ class ServerApi { /// Retrieve information about whether the server currently has a product key registered. /// /// Note: This method returns the HTTP [Response]. - Future getServerLicenseWithHttpInfo() async { + Future getServerLicenseWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/server/license'; @@ -275,14 +280,15 @@ class ServerApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// Get product key /// /// Retrieve information about whether the server currently has a product key registered. - Future getServerLicense() async { - final response = await getServerLicenseWithHttpInfo(); + Future getServerLicense({ Future? abortTrigger, }) async { + final response = await getServerLicenseWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -301,7 +307,7 @@ class ServerApi { /// Retrieve statistics about the entire Immich instance such as asset counts. /// /// Note: This method returns the HTTP [Response]. - Future getServerStatisticsWithHttpInfo() async { + Future getServerStatisticsWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/server/statistics'; @@ -323,14 +329,15 @@ class ServerApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// Get statistics /// /// Retrieve statistics about the entire Immich instance such as asset counts. - Future getServerStatistics() async { - final response = await getServerStatisticsWithHttpInfo(); + Future getServerStatistics({ Future? abortTrigger, }) async { + final response = await getServerStatisticsWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -349,7 +356,7 @@ class ServerApi { /// Retrieve the current server version in semantic versioning (semver) format. /// /// Note: This method returns the HTTP [Response]. - Future getServerVersionWithHttpInfo() async { + Future getServerVersionWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/server/version'; @@ -371,14 +378,15 @@ class ServerApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// Get server version /// /// Retrieve the current server version in semantic versioning (semver) format. - Future getServerVersion() async { - final response = await getServerVersionWithHttpInfo(); + Future getServerVersion({ Future? abortTrigger, }) async { + final response = await getServerVersionWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -397,7 +405,7 @@ class ServerApi { /// Retrieve the current storage utilization information of the server. /// /// Note: This method returns the HTTP [Response]. - Future getStorageWithHttpInfo() async { + Future getStorageWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/server/storage'; @@ -419,14 +427,15 @@ class ServerApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// Get storage /// /// Retrieve the current storage utilization information of the server. - Future getStorage() async { - final response = await getStorageWithHttpInfo(); + Future getStorage({ Future? abortTrigger, }) async { + final response = await getStorageWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -445,7 +454,7 @@ class ServerApi { /// Retrieve all media types supported by the server. /// /// Note: This method returns the HTTP [Response]. - Future getSupportedMediaTypesWithHttpInfo() async { + Future getSupportedMediaTypesWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/server/media-types'; @@ -467,14 +476,15 @@ class ServerApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// Get supported media types /// /// Retrieve all media types supported by the server. - Future getSupportedMediaTypes() async { - final response = await getSupportedMediaTypesWithHttpInfo(); + Future getSupportedMediaTypes({ Future? abortTrigger, }) async { + final response = await getSupportedMediaTypesWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -493,7 +503,7 @@ class ServerApi { /// Retrieve information about the last time the version check ran. /// /// Note: This method returns the HTTP [Response]. - Future getVersionCheckWithHttpInfo() async { + Future getVersionCheckWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/server/version-check'; @@ -515,14 +525,15 @@ class ServerApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// Get version check status /// /// Retrieve information about the last time the version check ran. - Future getVersionCheck() async { - final response = await getVersionCheckWithHttpInfo(); + Future getVersionCheck({ Future? abortTrigger, }) async { + final response = await getVersionCheckWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -541,7 +552,7 @@ class ServerApi { /// Retrieve a list of past versions the server has been on. /// /// Note: This method returns the HTTP [Response]. - Future getVersionHistoryWithHttpInfo() async { + Future getVersionHistoryWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/server/version-history'; @@ -563,14 +574,15 @@ class ServerApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// Get version history /// /// Retrieve a list of past versions the server has been on. - Future?> getVersionHistory() async { - final response = await getVersionHistoryWithHttpInfo(); + Future?> getVersionHistory({ Future? abortTrigger, }) async { + final response = await getVersionHistoryWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -592,7 +604,7 @@ class ServerApi { /// Pong /// /// Note: This method returns the HTTP [Response]. - Future pingServerWithHttpInfo() async { + Future pingServerWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/server/ping'; @@ -614,14 +626,15 @@ class ServerApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// Ping /// /// Pong - Future pingServer() async { - final response = await pingServerWithHttpInfo(); + Future pingServer({ Future? abortTrigger, }) async { + final response = await pingServerWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -644,7 +657,7 @@ class ServerApi { /// Parameters: /// /// * [LicenseKeyDto] licenseKeyDto (required): - Future setServerLicenseWithHttpInfo(LicenseKeyDto licenseKeyDto,) async { + Future setServerLicenseWithHttpInfo(LicenseKeyDto licenseKeyDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/server/license'; @@ -666,6 +679,7 @@ class ServerApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -676,8 +690,8 @@ class ServerApi { /// Parameters: /// /// * [LicenseKeyDto] licenseKeyDto (required): - Future setServerLicense(LicenseKeyDto licenseKeyDto,) async { - final response = await setServerLicenseWithHttpInfo(licenseKeyDto,); + Future setServerLicense(LicenseKeyDto licenseKeyDto, { Future? abortTrigger, }) async { + final response = await setServerLicenseWithHttpInfo(licenseKeyDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api/sessions_api.dart b/mobile/openapi/lib/api/sessions_api.dart index da508059bc..fdd6c09266 100644 --- a/mobile/openapi/lib/api/sessions_api.dart +++ b/mobile/openapi/lib/api/sessions_api.dart @@ -25,7 +25,7 @@ class SessionsApi { /// Parameters: /// /// * [SessionCreateDto] sessionCreateDto (required): - Future createSessionWithHttpInfo(SessionCreateDto sessionCreateDto,) async { + Future createSessionWithHttpInfo(SessionCreateDto sessionCreateDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/sessions'; @@ -47,6 +47,7 @@ class SessionsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -57,8 +58,8 @@ class SessionsApi { /// Parameters: /// /// * [SessionCreateDto] sessionCreateDto (required): - Future createSession(SessionCreateDto sessionCreateDto,) async { - final response = await createSessionWithHttpInfo(sessionCreateDto,); + Future createSession(SessionCreateDto sessionCreateDto, { Future? abortTrigger, }) async { + final response = await createSessionWithHttpInfo(sessionCreateDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -77,7 +78,7 @@ class SessionsApi { /// Delete all sessions for the user. This will not delete the current session. /// /// Note: This method returns the HTTP [Response]. - Future deleteAllSessionsWithHttpInfo() async { + Future deleteAllSessionsWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/sessions'; @@ -99,14 +100,15 @@ class SessionsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// Delete all sessions /// /// Delete all sessions for the user. This will not delete the current session. - Future deleteAllSessions() async { - final response = await deleteAllSessionsWithHttpInfo(); + Future deleteAllSessions({ Future? abortTrigger, }) async { + final response = await deleteAllSessionsWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -121,7 +123,7 @@ class SessionsApi { /// Parameters: /// /// * [String] id (required): - Future deleteSessionWithHttpInfo(String id,) async { + Future deleteSessionWithHttpInfo(String id, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/sessions/{id}' .replaceAll('{id}', id); @@ -144,6 +146,7 @@ class SessionsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -154,8 +157,8 @@ class SessionsApi { /// Parameters: /// /// * [String] id (required): - Future deleteSession(String id,) async { - final response = await deleteSessionWithHttpInfo(id,); + Future deleteSession(String id, { Future? abortTrigger, }) async { + final response = await deleteSessionWithHttpInfo(id, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -166,7 +169,7 @@ class SessionsApi { /// Retrieve a list of sessions for the user. /// /// Note: This method returns the HTTP [Response]. - Future getSessionsWithHttpInfo() async { + Future getSessionsWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/sessions'; @@ -188,14 +191,15 @@ class SessionsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// Retrieve sessions /// /// Retrieve a list of sessions for the user. - Future?> getSessions() async { - final response = await getSessionsWithHttpInfo(); + Future?> getSessions({ Future? abortTrigger, }) async { + final response = await getSessionsWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -221,7 +225,7 @@ class SessionsApi { /// Parameters: /// /// * [String] id (required): - Future lockSessionWithHttpInfo(String id,) async { + Future lockSessionWithHttpInfo(String id, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/sessions/{id}/lock' .replaceAll('{id}', id); @@ -244,6 +248,7 @@ class SessionsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -254,8 +259,8 @@ class SessionsApi { /// Parameters: /// /// * [String] id (required): - Future lockSession(String id,) async { - final response = await lockSessionWithHttpInfo(id,); + Future lockSession(String id, { Future? abortTrigger, }) async { + final response = await lockSessionWithHttpInfo(id, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -272,7 +277,7 @@ class SessionsApi { /// * [String] id (required): /// /// * [SessionUpdateDto] sessionUpdateDto (required): - Future updateSessionWithHttpInfo(String id, SessionUpdateDto sessionUpdateDto,) async { + Future updateSessionWithHttpInfo(String id, SessionUpdateDto sessionUpdateDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/sessions/{id}' .replaceAll('{id}', id); @@ -295,6 +300,7 @@ class SessionsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -307,8 +313,8 @@ class SessionsApi { /// * [String] id (required): /// /// * [SessionUpdateDto] sessionUpdateDto (required): - Future updateSession(String id, SessionUpdateDto sessionUpdateDto,) async { - final response = await updateSessionWithHttpInfo(id, sessionUpdateDto,); + Future updateSession(String id, SessionUpdateDto sessionUpdateDto, { Future? abortTrigger, }) async { + final response = await updateSessionWithHttpInfo(id, sessionUpdateDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api/shared_links_api.dart b/mobile/openapi/lib/api/shared_links_api.dart index 4750442287..5bd548d7d2 100644 --- a/mobile/openapi/lib/api/shared_links_api.dart +++ b/mobile/openapi/lib/api/shared_links_api.dart @@ -27,7 +27,7 @@ class SharedLinksApi { /// * [String] id (required): /// /// * [AssetIdsDto] assetIdsDto (required): - Future addSharedLinkAssetsWithHttpInfo(String id, AssetIdsDto assetIdsDto,) async { + Future addSharedLinkAssetsWithHttpInfo(String id, AssetIdsDto assetIdsDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/shared-links/{id}/assets' .replaceAll('{id}', id); @@ -50,6 +50,7 @@ class SharedLinksApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -62,8 +63,8 @@ class SharedLinksApi { /// * [String] id (required): /// /// * [AssetIdsDto] assetIdsDto (required): - Future?> addSharedLinkAssets(String id, AssetIdsDto assetIdsDto,) async { - final response = await addSharedLinkAssetsWithHttpInfo(id, assetIdsDto,); + Future?> addSharedLinkAssets(String id, AssetIdsDto assetIdsDto, { Future? abortTrigger, }) async { + final response = await addSharedLinkAssetsWithHttpInfo(id, assetIdsDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -89,7 +90,7 @@ class SharedLinksApi { /// Parameters: /// /// * [SharedLinkCreateDto] sharedLinkCreateDto (required): - Future createSharedLinkWithHttpInfo(SharedLinkCreateDto sharedLinkCreateDto,) async { + Future createSharedLinkWithHttpInfo(SharedLinkCreateDto sharedLinkCreateDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/shared-links'; @@ -111,6 +112,7 @@ class SharedLinksApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -121,8 +123,8 @@ class SharedLinksApi { /// Parameters: /// /// * [SharedLinkCreateDto] sharedLinkCreateDto (required): - Future createSharedLink(SharedLinkCreateDto sharedLinkCreateDto,) async { - final response = await createSharedLinkWithHttpInfo(sharedLinkCreateDto,); + Future createSharedLink(SharedLinkCreateDto sharedLinkCreateDto, { Future? abortTrigger, }) async { + final response = await createSharedLinkWithHttpInfo(sharedLinkCreateDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -149,7 +151,7 @@ class SharedLinksApi { /// /// * [String] id: /// Filter by shared link ID - Future getAllSharedLinksWithHttpInfo({ String? albumId, String? id, }) async { + Future getAllSharedLinksWithHttpInfo({ String? albumId, String? id, Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/shared-links'; @@ -178,6 +180,7 @@ class SharedLinksApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -192,8 +195,8 @@ class SharedLinksApi { /// /// * [String] id: /// Filter by shared link ID - Future?> getAllSharedLinks({ String? albumId, String? id, }) async { - final response = await getAllSharedLinksWithHttpInfo( albumId: albumId, id: id, ); + Future?> getAllSharedLinks({ String? albumId, String? id, Future? abortTrigger, }) async { + final response = await getAllSharedLinksWithHttpInfo(albumId: albumId, id: id, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -221,7 +224,7 @@ class SharedLinksApi { /// * [String] key: /// /// * [String] slug: - Future getMySharedLinkWithHttpInfo({ String? key, String? slug, }) async { + Future getMySharedLinkWithHttpInfo({ String? key, String? slug, Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/shared-links/me'; @@ -250,6 +253,7 @@ class SharedLinksApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -262,8 +266,8 @@ class SharedLinksApi { /// * [String] key: /// /// * [String] slug: - Future getMySharedLink({ String? key, String? slug, }) async { - final response = await getMySharedLinkWithHttpInfo( key: key, slug: slug, ); + Future getMySharedLink({ String? key, String? slug, Future? abortTrigger, }) async { + final response = await getMySharedLinkWithHttpInfo(key: key, slug: slug, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -286,7 +290,7 @@ class SharedLinksApi { /// Parameters: /// /// * [String] id (required): - Future getSharedLinkByIdWithHttpInfo(String id,) async { + Future getSharedLinkByIdWithHttpInfo(String id, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/shared-links/{id}' .replaceAll('{id}', id); @@ -309,6 +313,7 @@ class SharedLinksApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -319,8 +324,8 @@ class SharedLinksApi { /// Parameters: /// /// * [String] id (required): - Future getSharedLinkById(String id,) async { - final response = await getSharedLinkByIdWithHttpInfo(id,); + Future getSharedLinkById(String id, { Future? abortTrigger, }) async { + final response = await getSharedLinkByIdWithHttpInfo(id, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -343,7 +348,7 @@ class SharedLinksApi { /// Parameters: /// /// * [String] id (required): - Future removeSharedLinkWithHttpInfo(String id,) async { + Future removeSharedLinkWithHttpInfo(String id, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/shared-links/{id}' .replaceAll('{id}', id); @@ -366,6 +371,7 @@ class SharedLinksApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -376,8 +382,8 @@ class SharedLinksApi { /// Parameters: /// /// * [String] id (required): - Future removeSharedLink(String id,) async { - final response = await removeSharedLinkWithHttpInfo(id,); + Future removeSharedLink(String id, { Future? abortTrigger, }) async { + final response = await removeSharedLinkWithHttpInfo(id, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -394,7 +400,7 @@ class SharedLinksApi { /// * [String] id (required): /// /// * [AssetIdsDto] assetIdsDto (required): - Future removeSharedLinkAssetsWithHttpInfo(String id, AssetIdsDto assetIdsDto,) async { + Future removeSharedLinkAssetsWithHttpInfo(String id, AssetIdsDto assetIdsDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/shared-links/{id}/assets' .replaceAll('{id}', id); @@ -417,6 +423,7 @@ class SharedLinksApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -429,8 +436,8 @@ class SharedLinksApi { /// * [String] id (required): /// /// * [AssetIdsDto] assetIdsDto (required): - Future?> removeSharedLinkAssets(String id, AssetIdsDto assetIdsDto,) async { - final response = await removeSharedLinkAssetsWithHttpInfo(id, assetIdsDto,); + Future?> removeSharedLinkAssets(String id, AssetIdsDto assetIdsDto, { Future? abortTrigger, }) async { + final response = await removeSharedLinkAssetsWithHttpInfo(id, assetIdsDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -460,7 +467,7 @@ class SharedLinksApi { /// * [String] key: /// /// * [String] slug: - Future sharedLinkLoginWithHttpInfo(SharedLinkLoginDto sharedLinkLoginDto, { String? key, String? slug, }) async { + Future sharedLinkLoginWithHttpInfo(SharedLinkLoginDto sharedLinkLoginDto, { String? key, String? slug, Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/shared-links/login'; @@ -489,6 +496,7 @@ class SharedLinksApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -503,8 +511,8 @@ class SharedLinksApi { /// * [String] key: /// /// * [String] slug: - Future sharedLinkLogin(SharedLinkLoginDto sharedLinkLoginDto, { String? key, String? slug, }) async { - final response = await sharedLinkLoginWithHttpInfo(sharedLinkLoginDto, key: key, slug: slug, ); + Future sharedLinkLogin(SharedLinkLoginDto sharedLinkLoginDto, { String? key, String? slug, Future? abortTrigger, }) async { + final response = await sharedLinkLoginWithHttpInfo(sharedLinkLoginDto, key: key, slug: slug, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -529,7 +537,7 @@ class SharedLinksApi { /// * [String] id (required): /// /// * [SharedLinkEditDto] sharedLinkEditDto (required): - Future updateSharedLinkWithHttpInfo(String id, SharedLinkEditDto sharedLinkEditDto,) async { + Future updateSharedLinkWithHttpInfo(String id, SharedLinkEditDto sharedLinkEditDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/shared-links/{id}' .replaceAll('{id}', id); @@ -552,6 +560,7 @@ class SharedLinksApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -564,8 +573,8 @@ class SharedLinksApi { /// * [String] id (required): /// /// * [SharedLinkEditDto] sharedLinkEditDto (required): - Future updateSharedLink(String id, SharedLinkEditDto sharedLinkEditDto,) async { - final response = await updateSharedLinkWithHttpInfo(id, sharedLinkEditDto,); + Future updateSharedLink(String id, SharedLinkEditDto sharedLinkEditDto, { Future? abortTrigger, }) async { + final response = await updateSharedLinkWithHttpInfo(id, sharedLinkEditDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api/stacks_api.dart b/mobile/openapi/lib/api/stacks_api.dart index a691af2a7d..a99ebe0600 100644 --- a/mobile/openapi/lib/api/stacks_api.dart +++ b/mobile/openapi/lib/api/stacks_api.dart @@ -25,7 +25,7 @@ class StacksApi { /// Parameters: /// /// * [StackCreateDto] stackCreateDto (required): - Future createStackWithHttpInfo(StackCreateDto stackCreateDto,) async { + Future createStackWithHttpInfo(StackCreateDto stackCreateDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/stacks'; @@ -47,6 +47,7 @@ class StacksApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -57,8 +58,8 @@ class StacksApi { /// Parameters: /// /// * [StackCreateDto] stackCreateDto (required): - Future createStack(StackCreateDto stackCreateDto,) async { - final response = await createStackWithHttpInfo(stackCreateDto,); + Future createStack(StackCreateDto stackCreateDto, { Future? abortTrigger, }) async { + final response = await createStackWithHttpInfo(stackCreateDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -81,7 +82,7 @@ class StacksApi { /// Parameters: /// /// * [String] id (required): - Future deleteStackWithHttpInfo(String id,) async { + Future deleteStackWithHttpInfo(String id, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/stacks/{id}' .replaceAll('{id}', id); @@ -104,6 +105,7 @@ class StacksApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -114,8 +116,8 @@ class StacksApi { /// Parameters: /// /// * [String] id (required): - Future deleteStack(String id,) async { - final response = await deleteStackWithHttpInfo(id,); + Future deleteStack(String id, { Future? abortTrigger, }) async { + final response = await deleteStackWithHttpInfo(id, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -130,7 +132,7 @@ class StacksApi { /// Parameters: /// /// * [BulkIdsDto] bulkIdsDto (required): - Future deleteStacksWithHttpInfo(BulkIdsDto bulkIdsDto,) async { + Future deleteStacksWithHttpInfo(BulkIdsDto bulkIdsDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/stacks'; @@ -152,6 +154,7 @@ class StacksApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -162,8 +165,8 @@ class StacksApi { /// Parameters: /// /// * [BulkIdsDto] bulkIdsDto (required): - Future deleteStacks(BulkIdsDto bulkIdsDto,) async { - final response = await deleteStacksWithHttpInfo(bulkIdsDto,); + Future deleteStacks(BulkIdsDto bulkIdsDto, { Future? abortTrigger, }) async { + final response = await deleteStacksWithHttpInfo(bulkIdsDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -178,7 +181,7 @@ class StacksApi { /// Parameters: /// /// * [String] id (required): - Future getStackWithHttpInfo(String id,) async { + Future getStackWithHttpInfo(String id, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/stacks/{id}' .replaceAll('{id}', id); @@ -201,6 +204,7 @@ class StacksApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -211,8 +215,8 @@ class StacksApi { /// Parameters: /// /// * [String] id (required): - Future getStack(String id,) async { - final response = await getStackWithHttpInfo(id,); + Future getStack(String id, { Future? abortTrigger, }) async { + final response = await getStackWithHttpInfo(id, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -237,7 +241,7 @@ class StacksApi { /// * [String] assetId (required): /// /// * [String] id (required): - Future removeAssetFromStackWithHttpInfo(String assetId, String id,) async { + Future removeAssetFromStackWithHttpInfo(String assetId, String id, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/stacks/{id}/assets/{assetId}' .replaceAll('{assetId}', assetId) @@ -261,6 +265,7 @@ class StacksApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -273,8 +278,8 @@ class StacksApi { /// * [String] assetId (required): /// /// * [String] id (required): - Future removeAssetFromStack(String assetId, String id,) async { - final response = await removeAssetFromStackWithHttpInfo(assetId, id,); + Future removeAssetFromStack(String assetId, String id, { Future? abortTrigger, }) async { + final response = await removeAssetFromStackWithHttpInfo(assetId, id, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -290,7 +295,7 @@ class StacksApi { /// /// * [String] primaryAssetId: /// Filter by primary asset ID - Future searchStacksWithHttpInfo({ String? primaryAssetId, }) async { + Future searchStacksWithHttpInfo({ String? primaryAssetId, Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/stacks'; @@ -316,6 +321,7 @@ class StacksApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -327,8 +333,8 @@ class StacksApi { /// /// * [String] primaryAssetId: /// Filter by primary asset ID - Future?> searchStacks({ String? primaryAssetId, }) async { - final response = await searchStacksWithHttpInfo( primaryAssetId: primaryAssetId, ); + Future?> searchStacks({ String? primaryAssetId, Future? abortTrigger, }) async { + final response = await searchStacksWithHttpInfo(primaryAssetId: primaryAssetId, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -356,7 +362,7 @@ class StacksApi { /// * [String] id (required): /// /// * [StackUpdateDto] stackUpdateDto (required): - Future updateStackWithHttpInfo(String id, StackUpdateDto stackUpdateDto,) async { + Future updateStackWithHttpInfo(String id, StackUpdateDto stackUpdateDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/stacks/{id}' .replaceAll('{id}', id); @@ -379,6 +385,7 @@ class StacksApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -391,8 +398,8 @@ class StacksApi { /// * [String] id (required): /// /// * [StackUpdateDto] stackUpdateDto (required): - Future updateStack(String id, StackUpdateDto stackUpdateDto,) async { - final response = await updateStackWithHttpInfo(id, stackUpdateDto,); + Future updateStack(String id, StackUpdateDto stackUpdateDto, { Future? abortTrigger, }) async { + final response = await updateStackWithHttpInfo(id, stackUpdateDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api/sync_api.dart b/mobile/openapi/lib/api/sync_api.dart index e7bc822ace..c2a57c3395 100644 --- a/mobile/openapi/lib/api/sync_api.dart +++ b/mobile/openapi/lib/api/sync_api.dart @@ -25,7 +25,7 @@ class SyncApi { /// Parameters: /// /// * [SyncAckDeleteDto] syncAckDeleteDto (required): - Future deleteSyncAckWithHttpInfo(SyncAckDeleteDto syncAckDeleteDto,) async { + Future deleteSyncAckWithHttpInfo(SyncAckDeleteDto syncAckDeleteDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/sync/ack'; @@ -47,6 +47,7 @@ class SyncApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -57,8 +58,8 @@ class SyncApi { /// Parameters: /// /// * [SyncAckDeleteDto] syncAckDeleteDto (required): - Future deleteSyncAck(SyncAckDeleteDto syncAckDeleteDto,) async { - final response = await deleteSyncAckWithHttpInfo(syncAckDeleteDto,); + Future deleteSyncAck(SyncAckDeleteDto syncAckDeleteDto, { Future? abortTrigger, }) async { + final response = await deleteSyncAckWithHttpInfo(syncAckDeleteDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -69,7 +70,7 @@ class SyncApi { /// Retrieve the synchronization acknowledgments for the current session. /// /// Note: This method returns the HTTP [Response]. - Future getSyncAckWithHttpInfo() async { + Future getSyncAckWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/sync/ack'; @@ -91,14 +92,15 @@ class SyncApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// Retrieve acknowledgements /// /// Retrieve the synchronization acknowledgments for the current session. - Future?> getSyncAck() async { - final response = await getSyncAckWithHttpInfo(); + Future?> getSyncAck({ Future? abortTrigger, }) async { + final response = await getSyncAckWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -124,7 +126,7 @@ class SyncApi { /// Parameters: /// /// * [SyncStreamDto] syncStreamDto (required): - Future getSyncStreamWithHttpInfo(SyncStreamDto syncStreamDto,) async { + Future getSyncStreamWithHttpInfo(SyncStreamDto syncStreamDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/sync/stream'; @@ -146,6 +148,7 @@ class SyncApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -156,8 +159,8 @@ class SyncApi { /// Parameters: /// /// * [SyncStreamDto] syncStreamDto (required): - Future getSyncStream(SyncStreamDto syncStreamDto,) async { - final response = await getSyncStreamWithHttpInfo(syncStreamDto,); + Future getSyncStream(SyncStreamDto syncStreamDto, { Future? abortTrigger, }) async { + final response = await getSyncStreamWithHttpInfo(syncStreamDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -172,7 +175,7 @@ class SyncApi { /// Parameters: /// /// * [SyncAckSetDto] syncAckSetDto (required): - Future sendSyncAckWithHttpInfo(SyncAckSetDto syncAckSetDto,) async { + Future sendSyncAckWithHttpInfo(SyncAckSetDto syncAckSetDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/sync/ack'; @@ -194,6 +197,7 @@ class SyncApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -204,8 +208,8 @@ class SyncApi { /// Parameters: /// /// * [SyncAckSetDto] syncAckSetDto (required): - Future sendSyncAck(SyncAckSetDto syncAckSetDto,) async { - final response = await sendSyncAckWithHttpInfo(syncAckSetDto,); + Future sendSyncAck(SyncAckSetDto syncAckSetDto, { Future? abortTrigger, }) async { + final response = await sendSyncAckWithHttpInfo(syncAckSetDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api/system_config_api.dart b/mobile/openapi/lib/api/system_config_api.dart index b04da71273..ba5b82263a 100644 --- a/mobile/openapi/lib/api/system_config_api.dart +++ b/mobile/openapi/lib/api/system_config_api.dart @@ -21,7 +21,7 @@ class SystemConfigApi { /// Retrieve the current system configuration. /// /// Note: This method returns the HTTP [Response]. - Future getConfigWithHttpInfo() async { + Future getConfigWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/system-config'; @@ -43,14 +43,15 @@ class SystemConfigApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// Get system configuration /// /// Retrieve the current system configuration. - Future getConfig() async { - final response = await getConfigWithHttpInfo(); + Future getConfig({ Future? abortTrigger, }) async { + final response = await getConfigWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -69,7 +70,7 @@ class SystemConfigApi { /// Retrieve the default values for the system configuration. /// /// Note: This method returns the HTTP [Response]. - Future getConfigDefaultsWithHttpInfo() async { + Future getConfigDefaultsWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/system-config/defaults'; @@ -91,14 +92,15 @@ class SystemConfigApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// Get system configuration defaults /// /// Retrieve the default values for the system configuration. - Future getConfigDefaults() async { - final response = await getConfigDefaultsWithHttpInfo(); + Future getConfigDefaults({ Future? abortTrigger, }) async { + final response = await getConfigDefaultsWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -117,7 +119,7 @@ class SystemConfigApi { /// Retrieve exemplary storage template options. /// /// Note: This method returns the HTTP [Response]. - Future getStorageTemplateOptionsWithHttpInfo() async { + Future getStorageTemplateOptionsWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/system-config/storage-template-options'; @@ -139,14 +141,15 @@ class SystemConfigApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// Get storage template options /// /// Retrieve exemplary storage template options. - Future getStorageTemplateOptions() async { - final response = await getStorageTemplateOptionsWithHttpInfo(); + Future getStorageTemplateOptions({ Future? abortTrigger, }) async { + final response = await getStorageTemplateOptionsWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -169,7 +172,7 @@ class SystemConfigApi { /// Parameters: /// /// * [SystemConfigDto] systemConfigDto (required): - Future updateConfigWithHttpInfo(SystemConfigDto systemConfigDto,) async { + Future updateConfigWithHttpInfo(SystemConfigDto systemConfigDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/system-config'; @@ -191,6 +194,7 @@ class SystemConfigApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -201,8 +205,8 @@ class SystemConfigApi { /// Parameters: /// /// * [SystemConfigDto] systemConfigDto (required): - Future updateConfig(SystemConfigDto systemConfigDto,) async { - final response = await updateConfigWithHttpInfo(systemConfigDto,); + Future updateConfig(SystemConfigDto systemConfigDto, { Future? abortTrigger, }) async { + final response = await updateConfigWithHttpInfo(systemConfigDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api/system_metadata_api.dart b/mobile/openapi/lib/api/system_metadata_api.dart index 63fd7628ec..a1429b54b0 100644 --- a/mobile/openapi/lib/api/system_metadata_api.dart +++ b/mobile/openapi/lib/api/system_metadata_api.dart @@ -21,7 +21,7 @@ class SystemMetadataApi { /// Retrieve the current admin onboarding status. /// /// Note: This method returns the HTTP [Response]. - Future getAdminOnboardingWithHttpInfo() async { + Future getAdminOnboardingWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/system-metadata/admin-onboarding'; @@ -43,14 +43,15 @@ class SystemMetadataApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// Retrieve admin onboarding /// /// Retrieve the current admin onboarding status. - Future getAdminOnboarding() async { - final response = await getAdminOnboardingWithHttpInfo(); + Future getAdminOnboarding({ Future? abortTrigger, }) async { + final response = await getAdminOnboardingWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -69,7 +70,7 @@ class SystemMetadataApi { /// Retrieve the current state of the reverse geocoding import. /// /// Note: This method returns the HTTP [Response]. - Future getReverseGeocodingStateWithHttpInfo() async { + Future getReverseGeocodingStateWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/system-metadata/reverse-geocoding-state'; @@ -91,14 +92,15 @@ class SystemMetadataApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// Retrieve reverse geocoding state /// /// Retrieve the current state of the reverse geocoding import. - Future getReverseGeocodingState() async { - final response = await getReverseGeocodingStateWithHttpInfo(); + Future getReverseGeocodingState({ Future? abortTrigger, }) async { + final response = await getReverseGeocodingStateWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -117,7 +119,7 @@ class SystemMetadataApi { /// Retrieve the current state of the version check process. /// /// Note: This method returns the HTTP [Response]. - Future getVersionCheckStateWithHttpInfo() async { + Future getVersionCheckStateWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/system-metadata/version-check-state'; @@ -139,14 +141,15 @@ class SystemMetadataApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// Retrieve version check state /// /// Retrieve the current state of the version check process. - Future getVersionCheckState() async { - final response = await getVersionCheckStateWithHttpInfo(); + Future getVersionCheckState({ Future? abortTrigger, }) async { + final response = await getVersionCheckStateWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -169,7 +172,7 @@ class SystemMetadataApi { /// Parameters: /// /// * [AdminOnboardingUpdateDto] adminOnboardingUpdateDto (required): - Future updateAdminOnboardingWithHttpInfo(AdminOnboardingUpdateDto adminOnboardingUpdateDto,) async { + Future updateAdminOnboardingWithHttpInfo(AdminOnboardingUpdateDto adminOnboardingUpdateDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/system-metadata/admin-onboarding'; @@ -191,6 +194,7 @@ class SystemMetadataApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -201,8 +205,8 @@ class SystemMetadataApi { /// Parameters: /// /// * [AdminOnboardingUpdateDto] adminOnboardingUpdateDto (required): - Future updateAdminOnboarding(AdminOnboardingUpdateDto adminOnboardingUpdateDto,) async { - final response = await updateAdminOnboardingWithHttpInfo(adminOnboardingUpdateDto,); + Future updateAdminOnboarding(AdminOnboardingUpdateDto adminOnboardingUpdateDto, { Future? abortTrigger, }) async { + final response = await updateAdminOnboardingWithHttpInfo(adminOnboardingUpdateDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api/tags_api.dart b/mobile/openapi/lib/api/tags_api.dart index a6840f9483..c3cf9f545c 100644 --- a/mobile/openapi/lib/api/tags_api.dart +++ b/mobile/openapi/lib/api/tags_api.dart @@ -25,7 +25,7 @@ class TagsApi { /// Parameters: /// /// * [TagBulkAssetsDto] tagBulkAssetsDto (required): - Future bulkTagAssetsWithHttpInfo(TagBulkAssetsDto tagBulkAssetsDto,) async { + Future bulkTagAssetsWithHttpInfo(TagBulkAssetsDto tagBulkAssetsDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/tags/assets'; @@ -47,6 +47,7 @@ class TagsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -57,8 +58,8 @@ class TagsApi { /// Parameters: /// /// * [TagBulkAssetsDto] tagBulkAssetsDto (required): - Future bulkTagAssets(TagBulkAssetsDto tagBulkAssetsDto,) async { - final response = await bulkTagAssetsWithHttpInfo(tagBulkAssetsDto,); + Future bulkTagAssets(TagBulkAssetsDto tagBulkAssetsDto, { Future? abortTrigger, }) async { + final response = await bulkTagAssetsWithHttpInfo(tagBulkAssetsDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -81,7 +82,7 @@ class TagsApi { /// Parameters: /// /// * [TagCreateDto] tagCreateDto (required): - Future createTagWithHttpInfo(TagCreateDto tagCreateDto,) async { + Future createTagWithHttpInfo(TagCreateDto tagCreateDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/tags'; @@ -103,6 +104,7 @@ class TagsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -113,8 +115,8 @@ class TagsApi { /// Parameters: /// /// * [TagCreateDto] tagCreateDto (required): - Future createTag(TagCreateDto tagCreateDto,) async { - final response = await createTagWithHttpInfo(tagCreateDto,); + Future createTag(TagCreateDto tagCreateDto, { Future? abortTrigger, }) async { + final response = await createTagWithHttpInfo(tagCreateDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -137,7 +139,7 @@ class TagsApi { /// Parameters: /// /// * [String] id (required): - Future deleteTagWithHttpInfo(String id,) async { + Future deleteTagWithHttpInfo(String id, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/tags/{id}' .replaceAll('{id}', id); @@ -160,6 +162,7 @@ class TagsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -170,8 +173,8 @@ class TagsApi { /// Parameters: /// /// * [String] id (required): - Future deleteTag(String id,) async { - final response = await deleteTagWithHttpInfo(id,); + Future deleteTag(String id, { Future? abortTrigger, }) async { + final response = await deleteTagWithHttpInfo(id, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -182,7 +185,7 @@ class TagsApi { /// Retrieve a list of all tags. /// /// Note: This method returns the HTTP [Response]. - Future getAllTagsWithHttpInfo() async { + Future getAllTagsWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/tags'; @@ -204,14 +207,15 @@ class TagsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// Retrieve tags /// /// Retrieve a list of all tags. - Future?> getAllTags() async { - final response = await getAllTagsWithHttpInfo(); + Future?> getAllTags({ Future? abortTrigger, }) async { + final response = await getAllTagsWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -237,7 +241,7 @@ class TagsApi { /// Parameters: /// /// * [String] id (required): - Future getTagByIdWithHttpInfo(String id,) async { + Future getTagByIdWithHttpInfo(String id, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/tags/{id}' .replaceAll('{id}', id); @@ -260,6 +264,7 @@ class TagsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -270,8 +275,8 @@ class TagsApi { /// Parameters: /// /// * [String] id (required): - Future getTagById(String id,) async { - final response = await getTagByIdWithHttpInfo(id,); + Future getTagById(String id, { Future? abortTrigger, }) async { + final response = await getTagByIdWithHttpInfo(id, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -296,7 +301,7 @@ class TagsApi { /// * [String] id (required): /// /// * [BulkIdsDto] bulkIdsDto (required): - Future tagAssetsWithHttpInfo(String id, BulkIdsDto bulkIdsDto,) async { + Future tagAssetsWithHttpInfo(String id, BulkIdsDto bulkIdsDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/tags/{id}/assets' .replaceAll('{id}', id); @@ -319,6 +324,7 @@ class TagsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -331,8 +337,8 @@ class TagsApi { /// * [String] id (required): /// /// * [BulkIdsDto] bulkIdsDto (required): - Future?> tagAssets(String id, BulkIdsDto bulkIdsDto,) async { - final response = await tagAssetsWithHttpInfo(id, bulkIdsDto,); + Future?> tagAssets(String id, BulkIdsDto bulkIdsDto, { Future? abortTrigger, }) async { + final response = await tagAssetsWithHttpInfo(id, bulkIdsDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -360,7 +366,7 @@ class TagsApi { /// * [String] id (required): /// /// * [BulkIdsDto] bulkIdsDto (required): - Future untagAssetsWithHttpInfo(String id, BulkIdsDto bulkIdsDto,) async { + Future untagAssetsWithHttpInfo(String id, BulkIdsDto bulkIdsDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/tags/{id}/assets' .replaceAll('{id}', id); @@ -383,6 +389,7 @@ class TagsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -395,8 +402,8 @@ class TagsApi { /// * [String] id (required): /// /// * [BulkIdsDto] bulkIdsDto (required): - Future?> untagAssets(String id, BulkIdsDto bulkIdsDto,) async { - final response = await untagAssetsWithHttpInfo(id, bulkIdsDto,); + Future?> untagAssets(String id, BulkIdsDto bulkIdsDto, { Future? abortTrigger, }) async { + final response = await untagAssetsWithHttpInfo(id, bulkIdsDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -424,7 +431,7 @@ class TagsApi { /// * [String] id (required): /// /// * [TagUpdateDto] tagUpdateDto (required): - Future updateTagWithHttpInfo(String id, TagUpdateDto tagUpdateDto,) async { + Future updateTagWithHttpInfo(String id, TagUpdateDto tagUpdateDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/tags/{id}' .replaceAll('{id}', id); @@ -447,6 +454,7 @@ class TagsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -459,8 +467,8 @@ class TagsApi { /// * [String] id (required): /// /// * [TagUpdateDto] tagUpdateDto (required): - Future updateTag(String id, TagUpdateDto tagUpdateDto,) async { - final response = await updateTagWithHttpInfo(id, tagUpdateDto,); + Future updateTag(String id, TagUpdateDto tagUpdateDto, { Future? abortTrigger, }) async { + final response = await updateTagWithHttpInfo(id, tagUpdateDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -483,7 +491,7 @@ class TagsApi { /// Parameters: /// /// * [TagUpsertDto] tagUpsertDto (required): - Future upsertTagsWithHttpInfo(TagUpsertDto tagUpsertDto,) async { + Future upsertTagsWithHttpInfo(TagUpsertDto tagUpsertDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/tags'; @@ -505,6 +513,7 @@ class TagsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -515,8 +524,8 @@ class TagsApi { /// Parameters: /// /// * [TagUpsertDto] tagUpsertDto (required): - Future?> upsertTags(TagUpsertDto tagUpsertDto,) async { - final response = await upsertTagsWithHttpInfo(tagUpsertDto,); + Future?> upsertTags(TagUpsertDto tagUpsertDto, { Future? abortTrigger, }) async { + final response = await upsertTagsWithHttpInfo(tagUpsertDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api/timeline_api.dart b/mobile/openapi/lib/api/timeline_api.dart index 6c72f62604..a85aee2d7a 100644 --- a/mobile/openapi/lib/api/timeline_api.dart +++ b/mobile/openapi/lib/api/timeline_api.dart @@ -69,7 +69,7 @@ class TimelineApi { /// /// * [bool] withStacked: /// Include stacked assets in the response. When true, only primary assets from stacks are returned. - Future getTimeBucketWithHttpInfo(String timeBucket, { String? albumId, String? bbox, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, AssetOrderBy? orderBy, String? personId, String? slug, String? tagId, String? userId, AssetVisibility? visibility, bool? withCoordinates, bool? withPartners, bool? withStacked, }) async { + Future getTimeBucketWithHttpInfo(String timeBucket, { String? albumId, String? bbox, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, AssetOrderBy? orderBy, String? personId, String? slug, String? tagId, String? userId, AssetVisibility? visibility, bool? withCoordinates, bool? withPartners, bool? withStacked, Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/timeline/bucket'; @@ -138,6 +138,7 @@ class TimelineApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -192,8 +193,8 @@ class TimelineApi { /// /// * [bool] withStacked: /// Include stacked assets in the response. When true, only primary assets from stacks are returned. - Future getTimeBucket(String timeBucket, { String? albumId, String? bbox, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, AssetOrderBy? orderBy, String? personId, String? slug, String? tagId, String? userId, AssetVisibility? visibility, bool? withCoordinates, bool? withPartners, bool? withStacked, }) async { - final response = await getTimeBucketWithHttpInfo(timeBucket, albumId: albumId, bbox: bbox, isFavorite: isFavorite, isTrashed: isTrashed, key: key, order: order, orderBy: orderBy, personId: personId, slug: slug, tagId: tagId, userId: userId, visibility: visibility, withCoordinates: withCoordinates, withPartners: withPartners, withStacked: withStacked, ); + Future getTimeBucket(String timeBucket, { String? albumId, String? bbox, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, AssetOrderBy? orderBy, String? personId, String? slug, String? tagId, String? userId, AssetVisibility? visibility, bool? withCoordinates, bool? withPartners, bool? withStacked, Future? abortTrigger, }) async { + final response = await getTimeBucketWithHttpInfo(timeBucket, albumId: albumId, bbox: bbox, isFavorite: isFavorite, isTrashed: isTrashed, key: key, order: order, orderBy: orderBy, personId: personId, slug: slug, tagId: tagId, userId: userId, visibility: visibility, withCoordinates: withCoordinates, withPartners: withPartners, withStacked: withStacked, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -257,7 +258,7 @@ class TimelineApi { /// /// * [bool] withStacked: /// Include stacked assets in the response. When true, only primary assets from stacks are returned. - Future getTimeBucketsWithHttpInfo({ String? albumId, String? bbox, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, AssetOrderBy? orderBy, String? personId, String? slug, String? tagId, String? userId, AssetVisibility? visibility, bool? withCoordinates, bool? withPartners, bool? withStacked, }) async { + Future getTimeBucketsWithHttpInfo({ String? albumId, String? bbox, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, AssetOrderBy? orderBy, String? personId, String? slug, String? tagId, String? userId, AssetVisibility? visibility, bool? withCoordinates, bool? withPartners, bool? withStacked, Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/timeline/buckets'; @@ -325,6 +326,7 @@ class TimelineApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -376,8 +378,8 @@ class TimelineApi { /// /// * [bool] withStacked: /// Include stacked assets in the response. When true, only primary assets from stacks are returned. - Future?> getTimeBuckets({ String? albumId, String? bbox, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, AssetOrderBy? orderBy, String? personId, String? slug, String? tagId, String? userId, AssetVisibility? visibility, bool? withCoordinates, bool? withPartners, bool? withStacked, }) async { - final response = await getTimeBucketsWithHttpInfo( albumId: albumId, bbox: bbox, isFavorite: isFavorite, isTrashed: isTrashed, key: key, order: order, orderBy: orderBy, personId: personId, slug: slug, tagId: tagId, userId: userId, visibility: visibility, withCoordinates: withCoordinates, withPartners: withPartners, withStacked: withStacked, ); + Future?> getTimeBuckets({ String? albumId, String? bbox, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, AssetOrderBy? orderBy, String? personId, String? slug, String? tagId, String? userId, AssetVisibility? visibility, bool? withCoordinates, bool? withPartners, bool? withStacked, Future? abortTrigger, }) async { + final response = await getTimeBucketsWithHttpInfo(albumId: albumId, bbox: bbox, isFavorite: isFavorite, isTrashed: isTrashed, key: key, order: order, orderBy: orderBy, personId: personId, slug: slug, tagId: tagId, userId: userId, visibility: visibility, withCoordinates: withCoordinates, withPartners: withPartners, withStacked: withStacked, abortTrigger: abortTrigger,); 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 f1dcbb8896..7b593e5111 100644 --- a/mobile/openapi/lib/api/trash_api.dart +++ b/mobile/openapi/lib/api/trash_api.dart @@ -21,7 +21,7 @@ class TrashApi { /// Permanently delete all items in the trash. /// /// Note: This method returns the HTTP [Response]. - Future emptyTrashWithHttpInfo() async { + Future emptyTrashWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/trash/empty'; @@ -43,14 +43,15 @@ class TrashApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// Empty trash /// /// Permanently delete all items in the trash. - Future emptyTrash() async { - final response = await emptyTrashWithHttpInfo(); + Future emptyTrash({ Future? abortTrigger, }) async { + final response = await emptyTrashWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -73,7 +74,7 @@ class TrashApi { /// Parameters: /// /// * [BulkIdsDto] bulkIdsDto (required): - Future restoreAssetsWithHttpInfo(BulkIdsDto bulkIdsDto,) async { + Future restoreAssetsWithHttpInfo(BulkIdsDto bulkIdsDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/trash/restore/assets'; @@ -95,6 +96,7 @@ class TrashApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -105,8 +107,8 @@ class TrashApi { /// Parameters: /// /// * [BulkIdsDto] bulkIdsDto (required): - Future restoreAssets(BulkIdsDto bulkIdsDto,) async { - final response = await restoreAssetsWithHttpInfo(bulkIdsDto,); + Future restoreAssets(BulkIdsDto bulkIdsDto, { Future? abortTrigger, }) async { + final response = await restoreAssetsWithHttpInfo(bulkIdsDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -125,7 +127,7 @@ class TrashApi { /// Restore all items in the trash. /// /// Note: This method returns the HTTP [Response]. - Future restoreTrashWithHttpInfo() async { + Future restoreTrashWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/trash/restore'; @@ -147,14 +149,15 @@ class TrashApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// Restore trash /// /// Restore all items in the trash. - Future restoreTrash() async { - final response = await restoreTrashWithHttpInfo(); + Future restoreTrash({ Future? abortTrigger, }) async { + final response = await restoreTrashWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api/users_admin_api.dart b/mobile/openapi/lib/api/users_admin_api.dart index 5e165ffd5d..fd6b43d9ce 100644 --- a/mobile/openapi/lib/api/users_admin_api.dart +++ b/mobile/openapi/lib/api/users_admin_api.dart @@ -25,7 +25,7 @@ class UsersAdminApi { /// Parameters: /// /// * [UserAdminCreateDto] userAdminCreateDto (required): - Future createUserAdminWithHttpInfo(UserAdminCreateDto userAdminCreateDto,) async { + Future createUserAdminWithHttpInfo(UserAdminCreateDto userAdminCreateDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/admin/users'; @@ -47,6 +47,7 @@ class UsersAdminApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -57,8 +58,8 @@ class UsersAdminApi { /// Parameters: /// /// * [UserAdminCreateDto] userAdminCreateDto (required): - Future createUserAdmin(UserAdminCreateDto userAdminCreateDto,) async { - final response = await createUserAdminWithHttpInfo(userAdminCreateDto,); + Future createUserAdmin(UserAdminCreateDto userAdminCreateDto, { Future? abortTrigger, }) async { + final response = await createUserAdminWithHttpInfo(userAdminCreateDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -83,7 +84,7 @@ class UsersAdminApi { /// * [String] id (required): /// /// * [UserAdminDeleteDto] userAdminDeleteDto (required): - Future deleteUserAdminWithHttpInfo(String id, UserAdminDeleteDto userAdminDeleteDto,) async { + Future deleteUserAdminWithHttpInfo(String id, UserAdminDeleteDto userAdminDeleteDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/admin/users/{id}' .replaceAll('{id}', id); @@ -106,6 +107,7 @@ class UsersAdminApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -118,8 +120,8 @@ class UsersAdminApi { /// * [String] id (required): /// /// * [UserAdminDeleteDto] userAdminDeleteDto (required): - Future deleteUserAdmin(String id, UserAdminDeleteDto userAdminDeleteDto,) async { - final response = await deleteUserAdminWithHttpInfo(id, userAdminDeleteDto,); + Future deleteUserAdmin(String id, UserAdminDeleteDto userAdminDeleteDto, { Future? abortTrigger, }) async { + final response = await deleteUserAdminWithHttpInfo(id, userAdminDeleteDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -142,7 +144,7 @@ class UsersAdminApi { /// Parameters: /// /// * [String] id (required): - Future getUserAdminWithHttpInfo(String id,) async { + Future getUserAdminWithHttpInfo(String id, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/admin/users/{id}' .replaceAll('{id}', id); @@ -165,6 +167,7 @@ class UsersAdminApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -175,8 +178,8 @@ class UsersAdminApi { /// Parameters: /// /// * [String] id (required): - Future getUserAdmin(String id,) async { - final response = await getUserAdminWithHttpInfo(id,); + Future getUserAdmin(String id, { Future? abortTrigger, }) async { + final response = await getUserAdminWithHttpInfo(id, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -199,7 +202,7 @@ class UsersAdminApi { /// Parameters: /// /// * [String] id (required): - Future getUserPreferencesAdminWithHttpInfo(String id,) async { + Future getUserPreferencesAdminWithHttpInfo(String id, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/admin/users/{id}/preferences' .replaceAll('{id}', id); @@ -222,6 +225,7 @@ class UsersAdminApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -232,8 +236,8 @@ class UsersAdminApi { /// Parameters: /// /// * [String] id (required): - Future getUserPreferencesAdmin(String id,) async { - final response = await getUserPreferencesAdminWithHttpInfo(id,); + Future getUserPreferencesAdmin(String id, { Future? abortTrigger, }) async { + final response = await getUserPreferencesAdminWithHttpInfo(id, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -256,7 +260,7 @@ class UsersAdminApi { /// Parameters: /// /// * [String] id (required): - Future getUserSessionsAdminWithHttpInfo(String id,) async { + Future getUserSessionsAdminWithHttpInfo(String id, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/admin/users/{id}/sessions' .replaceAll('{id}', id); @@ -279,6 +283,7 @@ class UsersAdminApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -289,8 +294,8 @@ class UsersAdminApi { /// Parameters: /// /// * [String] id (required): - Future?> getUserSessionsAdmin(String id,) async { - final response = await getUserSessionsAdminWithHttpInfo(id,); + Future?> getUserSessionsAdmin(String id, { Future? abortTrigger, }) async { + final response = await getUserSessionsAdminWithHttpInfo(id, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -324,7 +329,7 @@ class UsersAdminApi { /// Filter by trash status /// /// * [AssetVisibility] visibility: - Future getUserStatisticsAdminWithHttpInfo(String id, { bool? isFavorite, bool? isTrashed, AssetVisibility? visibility, }) async { + Future getUserStatisticsAdminWithHttpInfo(String id, { bool? isFavorite, bool? isTrashed, AssetVisibility? visibility, Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/admin/users/{id}/statistics' .replaceAll('{id}', id); @@ -357,6 +362,7 @@ class UsersAdminApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -375,8 +381,8 @@ class UsersAdminApi { /// Filter by trash status /// /// * [AssetVisibility] visibility: - Future getUserStatisticsAdmin(String id, { bool? isFavorite, bool? isTrashed, AssetVisibility? visibility, }) async { - final response = await getUserStatisticsAdminWithHttpInfo(id, isFavorite: isFavorite, isTrashed: isTrashed, visibility: visibility, ); + Future getUserStatisticsAdmin(String id, { bool? isFavorite, bool? isTrashed, AssetVisibility? visibility, Future? abortTrigger, }) async { + final response = await getUserStatisticsAdminWithHttpInfo(id, isFavorite: isFavorite, isTrashed: isTrashed, visibility: visibility, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -399,7 +405,7 @@ class UsersAdminApi { /// Parameters: /// /// * [String] id (required): - Future restoreUserAdminWithHttpInfo(String id,) async { + Future restoreUserAdminWithHttpInfo(String id, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/admin/users/{id}/restore' .replaceAll('{id}', id); @@ -422,6 +428,7 @@ class UsersAdminApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -432,8 +439,8 @@ class UsersAdminApi { /// Parameters: /// /// * [String] id (required): - Future restoreUserAdmin(String id,) async { - final response = await restoreUserAdminWithHttpInfo(id,); + Future restoreUserAdmin(String id, { Future? abortTrigger, }) async { + final response = await restoreUserAdminWithHttpInfo(id, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -460,7 +467,7 @@ class UsersAdminApi { /// /// * [bool] withDeleted: /// Include deleted users - Future searchUsersAdminWithHttpInfo({ String? id, bool? withDeleted, }) async { + Future searchUsersAdminWithHttpInfo({ String? id, bool? withDeleted, Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/admin/users'; @@ -489,6 +496,7 @@ class UsersAdminApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -503,8 +511,8 @@ class UsersAdminApi { /// /// * [bool] withDeleted: /// Include deleted users - Future?> searchUsersAdmin({ String? id, bool? withDeleted, }) async { - final response = await searchUsersAdminWithHttpInfo( id: id, withDeleted: withDeleted, ); + Future?> searchUsersAdmin({ String? id, bool? withDeleted, Future? abortTrigger, }) async { + final response = await searchUsersAdminWithHttpInfo(id: id, withDeleted: withDeleted, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -532,7 +540,7 @@ class UsersAdminApi { /// * [String] id (required): /// /// * [UserAdminUpdateDto] userAdminUpdateDto (required): - Future updateUserAdminWithHttpInfo(String id, UserAdminUpdateDto userAdminUpdateDto,) async { + Future updateUserAdminWithHttpInfo(String id, UserAdminUpdateDto userAdminUpdateDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/admin/users/{id}' .replaceAll('{id}', id); @@ -555,6 +563,7 @@ class UsersAdminApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -567,8 +576,8 @@ class UsersAdminApi { /// * [String] id (required): /// /// * [UserAdminUpdateDto] userAdminUpdateDto (required): - Future updateUserAdmin(String id, UserAdminUpdateDto userAdminUpdateDto,) async { - final response = await updateUserAdminWithHttpInfo(id, userAdminUpdateDto,); + Future updateUserAdmin(String id, UserAdminUpdateDto userAdminUpdateDto, { Future? abortTrigger, }) async { + final response = await updateUserAdminWithHttpInfo(id, userAdminUpdateDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -593,7 +602,7 @@ class UsersAdminApi { /// * [String] id (required): /// /// * [UserPreferencesUpdateDto] userPreferencesUpdateDto (required): - Future updateUserPreferencesAdminWithHttpInfo(String id, UserPreferencesUpdateDto userPreferencesUpdateDto,) async { + Future updateUserPreferencesAdminWithHttpInfo(String id, UserPreferencesUpdateDto userPreferencesUpdateDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/admin/users/{id}/preferences' .replaceAll('{id}', id); @@ -616,6 +625,7 @@ class UsersAdminApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -628,8 +638,8 @@ class UsersAdminApi { /// * [String] id (required): /// /// * [UserPreferencesUpdateDto] userPreferencesUpdateDto (required): - Future updateUserPreferencesAdmin(String id, UserPreferencesUpdateDto userPreferencesUpdateDto,) async { - final response = await updateUserPreferencesAdminWithHttpInfo(id, userPreferencesUpdateDto,); + Future updateUserPreferencesAdmin(String id, UserPreferencesUpdateDto userPreferencesUpdateDto, { Future? abortTrigger, }) async { + final response = await updateUserPreferencesAdminWithHttpInfo(id, userPreferencesUpdateDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api/users_api.dart b/mobile/openapi/lib/api/users_api.dart index 401cf4e94b..a7fac3ea66 100644 --- a/mobile/openapi/lib/api/users_api.dart +++ b/mobile/openapi/lib/api/users_api.dart @@ -26,7 +26,7 @@ class UsersApi { /// /// * [MultipartFile] file (required): /// Profile image file - Future createProfileImageWithHttpInfo(MultipartFile file,) async { + Future createProfileImageWithHttpInfo(MultipartFile file, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/users/profile-image'; @@ -58,6 +58,7 @@ class UsersApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -69,8 +70,8 @@ class UsersApi { /// /// * [MultipartFile] file (required): /// Profile image file - Future createProfileImage(MultipartFile file,) async { - final response = await createProfileImageWithHttpInfo(file,); + Future createProfileImage(MultipartFile file, { Future? abortTrigger, }) async { + final response = await createProfileImageWithHttpInfo(file, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -89,7 +90,7 @@ class UsersApi { /// Delete the profile image of the current user. /// /// Note: This method returns the HTTP [Response]. - Future deleteProfileImageWithHttpInfo() async { + Future deleteProfileImageWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/users/profile-image'; @@ -111,14 +112,15 @@ class UsersApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// Delete user profile image /// /// Delete the profile image of the current user. - Future deleteProfileImage() async { - final response = await deleteProfileImageWithHttpInfo(); + Future deleteProfileImage({ Future? abortTrigger, }) async { + final response = await deleteProfileImageWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -129,7 +131,7 @@ class UsersApi { /// Delete the registered product key for the current user. /// /// Note: This method returns the HTTP [Response]. - Future deleteUserLicenseWithHttpInfo() async { + Future deleteUserLicenseWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/users/me/license'; @@ -151,14 +153,15 @@ class UsersApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// Delete user product key /// /// Delete the registered product key for the current user. - Future deleteUserLicense() async { - final response = await deleteUserLicenseWithHttpInfo(); + Future deleteUserLicense({ Future? abortTrigger, }) async { + final response = await deleteUserLicenseWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -169,7 +172,7 @@ class UsersApi { /// Delete the onboarding status of the current user. /// /// Note: This method returns the HTTP [Response]. - Future deleteUserOnboardingWithHttpInfo() async { + Future deleteUserOnboardingWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/users/me/onboarding'; @@ -191,14 +194,15 @@ class UsersApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// Delete user onboarding /// /// Delete the onboarding status of the current user. - Future deleteUserOnboarding() async { - final response = await deleteUserOnboardingWithHttpInfo(); + Future deleteUserOnboarding({ Future? abortTrigger, }) async { + final response = await deleteUserOnboardingWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -209,7 +213,7 @@ class UsersApi { /// Retrieve the preferences for the current user. /// /// Note: This method returns the HTTP [Response]. - Future getMyPreferencesWithHttpInfo() async { + Future getMyPreferencesWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/users/me/preferences'; @@ -231,14 +235,15 @@ class UsersApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// Get my preferences /// /// Retrieve the preferences for the current user. - Future getMyPreferences() async { - final response = await getMyPreferencesWithHttpInfo(); + Future getMyPreferences({ Future? abortTrigger, }) async { + final response = await getMyPreferencesWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -257,7 +262,7 @@ class UsersApi { /// Retrieve information about the user making the API request. /// /// Note: This method returns the HTTP [Response]. - Future getMyUserWithHttpInfo() async { + Future getMyUserWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/users/me'; @@ -279,14 +284,15 @@ class UsersApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// Get current user /// /// Retrieve information about the user making the API request. - Future getMyUser() async { - final response = await getMyUserWithHttpInfo(); + Future getMyUser({ Future? abortTrigger, }) async { + final response = await getMyUserWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -309,7 +315,7 @@ class UsersApi { /// Parameters: /// /// * [String] id (required): - Future getProfileImageWithHttpInfo(String id,) async { + Future getProfileImageWithHttpInfo(String id, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/users/{id}/profile-image' .replaceAll('{id}', id); @@ -332,6 +338,7 @@ class UsersApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -342,8 +349,8 @@ class UsersApi { /// Parameters: /// /// * [String] id (required): - Future getProfileImage(String id,) async { - final response = await getProfileImageWithHttpInfo(id,); + Future getProfileImage(String id, { Future? abortTrigger, }) async { + final response = await getProfileImageWithHttpInfo(id, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -366,7 +373,7 @@ class UsersApi { /// Parameters: /// /// * [String] id (required): - Future getUserWithHttpInfo(String id,) async { + Future getUserWithHttpInfo(String id, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/users/{id}' .replaceAll('{id}', id); @@ -389,6 +396,7 @@ class UsersApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -399,8 +407,8 @@ class UsersApi { /// Parameters: /// /// * [String] id (required): - Future getUser(String id,) async { - final response = await getUserWithHttpInfo(id,); + Future getUser(String id, { Future? abortTrigger, }) async { + final response = await getUserWithHttpInfo(id, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -419,7 +427,7 @@ class UsersApi { /// Retrieve information about whether the current user has a registered product key. /// /// Note: This method returns the HTTP [Response]. - Future getUserLicenseWithHttpInfo() async { + Future getUserLicenseWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/users/me/license'; @@ -441,14 +449,15 @@ class UsersApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// Retrieve user product key /// /// Retrieve information about whether the current user has a registered product key. - Future getUserLicense() async { - final response = await getUserLicenseWithHttpInfo(); + Future getUserLicense({ Future? abortTrigger, }) async { + final response = await getUserLicenseWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -467,7 +476,7 @@ class UsersApi { /// Retrieve the onboarding status of the current user. /// /// Note: This method returns the HTTP [Response]. - Future getUserOnboardingWithHttpInfo() async { + Future getUserOnboardingWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/users/me/onboarding'; @@ -489,14 +498,15 @@ class UsersApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// Retrieve user onboarding /// /// Retrieve the onboarding status of the current user. - Future getUserOnboarding() async { - final response = await getUserOnboardingWithHttpInfo(); + Future getUserOnboarding({ Future? abortTrigger, }) async { + final response = await getUserOnboardingWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -515,7 +525,7 @@ class UsersApi { /// Retrieve a list of all users on the server. /// /// Note: This method returns the HTTP [Response]. - Future searchUsersWithHttpInfo() async { + Future searchUsersWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/users'; @@ -537,14 +547,15 @@ class UsersApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// Get all users /// /// Retrieve a list of all users on the server. - Future?> searchUsers() async { - final response = await searchUsersWithHttpInfo(); + Future?> searchUsers({ Future? abortTrigger, }) async { + final response = await searchUsersWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -570,7 +581,7 @@ class UsersApi { /// Parameters: /// /// * [LicenseKeyDto] licenseKeyDto (required): - Future setUserLicenseWithHttpInfo(LicenseKeyDto licenseKeyDto,) async { + Future setUserLicenseWithHttpInfo(LicenseKeyDto licenseKeyDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/users/me/license'; @@ -592,6 +603,7 @@ class UsersApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -602,8 +614,8 @@ class UsersApi { /// Parameters: /// /// * [LicenseKeyDto] licenseKeyDto (required): - Future setUserLicense(LicenseKeyDto licenseKeyDto,) async { - final response = await setUserLicenseWithHttpInfo(licenseKeyDto,); + Future setUserLicense(LicenseKeyDto licenseKeyDto, { Future? abortTrigger, }) async { + final response = await setUserLicenseWithHttpInfo(licenseKeyDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -626,7 +638,7 @@ class UsersApi { /// Parameters: /// /// * [OnboardingDto] onboardingDto (required): - Future setUserOnboardingWithHttpInfo(OnboardingDto onboardingDto,) async { + Future setUserOnboardingWithHttpInfo(OnboardingDto onboardingDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/users/me/onboarding'; @@ -648,6 +660,7 @@ class UsersApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -658,8 +671,8 @@ class UsersApi { /// Parameters: /// /// * [OnboardingDto] onboardingDto (required): - Future setUserOnboarding(OnboardingDto onboardingDto,) async { - final response = await setUserOnboardingWithHttpInfo(onboardingDto,); + Future setUserOnboarding(OnboardingDto onboardingDto, { Future? abortTrigger, }) async { + final response = await setUserOnboardingWithHttpInfo(onboardingDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -682,7 +695,7 @@ class UsersApi { /// Parameters: /// /// * [UserPreferencesUpdateDto] userPreferencesUpdateDto (required): - Future updateMyPreferencesWithHttpInfo(UserPreferencesUpdateDto userPreferencesUpdateDto,) async { + Future updateMyPreferencesWithHttpInfo(UserPreferencesUpdateDto userPreferencesUpdateDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/users/me/preferences'; @@ -704,6 +717,7 @@ class UsersApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -714,8 +728,8 @@ class UsersApi { /// Parameters: /// /// * [UserPreferencesUpdateDto] userPreferencesUpdateDto (required): - Future updateMyPreferences(UserPreferencesUpdateDto userPreferencesUpdateDto,) async { - final response = await updateMyPreferencesWithHttpInfo(userPreferencesUpdateDto,); + Future updateMyPreferences(UserPreferencesUpdateDto userPreferencesUpdateDto, { Future? abortTrigger, }) async { + final response = await updateMyPreferencesWithHttpInfo(userPreferencesUpdateDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -738,7 +752,7 @@ class UsersApi { /// Parameters: /// /// * [UserUpdateMeDto] userUpdateMeDto (required): - Future updateMyUserWithHttpInfo(UserUpdateMeDto userUpdateMeDto,) async { + Future updateMyUserWithHttpInfo(UserUpdateMeDto userUpdateMeDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/users/me'; @@ -760,6 +774,7 @@ class UsersApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -770,8 +785,8 @@ class UsersApi { /// Parameters: /// /// * [UserUpdateMeDto] userUpdateMeDto (required): - Future updateMyUser(UserUpdateMeDto userUpdateMeDto,) async { - final response = await updateMyUserWithHttpInfo(userUpdateMeDto,); + Future updateMyUser(UserUpdateMeDto userUpdateMeDto, { Future? abortTrigger, }) async { + final response = await updateMyUserWithHttpInfo(userUpdateMeDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api/views_api.dart b/mobile/openapi/lib/api/views_api.dart index a45e89d58f..3ccbacb650 100644 --- a/mobile/openapi/lib/api/views_api.dart +++ b/mobile/openapi/lib/api/views_api.dart @@ -25,7 +25,7 @@ class ViewsApi { /// Parameters: /// /// * [String] path (required): - Future getAssetsByOriginalPathWithHttpInfo(String path,) async { + Future getAssetsByOriginalPathWithHttpInfo(String path, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/view/folder'; @@ -49,6 +49,7 @@ class ViewsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -59,8 +60,8 @@ class ViewsApi { /// Parameters: /// /// * [String] path (required): - Future?> getAssetsByOriginalPath(String path,) async { - final response = await getAssetsByOriginalPathWithHttpInfo(path,); + Future?> getAssetsByOriginalPath(String path, { Future? abortTrigger, }) async { + final response = await getAssetsByOriginalPathWithHttpInfo(path, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -82,7 +83,7 @@ class ViewsApi { /// Retrieve a list of unique folder paths from asset original paths. /// /// Note: This method returns the HTTP [Response]. - Future getUniqueOriginalPathsWithHttpInfo() async { + Future getUniqueOriginalPathsWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/view/folder/unique-paths'; @@ -104,14 +105,15 @@ class ViewsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// Retrieve unique paths /// /// Retrieve a list of unique folder paths from asset original paths. - Future?> getUniqueOriginalPaths() async { - final response = await getUniqueOriginalPathsWithHttpInfo(); + Future?> getUniqueOriginalPaths({ Future? abortTrigger, }) async { + final response = await getUniqueOriginalPathsWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api/workflows_api.dart b/mobile/openapi/lib/api/workflows_api.dart index 12b33b7238..4b27acd624 100644 --- a/mobile/openapi/lib/api/workflows_api.dart +++ b/mobile/openapi/lib/api/workflows_api.dart @@ -25,7 +25,7 @@ class WorkflowsApi { /// Parameters: /// /// * [WorkflowCreateDto] workflowCreateDto (required): - Future createWorkflowWithHttpInfo(WorkflowCreateDto workflowCreateDto,) async { + Future createWorkflowWithHttpInfo(WorkflowCreateDto workflowCreateDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/workflows'; @@ -47,6 +47,7 @@ class WorkflowsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -57,8 +58,8 @@ class WorkflowsApi { /// Parameters: /// /// * [WorkflowCreateDto] workflowCreateDto (required): - Future createWorkflow(WorkflowCreateDto workflowCreateDto,) async { - final response = await createWorkflowWithHttpInfo(workflowCreateDto,); + Future createWorkflow(WorkflowCreateDto workflowCreateDto, { Future? abortTrigger, }) async { + final response = await createWorkflowWithHttpInfo(workflowCreateDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -81,7 +82,7 @@ class WorkflowsApi { /// Parameters: /// /// * [String] id (required): - Future deleteWorkflowWithHttpInfo(String id,) async { + Future deleteWorkflowWithHttpInfo(String id, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/workflows/{id}' .replaceAll('{id}', id); @@ -104,6 +105,7 @@ class WorkflowsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -114,8 +116,8 @@ class WorkflowsApi { /// Parameters: /// /// * [String] id (required): - Future deleteWorkflow(String id,) async { - final response = await deleteWorkflowWithHttpInfo(id,); + Future deleteWorkflow(String id, { Future? abortTrigger, }) async { + final response = await deleteWorkflowWithHttpInfo(id, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -130,7 +132,7 @@ class WorkflowsApi { /// Parameters: /// /// * [String] id (required): - Future getWorkflowWithHttpInfo(String id,) async { + Future getWorkflowWithHttpInfo(String id, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/workflows/{id}' .replaceAll('{id}', id); @@ -153,6 +155,7 @@ class WorkflowsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -163,8 +166,8 @@ class WorkflowsApi { /// Parameters: /// /// * [String] id (required): - Future getWorkflow(String id,) async { - final response = await getWorkflowWithHttpInfo(id,); + Future getWorkflow(String id, { Future? abortTrigger, }) async { + final response = await getWorkflowWithHttpInfo(id, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -187,7 +190,7 @@ class WorkflowsApi { /// Parameters: /// /// * [String] id (required): - Future getWorkflowForShareWithHttpInfo(String id,) async { + Future getWorkflowForShareWithHttpInfo(String id, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/workflows/{id}/share' .replaceAll('{id}', id); @@ -210,6 +213,7 @@ class WorkflowsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -220,8 +224,8 @@ class WorkflowsApi { /// Parameters: /// /// * [String] id (required): - Future getWorkflowForShare(String id,) async { - final response = await getWorkflowForShareWithHttpInfo(id,); + Future getWorkflowForShare(String id, { Future? abortTrigger, }) async { + final response = await getWorkflowForShareWithHttpInfo(id, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -240,7 +244,7 @@ class WorkflowsApi { /// Retrieve a list of all available workflow triggers. /// /// Note: This method returns the HTTP [Response]. - Future getWorkflowTriggersWithHttpInfo() async { + Future getWorkflowTriggersWithHttpInfo({ Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/workflows/triggers'; @@ -262,14 +266,15 @@ class WorkflowsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } /// List all workflow triggers /// /// Retrieve a list of all available workflow triggers. - Future?> getWorkflowTriggers() async { - final response = await getWorkflowTriggersWithHttpInfo(); + Future?> getWorkflowTriggers({ Future? abortTrigger, }) async { + final response = await getWorkflowTriggersWithHttpInfo(abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -308,7 +313,7 @@ class WorkflowsApi { /// /// * [WorkflowTrigger] trigger: /// Workflow trigger type - Future searchWorkflowsWithHttpInfo({ String? description, bool? enabled, String? id, String? name, WorkflowTrigger? trigger, }) async { + Future searchWorkflowsWithHttpInfo({ String? description, bool? enabled, String? id, String? name, WorkflowTrigger? trigger, Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/workflows'; @@ -346,6 +351,7 @@ class WorkflowsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -369,8 +375,8 @@ class WorkflowsApi { /// /// * [WorkflowTrigger] trigger: /// Workflow trigger type - Future?> searchWorkflows({ String? description, bool? enabled, String? id, String? name, WorkflowTrigger? trigger, }) async { - final response = await searchWorkflowsWithHttpInfo( description: description, enabled: enabled, id: id, name: name, trigger: trigger, ); + Future?> searchWorkflows({ String? description, bool? enabled, String? id, String? name, WorkflowTrigger? trigger, Future? abortTrigger, }) async { + final response = await searchWorkflowsWithHttpInfo(description: description, enabled: enabled, id: id, name: name, trigger: trigger, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -398,7 +404,7 @@ class WorkflowsApi { /// * [String] id (required): /// /// * [WorkflowUpdateDto] workflowUpdateDto (required): - Future updateWorkflowWithHttpInfo(String id, WorkflowUpdateDto workflowUpdateDto,) async { + Future updateWorkflowWithHttpInfo(String id, WorkflowUpdateDto workflowUpdateDto, { Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'/workflows/{id}' .replaceAll('{id}', id); @@ -421,6 +427,7 @@ class WorkflowsApi { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -433,8 +440,8 @@ class WorkflowsApi { /// * [String] id (required): /// /// * [WorkflowUpdateDto] workflowUpdateDto (required): - Future updateWorkflow(String id, WorkflowUpdateDto workflowUpdateDto,) async { - final response = await updateWorkflowWithHttpInfo(id, workflowUpdateDto,); + Future updateWorkflow(String id, WorkflowUpdateDto workflowUpdateDto, { Future? abortTrigger, }) async { + final response = await updateWorkflowWithHttpInfo(id, workflowUpdateDto, abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api_client.dart b/mobile/openapi/lib/api_client.dart index 58503e0ca1..f3638c81de 100644 --- a/mobile/openapi/lib/api_client.dart +++ b/mobile/openapi/lib/api_client.dart @@ -13,7 +13,7 @@ part of openapi.api; class ApiClient { ApiClient({this.basePath = '/api', this.authentication,}); - final String basePath; + String basePath; final Authentication? authentication; var _client = Client(); @@ -44,8 +44,9 @@ class ApiClient { Object? body, Map headerParams, Map formParams, - String? contentType, - ) async { + String? contentType, { + Future? abortTrigger, + }) async { await authentication?.applyToParams(queryParams, headerParams); headerParams.addAll(_defaultHeaderMap); @@ -63,7 +64,7 @@ class ApiClient { body is MultipartFile && (contentType == null || !contentType.toLowerCase().startsWith('multipart/form-data')) ) { - final request = StreamedRequest(method, uri); + final request = AbortableStreamedRequest(method, uri, abortTrigger: abortTrigger); request.headers.addAll(headerParams); request.contentLength = body.length; body.finalize().listen( @@ -78,7 +79,7 @@ class ApiClient { } if (body is MultipartRequest) { - final request = MultipartRequest(method, uri); + final request = AbortableMultipartRequest(method, uri, abortTrigger: abortTrigger); request.fields.addAll(body.fields); request.files.addAll(body.files); request.headers.addAll(body.headers); @@ -92,14 +93,19 @@ class ApiClient { : await serializeAsync(body); final nullableHeaderParams = headerParams.isEmpty ? null : headerParams; - switch(method) { - case 'POST': return await _client.post(uri, headers: nullableHeaderParams, body: msgBody,); - case 'PUT': return await _client.put(uri, headers: nullableHeaderParams, body: msgBody,); - case 'DELETE': return await _client.delete(uri, headers: nullableHeaderParams, body: msgBody,); - case 'PATCH': return await _client.patch(uri, headers: nullableHeaderParams, body: msgBody,); - case 'HEAD': return await _client.head(uri, headers: nullableHeaderParams,); - case 'GET': return await _client.get(uri, headers: nullableHeaderParams,); + final request = AbortableRequest(method, uri, abortTrigger: abortTrigger); + if (nullableHeaderParams != null) { + request.headers.addAll(nullableHeaderParams); } + if (msgBody is String) { + request.body = msgBody; + } else if (msgBody is List) { + request.bodyBytes = msgBody; + } else if (msgBody is Map) { + request.bodyFields = msgBody; + } + final response = await _client.send(request); + return Response.fromStream(response); } on SocketException catch (error, trace) { throw ApiException.withInner( HttpStatus.badRequest, @@ -136,11 +142,6 @@ class ApiClient { trace, ); } - - throw ApiException( - HttpStatus.badRequest, - 'Invalid HTTP operation: $method $path', - ); } Future deserializeAsync(String value, String targetType, {bool growable = false,}) => @@ -564,6 +565,12 @@ class ApiClient { return ReactionLevelTypeTransformer().decode(value); case 'ReactionType': return ReactionTypeTypeTransformer().decode(value); + case 'ReleaseChannel': + return ReleaseChannelTypeTransformer().decode(value); + case 'ReleaseEventV1': + return ReleaseEventV1.fromJson(value); + case 'ReleaseType': + return ReleaseTypeTypeTransformer().decode(value); case 'ReverseGeocodingStateResponseDto': return ReverseGeocodingStateResponseDto.fromJson(value); case 'RotateParameters': @@ -732,6 +739,8 @@ class ApiClient { return SystemConfigDto.fromJson(value); case 'SystemConfigFFmpegDto': return SystemConfigFFmpegDto.fromJson(value); + case 'SystemConfigFFmpegRealtimeDto': + return SystemConfigFFmpegRealtimeDto.fromJson(value); case 'SystemConfigFacesDto': return SystemConfigFacesDto.fromJson(value); case 'SystemConfigGeneratedFullsizeImageDto': diff --git a/mobile/openapi/lib/api_helper.dart b/mobile/openapi/lib/api_helper.dart index b5d348edd6..6cf11022c3 100644 --- a/mobile/openapi/lib/api_helper.dart +++ b/mobile/openapi/lib/api_helper.dart @@ -157,6 +157,12 @@ String parameterToString(dynamic value) { if (value is ReactionType) { return ReactionTypeTypeTransformer().encode(value).toString(); } + if (value is ReleaseChannel) { + return ReleaseChannelTypeTransformer().encode(value).toString(); + } + if (value is ReleaseType) { + return ReleaseTypeTypeTransformer().encode(value).toString(); + } if (value is SearchSuggestionType) { return SearchSuggestionTypeTypeTransformer().encode(value).toString(); } diff --git a/mobile/openapi/lib/model/job_name.dart b/mobile/openapi/lib/model/job_name.dart index 444b080c12..435cffd623 100644 --- a/mobile/openapi/lib/model/job_name.dart +++ b/mobile/openapi/lib/model/job_name.dart @@ -52,6 +52,7 @@ class JobName { static const librarySyncFilesQueueAll = JobName._(r'LibrarySyncFilesQueueAll'); static const librarySyncFiles = JobName._(r'LibrarySyncFiles'); static const libraryScanQueueAll = JobName._(r'LibraryScanQueueAll'); + static const hlsSessionCleanup = JobName._(r'HlsSessionCleanup'); static const memoryCleanup = JobName._(r'MemoryCleanup'); static const memoryGenerate = JobName._(r'MemoryGenerate'); static const notificationsCleanup = JobName._(r'NotificationsCleanup'); @@ -77,7 +78,7 @@ class JobName { static const versionCheck = JobName._(r'VersionCheck'); static const ocrQueueAll = JobName._(r'OcrQueueAll'); static const ocr = JobName._(r'Ocr'); - static const workflowAssetCreate = JobName._(r'WorkflowAssetCreate'); + static const workflowAssetTrigger = JobName._(r'WorkflowAssetTrigger'); /// List of all possible values in this [enum][JobName]. static const values = [ @@ -110,6 +111,7 @@ class JobName { librarySyncFilesQueueAll, librarySyncFiles, libraryScanQueueAll, + hlsSessionCleanup, memoryCleanup, memoryGenerate, notificationsCleanup, @@ -135,7 +137,7 @@ class JobName { versionCheck, ocrQueueAll, ocr, - workflowAssetCreate, + workflowAssetTrigger, ]; static JobName? fromJson(dynamic value) => JobNameTypeTransformer().decode(value); @@ -203,6 +205,7 @@ class JobNameTypeTransformer { case r'LibrarySyncFilesQueueAll': return JobName.librarySyncFilesQueueAll; case r'LibrarySyncFiles': return JobName.librarySyncFiles; case r'LibraryScanQueueAll': return JobName.libraryScanQueueAll; + case r'HlsSessionCleanup': return JobName.hlsSessionCleanup; case r'MemoryCleanup': return JobName.memoryCleanup; case r'MemoryGenerate': return JobName.memoryGenerate; case r'NotificationsCleanup': return JobName.notificationsCleanup; @@ -228,7 +231,7 @@ class JobNameTypeTransformer { case r'VersionCheck': return JobName.versionCheck; case r'OcrQueueAll': return JobName.ocrQueueAll; case r'Ocr': return JobName.ocr; - case r'WorkflowAssetCreate': return JobName.workflowAssetCreate; + case r'WorkflowAssetTrigger': return JobName.workflowAssetTrigger; default: if (!allowNull) { throw ArgumentError('Unknown enum value to decode: $data'); diff --git a/mobile/openapi/lib/model/people_response.dart b/mobile/openapi/lib/model/people_response.dart index 9d5d8ec18a..ba7128d932 100644 --- a/mobile/openapi/lib/model/people_response.dart +++ b/mobile/openapi/lib/model/people_response.dart @@ -14,32 +14,52 @@ class PeopleResponse { /// Returns a new [PeopleResponse] instance. PeopleResponse({ required this.enabled, + this.minimumFaces, required this.sidebarWeb, }); /// Whether people are enabled bool enabled; + /// People face threshold + /// + /// Minimum value: 1 + /// Maximum value: 9007199254740991 + /// + /// 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. + /// + int? minimumFaces; + /// Whether people appear in web sidebar bool sidebarWeb; @override bool operator ==(Object other) => identical(this, other) || other is PeopleResponse && other.enabled == enabled && + other.minimumFaces == minimumFaces && other.sidebarWeb == sidebarWeb; @override int get hashCode => // ignore: unnecessary_parenthesis (enabled.hashCode) + + (minimumFaces == null ? 0 : minimumFaces!.hashCode) + (sidebarWeb.hashCode); @override - String toString() => 'PeopleResponse[enabled=$enabled, sidebarWeb=$sidebarWeb]'; + String toString() => 'PeopleResponse[enabled=$enabled, minimumFaces=$minimumFaces, sidebarWeb=$sidebarWeb]'; Map toJson() { final json = {}; json[r'enabled'] = this.enabled; + if (this.minimumFaces != null) { + json[r'minimumFaces'] = this.minimumFaces; + } else { + // json[r'minimumFaces'] = null; + } json[r'sidebarWeb'] = this.sidebarWeb; return json; } @@ -54,6 +74,7 @@ class PeopleResponse { return PeopleResponse( enabled: mapValueOfType(json, r'enabled')!, + minimumFaces: mapValueOfType(json, r'minimumFaces'), sidebarWeb: mapValueOfType(json, r'sidebarWeb')!, ); } diff --git a/mobile/openapi/lib/model/people_update.dart b/mobile/openapi/lib/model/people_update.dart index fe16479bac..05459f19f3 100644 --- a/mobile/openapi/lib/model/people_update.dart +++ b/mobile/openapi/lib/model/people_update.dart @@ -14,6 +14,7 @@ class PeopleUpdate { /// Returns a new [PeopleUpdate] instance. PeopleUpdate({ this.enabled, + this.minimumFaces, this.sidebarWeb, }); @@ -26,6 +27,18 @@ class PeopleUpdate { /// bool? enabled; + /// People face threshold + /// + /// Minimum value: 1 + /// Maximum value: 9007199254740991 + /// + /// 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. + /// + int? minimumFaces; + /// Whether people appear in web sidebar /// /// Please note: This property should have been non-nullable! Since the specification file @@ -38,16 +51,18 @@ class PeopleUpdate { @override bool operator ==(Object other) => identical(this, other) || other is PeopleUpdate && other.enabled == enabled && + other.minimumFaces == minimumFaces && other.sidebarWeb == sidebarWeb; @override int get hashCode => // ignore: unnecessary_parenthesis (enabled == null ? 0 : enabled!.hashCode) + + (minimumFaces == null ? 0 : minimumFaces!.hashCode) + (sidebarWeb == null ? 0 : sidebarWeb!.hashCode); @override - String toString() => 'PeopleUpdate[enabled=$enabled, sidebarWeb=$sidebarWeb]'; + String toString() => 'PeopleUpdate[enabled=$enabled, minimumFaces=$minimumFaces, sidebarWeb=$sidebarWeb]'; Map toJson() { final json = {}; @@ -56,6 +71,11 @@ class PeopleUpdate { } else { // json[r'enabled'] = null; } + if (this.minimumFaces != null) { + json[r'minimumFaces'] = this.minimumFaces; + } else { + // json[r'minimumFaces'] = null; + } if (this.sidebarWeb != null) { json[r'sidebarWeb'] = this.sidebarWeb; } else { @@ -74,6 +94,7 @@ class PeopleUpdate { return PeopleUpdate( enabled: mapValueOfType(json, r'enabled'), + minimumFaces: mapValueOfType(json, r'minimumFaces'), sidebarWeb: mapValueOfType(json, r'sidebarWeb'), ); } diff --git a/mobile/openapi/lib/model/plugin_template_response_dto.dart b/mobile/openapi/lib/model/plugin_template_response_dto.dart index 4625da37d3..9f54753f49 100644 --- a/mobile/openapi/lib/model/plugin_template_response_dto.dart +++ b/mobile/openapi/lib/model/plugin_template_response_dto.dart @@ -18,6 +18,7 @@ class PluginTemplateResponseDto { this.steps = const [], required this.title, required this.trigger, + this.uiHints = const [], }); /// Template description @@ -34,13 +35,17 @@ class PluginTemplateResponseDto { WorkflowTrigger trigger; + /// Ui hints, for example \"smart-album\" + List uiHints; + @override bool operator ==(Object other) => identical(this, other) || other is PluginTemplateResponseDto && other.description == description && other.key == key && _deepEquality.equals(other.steps, steps) && other.title == title && - other.trigger == trigger; + other.trigger == trigger && + _deepEquality.equals(other.uiHints, uiHints); @override int get hashCode => @@ -49,10 +54,11 @@ class PluginTemplateResponseDto { (key.hashCode) + (steps.hashCode) + (title.hashCode) + - (trigger.hashCode); + (trigger.hashCode) + + (uiHints.hashCode); @override - String toString() => 'PluginTemplateResponseDto[description=$description, key=$key, steps=$steps, title=$title, trigger=$trigger]'; + String toString() => 'PluginTemplateResponseDto[description=$description, key=$key, steps=$steps, title=$title, trigger=$trigger, uiHints=$uiHints]'; Map toJson() { final json = {}; @@ -61,6 +67,7 @@ class PluginTemplateResponseDto { json[r'steps'] = this.steps; json[r'title'] = this.title; json[r'trigger'] = this.trigger; + json[r'uiHints'] = this.uiHints; return json; } @@ -78,6 +85,9 @@ class PluginTemplateResponseDto { steps: PluginTemplateStepResponseDto.listFromJson(json[r'steps']), title: mapValueOfType(json, r'title')!, trigger: WorkflowTrigger.fromJson(json[r'trigger'])!, + uiHints: json[r'uiHints'] is Iterable + ? (json[r'uiHints'] as Iterable).cast().toList(growable: false) + : const [], ); } return null; @@ -130,6 +140,7 @@ class PluginTemplateResponseDto { 'steps', 'title', 'trigger', + 'uiHints', }; } diff --git a/mobile/openapi/lib/model/release_channel.dart b/mobile/openapi/lib/model/release_channel.dart new file mode 100644 index 0000000000..48b082af07 --- /dev/null +++ b/mobile/openapi/lib/model/release_channel.dart @@ -0,0 +1,85 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.18 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + +/// Release channel +class ReleaseChannel { + /// Instantiate a new enum with the provided [value]. + const ReleaseChannel._(this.value); + + /// The underlying value of this enum member. + final String value; + + @override + String toString() => value; + + String toJson() => value; + + static const stable = ReleaseChannel._(r'stable'); + static const releaseCandidate = ReleaseChannel._(r'releaseCandidate'); + + /// List of all possible values in this [enum][ReleaseChannel]. + static const values = [ + stable, + releaseCandidate, + ]; + + static ReleaseChannel? fromJson(dynamic value) => ReleaseChannelTypeTransformer().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 = ReleaseChannel.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } +} + +/// Transformation class that can [encode] an instance of [ReleaseChannel] to String, +/// and [decode] dynamic data back to [ReleaseChannel]. +class ReleaseChannelTypeTransformer { + factory ReleaseChannelTypeTransformer() => _instance ??= const ReleaseChannelTypeTransformer._(); + + const ReleaseChannelTypeTransformer._(); + + String encode(ReleaseChannel data) => data.value; + + /// Decodes a [dynamic value][data] to a ReleaseChannel. + /// + /// 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. + ReleaseChannel? decode(dynamic data, {bool allowNull = true}) { + if (data != null) { + switch (data) { + case r'stable': return ReleaseChannel.stable; + case r'releaseCandidate': return ReleaseChannel.releaseCandidate; + default: + if (!allowNull) { + throw ArgumentError('Unknown enum value to decode: $data'); + } + } + } + return null; + } + + /// Singleton [ReleaseChannelTypeTransformer] instance. + static ReleaseChannelTypeTransformer? _instance; +} + diff --git a/mobile/openapi/lib/model/release_event_v1.dart b/mobile/openapi/lib/model/release_event_v1.dart new file mode 100644 index 0000000000..f26ae3e96e --- /dev/null +++ b/mobile/openapi/lib/model/release_event_v1.dart @@ -0,0 +1,133 @@ +// +// 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 ReleaseEventV1 { + /// Returns a new [ReleaseEventV1] instance. + ReleaseEventV1({ + required this.checkedAt, + required this.isAvailable, + required this.releaseVersion, + required this.serverVersion, + required this.type, + }); + + /// When the server last checked for a latest version. As an ISO timestamp + String checkedAt; + + /// Whether a new version is available + bool isAvailable; + + ServerVersionResponseDto releaseVersion; + + ServerVersionResponseDto serverVersion; + + ReleaseType type; + + @override + bool operator ==(Object other) => identical(this, other) || other is ReleaseEventV1 && + other.checkedAt == checkedAt && + other.isAvailable == isAvailable && + other.releaseVersion == releaseVersion && + other.serverVersion == serverVersion && + other.type == type; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (checkedAt.hashCode) + + (isAvailable.hashCode) + + (releaseVersion.hashCode) + + (serverVersion.hashCode) + + (type.hashCode); + + @override + String toString() => 'ReleaseEventV1[checkedAt=$checkedAt, isAvailable=$isAvailable, releaseVersion=$releaseVersion, serverVersion=$serverVersion, type=$type]'; + + Map toJson() { + final json = {}; + json[r'checkedAt'] = this.checkedAt; + json[r'isAvailable'] = this.isAvailable; + json[r'releaseVersion'] = this.releaseVersion; + json[r'serverVersion'] = this.serverVersion; + json[r'type'] = this.type; + return json; + } + + /// Returns a new [ReleaseEventV1] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static ReleaseEventV1? fromJson(dynamic value) { + upgradeDto(value, "ReleaseEventV1"); + if (value is Map) { + final json = value.cast(); + + return ReleaseEventV1( + checkedAt: mapValueOfType(json, r'checkedAt')!, + isAvailable: mapValueOfType(json, r'isAvailable')!, + releaseVersion: ServerVersionResponseDto.fromJson(json[r'releaseVersion'])!, + serverVersion: ServerVersionResponseDto.fromJson(json[r'serverVersion'])!, + type: ReleaseType.fromJson(json[r'type'])!, + ); + } + 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 = ReleaseEventV1.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 = ReleaseEventV1.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of ReleaseEventV1-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] = ReleaseEventV1.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'checkedAt', + 'isAvailable', + 'releaseVersion', + 'serverVersion', + 'type', + }; +} + diff --git a/mobile/openapi/lib/model/release_type.dart b/mobile/openapi/lib/model/release_type.dart new file mode 100644 index 0000000000..2d61072286 --- /dev/null +++ b/mobile/openapi/lib/model/release_type.dart @@ -0,0 +1,100 @@ +// +// 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 ReleaseType { + /// Instantiate a new enum with the provided [value]. + const ReleaseType._(this.value); + + /// The underlying value of this enum member. + final String value; + + @override + String toString() => value; + + String toJson() => value; + + static const major = ReleaseType._(r'major'); + static const premajor = ReleaseType._(r'premajor'); + static const minor = ReleaseType._(r'minor'); + static const preminor = ReleaseType._(r'preminor'); + static const patch_ = ReleaseType._(r'patch'); + static const prepatch = ReleaseType._(r'prepatch'); + static const prerelease = ReleaseType._(r'prerelease'); + + /// List of all possible values in this [enum][ReleaseType]. + static const values = [ + major, + premajor, + minor, + preminor, + patch_, + prepatch, + prerelease, + ]; + + static ReleaseType? fromJson(dynamic value) => ReleaseTypeTypeTransformer().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 = ReleaseType.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } +} + +/// Transformation class that can [encode] an instance of [ReleaseType] to String, +/// and [decode] dynamic data back to [ReleaseType]. +class ReleaseTypeTypeTransformer { + factory ReleaseTypeTypeTransformer() => _instance ??= const ReleaseTypeTypeTransformer._(); + + const ReleaseTypeTypeTransformer._(); + + String encode(ReleaseType data) => data.value; + + /// Decodes a [dynamic value][data] to a ReleaseType. + /// + /// 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. + ReleaseType? decode(dynamic data, {bool allowNull = true}) { + if (data != null) { + switch (data) { + case r'major': return ReleaseType.major; + case r'premajor': return ReleaseType.premajor; + case r'minor': return ReleaseType.minor; + case r'preminor': return ReleaseType.preminor; + case r'patch': return ReleaseType.patch_; + case r'prepatch': return ReleaseType.prepatch; + case r'prerelease': return ReleaseType.prerelease; + default: + if (!allowNull) { + throw ArgumentError('Unknown enum value to decode: $data'); + } + } + } + return null; + } + + /// Singleton [ReleaseTypeTypeTransformer] instance. + static ReleaseTypeTypeTransformer? _instance; +} + diff --git a/mobile/openapi/lib/model/server_config_dto.dart b/mobile/openapi/lib/model/server_config_dto.dart index 316edb609f..0eaaec7c7f 100644 --- a/mobile/openapi/lib/model/server_config_dto.dart +++ b/mobile/openapi/lib/model/server_config_dto.dart @@ -20,6 +20,7 @@ class ServerConfigDto { required this.maintenanceMode, required this.mapDarkStyleUrl, required this.mapLightStyleUrl, + required this.minFaces, required this.oauthButtonText, required this.publicUsers, required this.trashDays, @@ -47,6 +48,12 @@ class ServerConfigDto { /// Map light style URL String mapLightStyleUrl; + /// People min faces server default + /// + /// Minimum value: -9007199254740991 + /// Maximum value: 9007199254740991 + int minFaces; + /// OAuth button text String oauthButtonText; @@ -74,6 +81,7 @@ class ServerConfigDto { other.maintenanceMode == maintenanceMode && other.mapDarkStyleUrl == mapDarkStyleUrl && other.mapLightStyleUrl == mapLightStyleUrl && + other.minFaces == minFaces && other.oauthButtonText == oauthButtonText && other.publicUsers == publicUsers && other.trashDays == trashDays && @@ -89,13 +97,14 @@ class ServerConfigDto { (maintenanceMode.hashCode) + (mapDarkStyleUrl.hashCode) + (mapLightStyleUrl.hashCode) + + (minFaces.hashCode) + (oauthButtonText.hashCode) + (publicUsers.hashCode) + (trashDays.hashCode) + (userDeleteDelay.hashCode); @override - String toString() => 'ServerConfigDto[externalDomain=$externalDomain, isInitialized=$isInitialized, isOnboarded=$isOnboarded, loginPageMessage=$loginPageMessage, maintenanceMode=$maintenanceMode, mapDarkStyleUrl=$mapDarkStyleUrl, mapLightStyleUrl=$mapLightStyleUrl, oauthButtonText=$oauthButtonText, publicUsers=$publicUsers, trashDays=$trashDays, userDeleteDelay=$userDeleteDelay]'; + String toString() => 'ServerConfigDto[externalDomain=$externalDomain, isInitialized=$isInitialized, isOnboarded=$isOnboarded, loginPageMessage=$loginPageMessage, maintenanceMode=$maintenanceMode, mapDarkStyleUrl=$mapDarkStyleUrl, mapLightStyleUrl=$mapLightStyleUrl, minFaces=$minFaces, oauthButtonText=$oauthButtonText, publicUsers=$publicUsers, trashDays=$trashDays, userDeleteDelay=$userDeleteDelay]'; Map toJson() { final json = {}; @@ -106,6 +115,7 @@ class ServerConfigDto { json[r'maintenanceMode'] = this.maintenanceMode; json[r'mapDarkStyleUrl'] = this.mapDarkStyleUrl; json[r'mapLightStyleUrl'] = this.mapLightStyleUrl; + json[r'minFaces'] = this.minFaces; json[r'oauthButtonText'] = this.oauthButtonText; json[r'publicUsers'] = this.publicUsers; json[r'trashDays'] = this.trashDays; @@ -129,6 +139,7 @@ class ServerConfigDto { maintenanceMode: mapValueOfType(json, r'maintenanceMode')!, mapDarkStyleUrl: mapValueOfType(json, r'mapDarkStyleUrl')!, mapLightStyleUrl: mapValueOfType(json, r'mapLightStyleUrl')!, + minFaces: mapValueOfType(json, r'minFaces')!, oauthButtonText: mapValueOfType(json, r'oauthButtonText')!, publicUsers: mapValueOfType(json, r'publicUsers')!, trashDays: mapValueOfType(json, r'trashDays')!, @@ -187,6 +198,7 @@ class ServerConfigDto { 'maintenanceMode', 'mapDarkStyleUrl', 'mapLightStyleUrl', + 'minFaces', 'oauthButtonText', 'publicUsers', 'trashDays', diff --git a/mobile/openapi/lib/model/server_features_dto.dart b/mobile/openapi/lib/model/server_features_dto.dart index 79494b74eb..9b75ef2b32 100644 --- a/mobile/openapi/lib/model/server_features_dto.dart +++ b/mobile/openapi/lib/model/server_features_dto.dart @@ -23,6 +23,7 @@ class ServerFeaturesDto { required this.oauthAutoLaunch, required this.ocr, required this.passwordLogin, + required this.realtimeTranscoding, required this.reverseGeocoding, required this.search, required this.sidecar, @@ -60,6 +61,9 @@ class ServerFeaturesDto { /// Whether password login is enabled bool passwordLogin; + /// Whether real-time transcoding is enabled + bool realtimeTranscoding; + /// Whether reverse geocoding is enabled bool reverseGeocoding; @@ -87,6 +91,7 @@ class ServerFeaturesDto { other.oauthAutoLaunch == oauthAutoLaunch && other.ocr == ocr && other.passwordLogin == passwordLogin && + other.realtimeTranscoding == realtimeTranscoding && other.reverseGeocoding == reverseGeocoding && other.search == search && other.sidecar == sidecar && @@ -106,6 +111,7 @@ class ServerFeaturesDto { (oauthAutoLaunch.hashCode) + (ocr.hashCode) + (passwordLogin.hashCode) + + (realtimeTranscoding.hashCode) + (reverseGeocoding.hashCode) + (search.hashCode) + (sidecar.hashCode) + @@ -113,7 +119,7 @@ class ServerFeaturesDto { (trash.hashCode); @override - String toString() => 'ServerFeaturesDto[configFile=$configFile, duplicateDetection=$duplicateDetection, email=$email, facialRecognition=$facialRecognition, importFaces=$importFaces, map=$map, oauth=$oauth, oauthAutoLaunch=$oauthAutoLaunch, ocr=$ocr, passwordLogin=$passwordLogin, reverseGeocoding=$reverseGeocoding, search=$search, sidecar=$sidecar, smartSearch=$smartSearch, trash=$trash]'; + String toString() => 'ServerFeaturesDto[configFile=$configFile, duplicateDetection=$duplicateDetection, email=$email, facialRecognition=$facialRecognition, importFaces=$importFaces, map=$map, oauth=$oauth, oauthAutoLaunch=$oauthAutoLaunch, ocr=$ocr, passwordLogin=$passwordLogin, realtimeTranscoding=$realtimeTranscoding, reverseGeocoding=$reverseGeocoding, search=$search, sidecar=$sidecar, smartSearch=$smartSearch, trash=$trash]'; Map toJson() { final json = {}; @@ -127,6 +133,7 @@ class ServerFeaturesDto { json[r'oauthAutoLaunch'] = this.oauthAutoLaunch; json[r'ocr'] = this.ocr; json[r'passwordLogin'] = this.passwordLogin; + json[r'realtimeTranscoding'] = this.realtimeTranscoding; json[r'reverseGeocoding'] = this.reverseGeocoding; json[r'search'] = this.search; json[r'sidecar'] = this.sidecar; @@ -154,6 +161,7 @@ class ServerFeaturesDto { oauthAutoLaunch: mapValueOfType(json, r'oauthAutoLaunch')!, ocr: mapValueOfType(json, r'ocr')!, passwordLogin: mapValueOfType(json, r'passwordLogin')!, + realtimeTranscoding: mapValueOfType(json, r'realtimeTranscoding')!, reverseGeocoding: mapValueOfType(json, r'reverseGeocoding')!, search: mapValueOfType(json, r'search')!, sidecar: mapValueOfType(json, r'sidecar')!, @@ -216,6 +224,7 @@ class ServerFeaturesDto { 'oauthAutoLaunch', 'ocr', 'passwordLogin', + 'realtimeTranscoding', 'reverseGeocoding', 'search', 'sidecar', diff --git a/mobile/openapi/lib/model/server_version_response_dto.dart b/mobile/openapi/lib/model/server_version_response_dto.dart index 60161a7458..eae574f335 100644 --- a/mobile/openapi/lib/model/server_version_response_dto.dart +++ b/mobile/openapi/lib/model/server_version_response_dto.dart @@ -16,47 +16,61 @@ class ServerVersionResponseDto { required this.major, required this.minor, required this.patch_, + required this.prerelease, }); /// Major version number /// - /// Minimum value: -9007199254740991 + /// Minimum value: 0 /// Maximum value: 9007199254740991 int major; /// Minor version number /// - /// Minimum value: -9007199254740991 + /// Minimum value: 0 /// Maximum value: 9007199254740991 int minor; /// Patch version number /// - /// Minimum value: -9007199254740991 + /// Minimum value: 0 /// Maximum value: 9007199254740991 int patch_; + /// Pre-release version number + /// + /// Minimum value: 0 + /// Maximum value: 9007199254740991 + int? prerelease; + @override bool operator ==(Object other) => identical(this, other) || other is ServerVersionResponseDto && other.major == major && other.minor == minor && - other.patch_ == patch_; + other.patch_ == patch_ && + other.prerelease == prerelease; @override int get hashCode => // ignore: unnecessary_parenthesis (major.hashCode) + (minor.hashCode) + - (patch_.hashCode); + (patch_.hashCode) + + (prerelease == null ? 0 : prerelease!.hashCode); @override - String toString() => 'ServerVersionResponseDto[major=$major, minor=$minor, patch_=$patch_]'; + String toString() => 'ServerVersionResponseDto[major=$major, minor=$minor, patch_=$patch_, prerelease=$prerelease]'; Map toJson() { final json = {}; json[r'major'] = this.major; json[r'minor'] = this.minor; json[r'patch'] = this.patch_; + if (this.prerelease != null) { + json[r'prerelease'] = this.prerelease; + } else { + // json[r'prerelease'] = null; + } return json; } @@ -72,6 +86,7 @@ class ServerVersionResponseDto { major: mapValueOfType(json, r'major')!, minor: mapValueOfType(json, r'minor')!, patch_: mapValueOfType(json, r'patch')!, + prerelease: mapValueOfType(json, r'prerelease'), ); } return null; @@ -122,6 +137,7 @@ class ServerVersionResponseDto { 'major', 'minor', 'patch', + 'prerelease', }; } diff --git a/mobile/openapi/lib/model/system_config_f_fmpeg_dto.dart b/mobile/openapi/lib/model/system_config_f_fmpeg_dto.dart index ecf2e5da4a..79da8da97f 100644 --- a/mobile/openapi/lib/model/system_config_f_fmpeg_dto.dart +++ b/mobile/openapi/lib/model/system_config_f_fmpeg_dto.dart @@ -25,6 +25,7 @@ class SystemConfigFFmpegDto { required this.maxBitrate, required this.preferredHwDevice, required this.preset, + required this.realtime, required this.refs, required this.targetAudioCodec, required this.targetResolution, @@ -79,6 +80,8 @@ class SystemConfigFFmpegDto { /// Preset String preset; + SystemConfigFFmpegRealtimeDto realtime; + /// References /// /// Minimum value: 0 @@ -122,6 +125,7 @@ class SystemConfigFFmpegDto { other.maxBitrate == maxBitrate && other.preferredHwDevice == preferredHwDevice && other.preset == preset && + other.realtime == realtime && other.refs == refs && other.targetAudioCodec == targetAudioCodec && other.targetResolution == targetResolution && @@ -147,6 +151,7 @@ class SystemConfigFFmpegDto { (maxBitrate.hashCode) + (preferredHwDevice.hashCode) + (preset.hashCode) + + (realtime.hashCode) + (refs.hashCode) + (targetAudioCodec.hashCode) + (targetResolution.hashCode) + @@ -158,7 +163,7 @@ class SystemConfigFFmpegDto { (twoPass.hashCode); @override - String toString() => 'SystemConfigFFmpegDto[accel=$accel, accelDecode=$accelDecode, acceptedAudioCodecs=$acceptedAudioCodecs, acceptedContainers=$acceptedContainers, acceptedVideoCodecs=$acceptedVideoCodecs, bframes=$bframes, cqMode=$cqMode, crf=$crf, gopSize=$gopSize, maxBitrate=$maxBitrate, preferredHwDevice=$preferredHwDevice, preset=$preset, refs=$refs, targetAudioCodec=$targetAudioCodec, targetResolution=$targetResolution, targetVideoCodec=$targetVideoCodec, temporalAQ=$temporalAQ, threads=$threads, tonemap=$tonemap, transcode=$transcode, twoPass=$twoPass]'; + String toString() => 'SystemConfigFFmpegDto[accel=$accel, accelDecode=$accelDecode, acceptedAudioCodecs=$acceptedAudioCodecs, acceptedContainers=$acceptedContainers, acceptedVideoCodecs=$acceptedVideoCodecs, bframes=$bframes, cqMode=$cqMode, crf=$crf, gopSize=$gopSize, maxBitrate=$maxBitrate, preferredHwDevice=$preferredHwDevice, preset=$preset, realtime=$realtime, refs=$refs, targetAudioCodec=$targetAudioCodec, targetResolution=$targetResolution, targetVideoCodec=$targetVideoCodec, temporalAQ=$temporalAQ, threads=$threads, tonemap=$tonemap, transcode=$transcode, twoPass=$twoPass]'; Map toJson() { final json = {}; @@ -174,6 +179,7 @@ class SystemConfigFFmpegDto { json[r'maxBitrate'] = this.maxBitrate; json[r'preferredHwDevice'] = this.preferredHwDevice; json[r'preset'] = this.preset; + json[r'realtime'] = this.realtime; json[r'refs'] = this.refs; json[r'targetAudioCodec'] = this.targetAudioCodec; json[r'targetResolution'] = this.targetResolution; @@ -207,6 +213,7 @@ class SystemConfigFFmpegDto { maxBitrate: mapValueOfType(json, r'maxBitrate')!, preferredHwDevice: mapValueOfType(json, r'preferredHwDevice')!, preset: mapValueOfType(json, r'preset')!, + realtime: SystemConfigFFmpegRealtimeDto.fromJson(json[r'realtime'])!, refs: mapValueOfType(json, r'refs')!, targetAudioCodec: AudioCodec.fromJson(json[r'targetAudioCodec'])!, targetResolution: mapValueOfType(json, r'targetResolution')!, @@ -275,6 +282,7 @@ class SystemConfigFFmpegDto { 'maxBitrate', 'preferredHwDevice', 'preset', + 'realtime', 'refs', 'targetAudioCodec', 'targetResolution', diff --git a/mobile/openapi/lib/model/system_config_f_fmpeg_realtime_dto.dart b/mobile/openapi/lib/model/system_config_f_fmpeg_realtime_dto.dart new file mode 100644 index 0000000000..1a8669912e --- /dev/null +++ b/mobile/openapi/lib/model/system_config_f_fmpeg_realtime_dto.dart @@ -0,0 +1,100 @@ +// +// 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 SystemConfigFFmpegRealtimeDto { + /// Returns a new [SystemConfigFFmpegRealtimeDto] instance. + SystemConfigFFmpegRealtimeDto({ + required this.enabled, + }); + + /// Enable real-time HLS transcoding (alpha) + bool enabled; + + @override + bool operator ==(Object other) => identical(this, other) || other is SystemConfigFFmpegRealtimeDto && + other.enabled == enabled; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (enabled.hashCode); + + @override + String toString() => 'SystemConfigFFmpegRealtimeDto[enabled=$enabled]'; + + Map toJson() { + final json = {}; + json[r'enabled'] = this.enabled; + return json; + } + + /// Returns a new [SystemConfigFFmpegRealtimeDto] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static SystemConfigFFmpegRealtimeDto? fromJson(dynamic value) { + upgradeDto(value, "SystemConfigFFmpegRealtimeDto"); + if (value is Map) { + final json = value.cast(); + + return SystemConfigFFmpegRealtimeDto( + enabled: mapValueOfType(json, r'enabled')!, + ); + } + 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 = SystemConfigFFmpegRealtimeDto.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 = SystemConfigFFmpegRealtimeDto.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of SystemConfigFFmpegRealtimeDto-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] = SystemConfigFFmpegRealtimeDto.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'enabled', + }; +} + diff --git a/mobile/openapi/lib/model/system_config_new_version_check_dto.dart b/mobile/openapi/lib/model/system_config_new_version_check_dto.dart index ec2b400dfd..17ae9577e8 100644 --- a/mobile/openapi/lib/model/system_config_new_version_check_dto.dart +++ b/mobile/openapi/lib/model/system_config_new_version_check_dto.dart @@ -13,26 +13,32 @@ part of openapi.api; class SystemConfigNewVersionCheckDto { /// Returns a new [SystemConfigNewVersionCheckDto] instance. SystemConfigNewVersionCheckDto({ + required this.channel, required this.enabled, }); + ReleaseChannel channel; + /// Enabled bool enabled; @override bool operator ==(Object other) => identical(this, other) || other is SystemConfigNewVersionCheckDto && + other.channel == channel && other.enabled == enabled; @override int get hashCode => // ignore: unnecessary_parenthesis + (channel.hashCode) + (enabled.hashCode); @override - String toString() => 'SystemConfigNewVersionCheckDto[enabled=$enabled]'; + String toString() => 'SystemConfigNewVersionCheckDto[channel=$channel, enabled=$enabled]'; Map toJson() { final json = {}; + json[r'channel'] = this.channel; json[r'enabled'] = this.enabled; return json; } @@ -46,6 +52,7 @@ class SystemConfigNewVersionCheckDto { final json = value.cast(); return SystemConfigNewVersionCheckDto( + channel: ReleaseChannel.fromJson(json[r'channel'])!, enabled: mapValueOfType(json, r'enabled')!, ); } @@ -94,6 +101,7 @@ class SystemConfigNewVersionCheckDto { /// The list of required keys that must be present in a JSON. static const requiredKeys = { + 'channel', 'enabled', }; } diff --git a/mobile/openapi/lib/model/workflow_trigger.dart b/mobile/openapi/lib/model/workflow_trigger.dart index 47bf95e05e..b56d1b0dba 100644 --- a/mobile/openapi/lib/model/workflow_trigger.dart +++ b/mobile/openapi/lib/model/workflow_trigger.dart @@ -24,11 +24,13 @@ class WorkflowTrigger { String toJson() => value; static const assetCreate = WorkflowTrigger._(r'AssetCreate'); + static const assetMetadataExtraction = WorkflowTrigger._(r'AssetMetadataExtraction'); static const personRecognized = WorkflowTrigger._(r'PersonRecognized'); /// List of all possible values in this [enum][WorkflowTrigger]. static const values = [ assetCreate, + assetMetadataExtraction, personRecognized, ]; @@ -69,6 +71,7 @@ class WorkflowTriggerTypeTransformer { if (data != null) { switch (data) { case r'AssetCreate': return WorkflowTrigger.assetCreate; + case r'AssetMetadataExtraction': return WorkflowTrigger.assetMetadataExtraction; case r'PersonRecognized': return WorkflowTrigger.personRecognized; default: if (!allowNull) { diff --git a/mobile/packages/ui/lib/src/previews.dart b/mobile/packages/ui/lib/src/previews.dart new file mode 100644 index 0000000000..076bc253c9 --- /dev/null +++ b/mobile/packages/ui/lib/src/previews.dart @@ -0,0 +1,60 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/widget_previews.dart'; +import 'package:immich_ui/src/theme.dart'; + +const ColorScheme _lightColorScheme = ColorScheme.light( + primary: Color(0xFF4250AF), + onPrimary: Colors.white, + primaryContainer: Color(0xFFD4D6F0), + onPrimaryContainer: Color(0xFF181E44), + secondary: Color(0xFF737373), + onSecondary: Colors.white, + error: Color(0xFFE53E3E), + onError: Colors.white, + surface: Color(0xFFFAFAFA), + onSurface: Color(0xFF1A1C1E), + surfaceContainerHighest: Color(0xFFE3E4E8), + outline: Color(0xFFD1D3D9), + outlineVariant: Color(0xFFD4D4D4), +); + +const ColorScheme _darkColorScheme = ColorScheme.dark( + primary: Color(0xFFACCBFA), + onPrimary: Color(0xFF0F1433), + primaryContainer: Color(0xFF616D94), + onPrimaryContainer: Color(0xFFD4D6F0), + secondary: Color(0xFFC4C6D0), + onSecondary: Color(0xFF2E3042), + error: Color(0xFFE88080), + onError: Color(0xFF0F1433), + surface: Color(0xFF0A0A0A), + onSurface: Color(0xFFE3E3E6), + surfaceContainerHighest: Color(0xFF262626), + outline: Color(0xFF8E9099), + outlineVariant: Color(0xFF43464F), +); + +PreviewThemeData immichPreviewTheme() => PreviewThemeData( + materialLight: ThemeData(colorScheme: _lightColorScheme, useMaterial3: true), + materialDark: ThemeData(colorScheme: _darkColorScheme, useMaterial3: true), + ); + +Widget immichPreviewWrapper(Widget child) { + return Builder( + builder: (context) => ImmichThemeProvider( + colorScheme: Theme.of(context).colorScheme, + child: Scaffold( + backgroundColor: Theme.of(context).colorScheme.surface, + body: Padding( + padding: const EdgeInsets.all(16), + child: Align(alignment: Alignment.topLeft, child: child), + ), + ), + ), + ); +} + +final class ImmichPreview extends Preview { + const ImmichPreview({super.name, super.group, super.size, super.textScaleFactor}) + : super(theme: immichPreviewTheme, wrapper: immichPreviewWrapper); +} diff --git a/mobile/packages/ui/lib/src/previews/close_button.dart b/mobile/packages/ui/lib/src/previews/close_button.dart new file mode 100644 index 0000000000..678d6bed18 --- /dev/null +++ b/mobile/packages/ui/lib/src/previews/close_button.dart @@ -0,0 +1,26 @@ +import 'package:flutter/material.dart'; +import 'package:immich_ui/src/components/close_button.dart'; +import 'package:immich_ui/src/previews.dart'; +import 'package:immich_ui/src/types.dart'; + +void _previewNoop() {} + +@ImmichPreview(group: 'CloseButton', name: 'Variants') +Widget previewCloseButtonVariants() => const Wrap( + spacing: 12, + runSpacing: 12, + children: [ + ImmichCloseButton(onPressed: _previewNoop), + ImmichCloseButton(onPressed: _previewNoop, variant: ImmichVariant.filled), + ], + ); + +@ImmichPreview(group: 'CloseButton', name: 'Colors') +Widget previewCloseButtonColors() => const Wrap( + spacing: 12, + runSpacing: 12, + children: [ + ImmichCloseButton(onPressed: _previewNoop), + ImmichCloseButton(onPressed: _previewNoop, color: ImmichColor.secondary), + ], + ); diff --git a/mobile/packages/ui/lib/src/previews/form.dart b/mobile/packages/ui/lib/src/previews/form.dart new file mode 100644 index 0000000000..9e488cfd68 --- /dev/null +++ b/mobile/packages/ui/lib/src/previews/form.dart @@ -0,0 +1,72 @@ +import 'package:flutter/material.dart'; +import 'package:immich_ui/src/components/form.dart'; +import 'package:immich_ui/src/components/password_input.dart'; +import 'package:immich_ui/src/components/text_input.dart'; +import 'package:immich_ui/src/constants.dart'; +import 'package:immich_ui/src/previews.dart'; + +@ImmichPreview(group: 'Form', name: 'Login Form') +Widget previewFormLogin() => const _PreviewLoginForm(); + +class _PreviewLoginForm extends StatefulWidget { + const _PreviewLoginForm(); + + @override + State<_PreviewLoginForm> createState() => _PreviewLoginFormState(); +} + +class _PreviewLoginFormState extends State<_PreviewLoginForm> { + final _emailController = TextEditingController(); + final _passwordController = TextEditingController(); + String _result = ''; + + @override + void dispose() { + _emailController.dispose(); + _passwordController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ImmichForm( + submitText: 'Login', + submitIcon: Icons.login, + onSubmit: () async { + await Future.delayed(const Duration(seconds: 1)); + if (!mounted) { + return; + } + setState(() { + _result = 'Form submitted!'; + }); + }, + builder: (context, form) => Column( + spacing: ImmichSpacing.sm, + children: [ + ImmichTextInput( + label: 'Email', + controller: _emailController, + keyboardType: TextInputType.emailAddress, + validator: (value) => value?.isEmpty ?? true ? 'Required' : null, + ), + ImmichPasswordInput( + label: 'Password', + controller: _passwordController, + validator: (value) => value?.isEmpty ?? true ? 'Required' : null, + onSubmit: (_) => form.submit(), + ), + ], + ), + ), + if (_result.isNotEmpty) ...[ + const SizedBox(height: 16), + Text(_result, style: const TextStyle(color: Colors.green)), + ], + ], + ); + } +} diff --git a/mobile/packages/ui/lib/src/previews/formatted_text.dart b/mobile/packages/ui/lib/src/previews/formatted_text.dart new file mode 100644 index 0000000000..9ef3b4b851 --- /dev/null +++ b/mobile/packages/ui/lib/src/previews/formatted_text.dart @@ -0,0 +1,50 @@ +import 'package:flutter/material.dart'; +import 'package:immich_ui/src/components/formatted_text.dart'; +import 'package:immich_ui/src/previews.dart'; + +@ImmichPreview(group: 'FormattedText', name: 'Bold') +Widget previewFormattedTextBold() => const ImmichFormattedText('This is bold text.'); + +@ImmichPreview(group: 'FormattedText', name: 'Links') +Widget previewFormattedTextLinks() => const _PreviewFormattedTextLinks(); + +@ImmichPreview(group: 'FormattedText', name: 'Mixed Content') +Widget previewFormattedTextMixed() => const _PreviewFormattedTextMixed(); + +class _PreviewFormattedTextLinks extends StatelessWidget { + const _PreviewFormattedTextLinks(); + + @override + Widget build(BuildContext context) { + return ImmichFormattedText( + 'Read the documentation or visit GitHub.', + spanBuilder: (tag) => FormattedSpan( + onTap: switch (tag) { + 'docs-link' => + () => ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Docs link clicked!'))), + 'github-link' => + () => ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('GitHub link clicked!'))), + _ => null, + }, + ), + ); + } +} + +class _PreviewFormattedTextMixed extends StatelessWidget { + const _PreviewFormattedTextMixed(); + + @override + Widget build(BuildContext context) { + return ImmichFormattedText( + 'You can use bold text and links together.', + spanBuilder: (tag) => switch (tag) { + 'b' => const FormattedSpan(style: TextStyle(fontWeight: FontWeight.bold)), + _ => FormattedSpan( + onTap: () => + ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Link clicked!'))), + ), + }, + ); + } +} diff --git a/mobile/packages/ui/lib/src/previews/icon_button.dart b/mobile/packages/ui/lib/src/previews/icon_button.dart new file mode 100644 index 0000000000..6a0196bc81 --- /dev/null +++ b/mobile/packages/ui/lib/src/previews/icon_button.dart @@ -0,0 +1,41 @@ +import 'package:flutter/material.dart'; +import 'package:immich_ui/src/components/icon_button.dart'; +import 'package:immich_ui/src/previews.dart'; +import 'package:immich_ui/src/types.dart'; + +void _previewNoop() {} + +@ImmichPreview(group: 'IconButton', name: 'Variants') +Widget previewIconButtonVariants() => const Wrap( + spacing: 12, + runSpacing: 12, + children: [ + ImmichIconButton(icon: Icons.add, onPressed: _previewNoop), + ImmichIconButton(icon: Icons.edit, onPressed: _previewNoop, variant: ImmichVariant.ghost), + ], + ); + +@ImmichPreview(group: 'IconButton', name: 'Colors') +Widget previewIconButtonColors() => const Wrap( + spacing: 12, + runSpacing: 12, + children: [ + ImmichIconButton(icon: Icons.favorite, onPressed: _previewNoop), + ImmichIconButton(icon: Icons.delete, onPressed: _previewNoop, color: ImmichColor.secondary), + ], + ); + +@ImmichPreview(group: 'IconButton', name: 'Disabled') +Widget previewIconButtonDisabled() => const Wrap( + spacing: 12, + runSpacing: 12, + children: [ + ImmichIconButton(icon: Icons.settings, onPressed: _previewNoop, disabled: true), + ImmichIconButton( + icon: Icons.settings, + onPressed: _previewNoop, + disabled: true, + variant: ImmichVariant.ghost, + ), + ], + ); diff --git a/mobile/packages/ui/lib/src/previews/password_input.dart b/mobile/packages/ui/lib/src/previews/password_input.dart new file mode 100644 index 0000000000..72bd9cbfc5 --- /dev/null +++ b/mobile/packages/ui/lib/src/previews/password_input.dart @@ -0,0 +1,18 @@ +import 'package:flutter/material.dart'; +import 'package:immich_ui/src/components/password_input.dart'; +import 'package:immich_ui/src/previews.dart'; + +@ImmichPreview(group: 'PasswordInput', name: 'With Validator') +Widget previewPasswordInput() => ImmichPasswordInput( + label: 'Password', + hintText: 'Enter your password', + validator: (value) { + if (value == null || value.isEmpty) { + return 'Password is required'; + } + if (value.length < 8) { + return 'Password must be at least 8 characters'; + } + return null; + }, + ); diff --git a/mobile/packages/ui/lib/src/previews/text_button.dart b/mobile/packages/ui/lib/src/previews/text_button.dart new file mode 100644 index 0000000000..46c689bbae --- /dev/null +++ b/mobile/packages/ui/lib/src/previews/text_button.dart @@ -0,0 +1,88 @@ +import 'package:flutter/material.dart'; +import 'package:immich_ui/src/components/text_button.dart'; +import 'package:immich_ui/src/previews.dart'; +import 'package:immich_ui/src/types.dart'; + +void _previewNoop() {} + +@ImmichPreview(group: 'TextButton', name: 'Variants') +Widget previewTextButtonVariants() => const Wrap( + spacing: 12, + runSpacing: 12, + children: [ + ImmichTextButton(onPressed: _previewNoop, labelText: 'Filled', expanded: false), + ImmichTextButton(onPressed: _previewNoop, labelText: 'Ghost', variant: ImmichVariant.ghost, expanded: false), + ], +); + +@ImmichPreview(group: 'TextButton', name: 'Colors') +Widget previewTextButtonColors() => const Wrap( + spacing: 12, + runSpacing: 12, + children: [ + ImmichTextButton(onPressed: _previewNoop, labelText: 'Primary', expanded: false), + ImmichTextButton(onPressed: _previewNoop, labelText: 'Secondary', color: ImmichColor.secondary, expanded: false), + ], +); + +@ImmichPreview(group: 'TextButton', name: 'With Icons') +Widget previewTextButtonWithIcons() => const Wrap( + spacing: 12, + runSpacing: 12, + children: [ + ImmichTextButton(onPressed: _previewNoop, labelText: 'With Icon', icon: Icons.add, expanded: false), + ImmichTextButton( + onPressed: _previewNoop, + labelText: 'Download', + icon: Icons.download, + variant: ImmichVariant.ghost, + expanded: false, + ), + ], +); + +@ImmichPreview(group: 'TextButton', name: 'Loading') +Widget previewTextButtonLoading() => const _PreviewLoadingDemo(); + +@ImmichPreview(group: 'TextButton', name: 'Disabled') +Widget previewTextButtonDisabled() => const Wrap( + spacing: 12, + runSpacing: 12, + children: [ + ImmichTextButton(onPressed: _previewNoop, labelText: 'Disabled', disabled: true, expanded: false), + ImmichTextButton( + onPressed: _previewNoop, + labelText: 'Disabled Ghost', + variant: ImmichVariant.ghost, + disabled: true, + expanded: false, + ), + ], +); + +class _PreviewLoadingDemo extends StatefulWidget { + const _PreviewLoadingDemo(); + + @override + State<_PreviewLoadingDemo> createState() => _PreviewLoadingDemoState(); +} + +class _PreviewLoadingDemoState extends State<_PreviewLoadingDemo> { + bool _isLoading = false; + + @override + Widget build(BuildContext context) { + return ImmichTextButton( + onPressed: () async { + setState(() => _isLoading = true); + await Future.delayed(const Duration(seconds: 2)); + if (mounted) { + setState(() => _isLoading = false); + } + }, + labelText: _isLoading ? 'Loading...' : 'Click Me', + loading: _isLoading, + expanded: false, + ); + } +} diff --git a/mobile/packages/ui/lib/src/previews/text_input.dart b/mobile/packages/ui/lib/src/previews/text_input.dart new file mode 100644 index 0000000000..fab58c8cc4 --- /dev/null +++ b/mobile/packages/ui/lib/src/previews/text_input.dart @@ -0,0 +1,71 @@ +import 'package:flutter/material.dart'; +import 'package:immich_ui/src/components/text_input.dart'; +import 'package:immich_ui/src/previews.dart'; + +@ImmichPreview(group: 'TextInput', name: 'Basic') +Widget previewTextInputBasic() => const _PreviewTextInputBasic(); + +@ImmichPreview(group: 'TextInput', name: 'With Validator') +Widget previewTextInputValidator() => const _PreviewTextInputValidator(); + +class _PreviewTextInputBasic extends StatefulWidget { + const _PreviewTextInputBasic(); + + @override + State<_PreviewTextInputBasic> createState() => _PreviewTextInputBasicState(); +} + +class _PreviewTextInputBasicState extends State<_PreviewTextInputBasic> { + final _controller = TextEditingController(); + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return ImmichTextInput( + label: 'Email', + hintText: 'Enter your email', + controller: _controller, + keyboardType: TextInputType.emailAddress, + ); + } +} + +class _PreviewTextInputValidator extends StatefulWidget { + const _PreviewTextInputValidator(); + + @override + State<_PreviewTextInputValidator> createState() => _PreviewTextInputValidatorState(); +} + +class _PreviewTextInputValidatorState extends State<_PreviewTextInputValidator> { + final _controller = TextEditingController(); + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return ImmichTextInput( + label: 'Username', + controller: _controller, + autovalidateMode: AutovalidateMode.onUserInteraction, + validator: (value) { + if (value == null || value.isEmpty) { + return 'Username is required'; + } + if (value.length < 3) { + return 'Username must be at least 3 characters'; + } + return null; + }, + ); + } +} diff --git a/mobile/packages/ui/lib/src/previews/url_input.dart b/mobile/packages/ui/lib/src/previews/url_input.dart new file mode 100644 index 0000000000..6819ac5796 --- /dev/null +++ b/mobile/packages/ui/lib/src/previews/url_input.dart @@ -0,0 +1,28 @@ +import 'package:flutter/material.dart'; +import 'package:immich_ui/src/components/url_input.dart'; +import 'package:immich_ui/src/previews.dart'; + +@ImmichPreview(group: 'URLInput', name: 'Basic') +Widget previewUrlInput() => const _PreviewUrlInput(); + +class _PreviewUrlInput extends StatefulWidget { + const _PreviewUrlInput(); + + @override + State<_PreviewUrlInput> createState() => _PreviewUrlInputState(); +} + +class _PreviewUrlInputState extends State<_PreviewUrlInput> { + final _controller = TextEditingController(); + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return ImmichURLInput(label: 'Server URL', hintText: 'https://demo.immich.com', controller: _controller); + } +} diff --git a/mobile/packages/ui/pubspec.lock b/mobile/packages/ui/pubspec.lock index 294c46e424..9d11b49253 100644 --- a/mobile/packages/ui/pubspec.lock +++ b/mobile/packages/ui/pubspec.lock @@ -185,5 +185,5 @@ packages: source: hosted version: "15.2.0" sdks: - dart: ">=3.11.0 <4.0.0" + dart: ">=3.12.0 <4.0.0" flutter: ">=3.18.0-18.0.pre.54" diff --git a/mobile/packages/ui/pubspec.yaml b/mobile/packages/ui/pubspec.yaml index de50e0a429..1f44694ace 100644 --- a/mobile/packages/ui/pubspec.yaml +++ b/mobile/packages/ui/pubspec.yaml @@ -2,7 +2,7 @@ name: immich_ui publish_to: none environment: - sdk: '>=3.11.0 <4.0.0' + sdk: '>=3.12.0 <4.0.0' dependencies: flutter: diff --git a/mobile/packages/ui/showcase/.gitignore b/mobile/packages/ui/showcase/.gitignore deleted file mode 100644 index b285cd608b..0000000000 --- a/mobile/packages/ui/showcase/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -# Build artifacts -build/ - -# Test cache and generated files -.dart_tool/ -.packages -.flutter-plugins -.flutter-plugins-dependencies - -# IDE-specific files -.vscode/ \ No newline at end of file diff --git a/mobile/packages/ui/showcase/.metadata b/mobile/packages/ui/showcase/.metadata deleted file mode 100644 index b95fa4d74e..0000000000 --- a/mobile/packages/ui/showcase/.metadata +++ /dev/null @@ -1,30 +0,0 @@ -# This file tracks properties of this Flutter project. -# Used by Flutter tool to assess capabilities and perform upgrades etc. -# -# This file should be version controlled and should not be manually edited. - -version: - revision: "adc901062556672b4138e18a4dc62a4be8f4b3c2" - channel: "stable" - -project_type: app - -# Tracks metadata for the flutter migrate command -migration: - platforms: - - platform: root - create_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2 - base_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2 - - platform: web - create_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2 - base_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2 - - # User provided section - - # List of Local paths (relative to this file) that should be - # ignored by the migrate tool. - # - # Files that are not part of the templates will be ignored by default. - unmanaged_files: - - 'lib/main.dart' - - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/mobile/packages/ui/showcase/analysis_options.yaml b/mobile/packages/ui/showcase/analysis_options.yaml deleted file mode 100644 index f9b303465f..0000000000 --- a/mobile/packages/ui/showcase/analysis_options.yaml +++ /dev/null @@ -1 +0,0 @@ -include: package:flutter_lints/flutter.yaml diff --git a/mobile/packages/ui/showcase/assets/immich-text-dark.png b/mobile/packages/ui/showcase/assets/immich-text-dark.png deleted file mode 100644 index 215687af8f..0000000000 Binary files a/mobile/packages/ui/showcase/assets/immich-text-dark.png and /dev/null differ diff --git a/mobile/packages/ui/showcase/assets/immich-text-light.png b/mobile/packages/ui/showcase/assets/immich-text-light.png deleted file mode 100644 index 478158d39c..0000000000 Binary files a/mobile/packages/ui/showcase/assets/immich-text-light.png and /dev/null differ diff --git a/mobile/packages/ui/showcase/assets/immich_logo.png b/mobile/packages/ui/showcase/assets/immich_logo.png deleted file mode 100644 index 49fd3ae289..0000000000 Binary files a/mobile/packages/ui/showcase/assets/immich_logo.png and /dev/null differ diff --git a/mobile/packages/ui/showcase/assets/themes/github_dark.json b/mobile/packages/ui/showcase/assets/themes/github_dark.json deleted file mode 100644 index bd4801482e..0000000000 --- a/mobile/packages/ui/showcase/assets/themes/github_dark.json +++ /dev/null @@ -1,339 +0,0 @@ -{ - "name": "GitHub Dark", - "settings": [ - { - "settings": { - "foreground": "#e1e4e8", - "background": "#24292e" - } - }, - { - "scope": [ - "comment", - "punctuation.definition.comment", - "string.comment" - ], - "settings": { - "foreground": "#6a737d" - } - }, - { - "scope": [ - "constant", - "entity.name.constant", - "variable.other.constant", - "variable.other.enummember", - "variable.language" - ], - "settings": { - "foreground": "#79b8ff" - } - }, - { - "scope": [ - "entity", - "entity.name" - ], - "settings": { - "foreground": "#b392f0" - } - }, - { - "scope": "variable.parameter.function", - "settings": { - "foreground": "#e1e4e8" - } - }, - { - "scope": "entity.name.tag", - "settings": { - "foreground": "#85e89d" - } - }, - { - "scope": "keyword", - "settings": { - "foreground": "#f97583" - } - }, - { - "scope": [ - "storage", - "storage.type" - ], - "settings": { - "foreground": "#f97583" - } - }, - { - "scope": [ - "storage.modifier.package", - "storage.modifier.import", - "storage.type.java" - ], - "settings": { - "foreground": "#e1e4e8" - } - }, - { - "scope": [ - "string", - "punctuation.definition.string", - "string punctuation.section.embedded source" - ], - "settings": { - "foreground": "#9ecbff" - } - }, - { - "scope": "support", - "settings": { - "foreground": "#79b8ff" - } - }, - { - "scope": "meta.property-name", - "settings": { - "foreground": "#79b8ff" - } - }, - { - "scope": "variable", - "settings": { - "foreground": "#ffab70" - } - }, - { - "scope": "variable.other", - "settings": { - "foreground": "#e1e4e8" - } - }, - { - "scope": "invalid.broken", - "settings": { - "fontStyle": "italic", - "foreground": "#fdaeb7" - } - }, - { - "scope": "invalid.deprecated", - "settings": { - "fontStyle": "italic", - "foreground": "#fdaeb7" - } - }, - { - "scope": "invalid.illegal", - "settings": { - "fontStyle": "italic", - "foreground": "#fdaeb7" - } - }, - { - "scope": "invalid.unimplemented", - "settings": { - "fontStyle": "italic", - "foreground": "#fdaeb7" - } - }, - { - "scope": "message.error", - "settings": { - "foreground": "#fdaeb7" - } - }, - { - "scope": "string variable", - "settings": { - "foreground": "#79b8ff" - } - }, - { - "scope": [ - "source.regexp", - "string.regexp" - ], - "settings": { - "foreground": "#dbedff" - } - }, - { - "scope": [ - "string.regexp.character-class", - "string.regexp constant.character.escape", - "string.regexp source.ruby.embedded", - "string.regexp string.regexp.arbitrary-repitition" - ], - "settings": { - "foreground": "#dbedff" - } - }, - { - "scope": "string.regexp constant.character.escape", - "settings": { - "fontStyle": "bold", - "foreground": "#85e89d" - } - }, - { - "scope": "support.constant", - "settings": { - "foreground": "#79b8ff" - } - }, - { - "scope": "support.variable", - "settings": { - "foreground": "#79b8ff" - } - }, - { - "scope": "meta.module-reference", - "settings": { - "foreground": "#79b8ff" - } - }, - { - "scope": "punctuation.definition.list.begin.markdown", - "settings": { - "foreground": "#ffab70" - } - }, - { - "scope": [ - "markup.heading", - "markup.heading entity.name" - ], - "settings": { - "fontStyle": "bold", - "foreground": "#79b8ff" - } - }, - { - "scope": "markup.quote", - "settings": { - "foreground": "#85e89d" - } - }, - { - "scope": "markup.italic", - "settings": { - "fontStyle": "italic", - "foreground": "#e1e4e8" - } - }, - { - "scope": "markup.bold", - "settings": { - "fontStyle": "bold", - "foreground": "#e1e4e8" - } - }, - { - "scope": "markup.underline", - "settings": { - "fontStyle": "underline" - } - }, - { - "scope": "markup.inline.raw", - "settings": { - "foreground": "#79b8ff" - } - }, - { - "scope": [ - "markup.deleted", - "meta.diff.header.from-file", - "punctuation.definition.deleted" - ], - "settings": { - "foreground": "#fdaeb7" - } - }, - { - "scope": [ - "markup.inserted", - "meta.diff.header.to-file", - "punctuation.definition.inserted" - ], - "settings": { - "foreground": "#85e89d" - } - }, - { - "scope": [ - "markup.changed", - "punctuation.definition.changed" - ], - "settings": { - "foreground": "#ffab70" - } - }, - { - "scope": [ - "markup.ignored", - "markup.untracked" - ], - "settings": { - "foreground": "#2f363d" - } - }, - { - "scope": "meta.diff.range", - "settings": { - "fontStyle": "bold", - "foreground": "#b392f0" - } - }, - { - "scope": "meta.diff.header", - "settings": { - "foreground": "#79b8ff" - } - }, - { - "scope": "meta.separator", - "settings": { - "fontStyle": "bold", - "foreground": "#79b8ff" - } - }, - { - "scope": "meta.output", - "settings": { - "foreground": "#79b8ff" - } - }, - { - "scope": [ - "brackethighlighter.tag", - "brackethighlighter.curly", - "brackethighlighter.round", - "brackethighlighter.square", - "brackethighlighter.angle", - "brackethighlighter.quote" - ], - "settings": { - "foreground": "#d1d5da" - } - }, - { - "scope": "brackethighlighter.unmatched", - "settings": { - "foreground": "#fdaeb7" - } - }, - { - "scope": [ - "constant.other.reference.link", - "string.other.link" - ], - "settings": { - "fontStyle": "underline", - "foreground": "#dbedff" - } - } - ] -} diff --git a/mobile/packages/ui/showcase/lib/app_theme.dart b/mobile/packages/ui/showcase/lib/app_theme.dart deleted file mode 100644 index 995bf3c91e..0000000000 --- a/mobile/packages/ui/showcase/lib/app_theme.dart +++ /dev/null @@ -1,96 +0,0 @@ -import 'package:flutter/material.dart'; - -class AppTheme { - // Light theme colors - static const _primary500 = Color(0xFF4250AF); - static const _primary100 = Color(0xFFD4D6F0); - static const _primary900 = Color(0xFF181E44); - static const _danger500 = Color(0xFFE53E3E); - static const _light50 = Color(0xFFFAFAFA); - static const _light300 = Color(0xFFD4D4D4); - static const _light500 = Color(0xFF737373); - - // Dark theme colors - static const _darkPrimary500 = Color(0xFFACCBFA); - static const _darkPrimary300 = Color(0xFF616D94); - static const _darkDanger500 = Color(0xFFE88080); - static const _darkLight50 = Color(0xFF0A0A0A); - static const _darkLight100 = Color(0xFF171717); - static const _darkLight200 = Color(0xFF262626); - - static ThemeData get lightTheme { - return ThemeData( - colorScheme: const ColorScheme.light( - primary: _primary500, - onPrimary: Colors.white, - primaryContainer: _primary100, - onPrimaryContainer: _primary900, - secondary: _light500, - onSecondary: Colors.white, - error: _danger500, - onError: Colors.white, - surface: _light50, - onSurface: Color(0xFF1A1C1E), - surfaceContainerHighest: Color(0xFFE3E4E8), - outline: Color(0xFFD1D3D9), - outlineVariant: _light300, - ), - useMaterial3: true, - fontFamily: 'GoogleSans', - scaffoldBackgroundColor: _light50, - cardTheme: const CardThemeData( - elevation: 0, - color: Colors.white, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(12)), - side: BorderSide(color: _light300, width: 1), - ), - ), - appBarTheme: const AppBarTheme( - centerTitle: false, - elevation: 0, - backgroundColor: Colors.white, - surfaceTintColor: Colors.transparent, - foregroundColor: Color(0xFF1A1C1E), - ), - ); - } - - static ThemeData get darkTheme { - return ThemeData( - colorScheme: const ColorScheme.dark( - primary: _darkPrimary500, - onPrimary: Color(0xFF0F1433), - primaryContainer: _darkPrimary300, - onPrimaryContainer: _primary100, - secondary: Color(0xFFC4C6D0), - onSecondary: Color(0xFF2E3042), - error: _darkDanger500, - onError: Color(0xFF0F1433), - surface: _darkLight50, - onSurface: Color(0xFFE3E3E6), - surfaceContainerHighest: _darkLight200, - outline: Color(0xFF8E9099), - outlineVariant: Color(0xFF43464F), - ), - useMaterial3: true, - fontFamily: 'GoogleSans', - scaffoldBackgroundColor: _darkLight50, - cardTheme: const CardThemeData( - elevation: 0, - color: _darkLight100, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(12)), - side: BorderSide(color: _darkLight200, width: 1), - ), - ), - appBarTheme: const AppBarTheme( - centerTitle: false, - elevation: 0, - backgroundColor: _darkLight50, - surfaceTintColor: Colors.transparent, - foregroundColor: Color(0xFFE3E3E6), - ), - ); - } -} diff --git a/mobile/packages/ui/showcase/lib/constants.dart b/mobile/packages/ui/showcase/lib/constants.dart deleted file mode 100644 index cfca4cfda9..0000000000 --- a/mobile/packages/ui/showcase/lib/constants.dart +++ /dev/null @@ -1,16 +0,0 @@ -const String appTitle = '@immich/ui'; - -class LayoutConstants { - static const double sidebarWidth = 220.0; - - static const double gridSpacing = 16.0; - static const double gridAspectRatio = 2.5; - - static const double borderRadiusSmall = 6.0; - static const double borderRadiusMedium = 8.0; - static const double borderRadiusLarge = 12.0; - - static const double iconSizeSmall = 16.0; - static const double iconSizeMedium = 18.0; - static const double iconSizeLarge = 20.0; -} diff --git a/mobile/packages/ui/showcase/lib/main.dart b/mobile/packages/ui/showcase/lib/main.dart deleted file mode 100644 index 6cd2df4fe5..0000000000 --- a/mobile/packages/ui/showcase/lib/main.dart +++ /dev/null @@ -1,55 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; -import 'package:immich_ui/immich_ui.dart'; -import 'package:showcase/app_theme.dart'; -import 'package:showcase/constants.dart'; -import 'package:showcase/router.dart'; -import 'package:showcase/widgets/example_card.dart'; - -void main() async { - WidgetsFlutterBinding.ensureInitialized(); - await initializeCodeHighlighter(); - runApp(const ShowcaseApp()); -} - -class ShowcaseApp extends StatefulWidget { - const ShowcaseApp({super.key}); - - @override - State createState() => _ShowcaseAppState(); -} - -class _ShowcaseAppState extends State { - ThemeMode _themeMode = ThemeMode.light; - late final GoRouter _router; - - @override - void initState() { - super.initState(); - _router = AppRouter.createRouter(_toggleTheme); - } - - void _toggleTheme() { - setState(() { - _themeMode = _themeMode == ThemeMode.light - ? ThemeMode.dark - : ThemeMode.light; - }); - } - - @override - Widget build(BuildContext context) { - return MaterialApp.router( - title: appTitle, - themeMode: _themeMode, - routerConfig: _router, - theme: AppTheme.lightTheme, - darkTheme: AppTheme.darkTheme, - debugShowCheckedModeBanner: false, - builder: (context, child) => ImmichThemeProvider( - colorScheme: Theme.of(context).colorScheme, - child: child!, - ), - ); - } -} diff --git a/mobile/packages/ui/showcase/lib/pages/components/close_button_page.dart b/mobile/packages/ui/showcase/lib/pages/components/close_button_page.dart deleted file mode 100644 index 1bae98e0a4..0000000000 --- a/mobile/packages/ui/showcase/lib/pages/components/close_button_page.dart +++ /dev/null @@ -1,41 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:immich_ui/immich_ui.dart'; -import 'package:showcase/routes.dart'; -import 'package:showcase/widgets/component_examples.dart'; -import 'package:showcase/widgets/example_card.dart'; -import 'package:showcase/widgets/page_title.dart'; - -class CloseButtonPage extends StatelessWidget { - const CloseButtonPage({super.key}); - - @override - Widget build(BuildContext context) { - return PageTitle( - title: AppRoute.closeButton.name, - child: ComponentExamples( - title: 'ImmichCloseButton', - subtitle: 'Pre-configured close button for dialogs and sheets.', - examples: [ - ExampleCard( - title: 'Default & Custom', - preview: Wrap( - spacing: 12, - runSpacing: 12, - children: [ - ImmichCloseButton(onPressed: () {}), - ImmichCloseButton( - variant: ImmichVariant.filled, - onPressed: () {}, - ), - ImmichCloseButton( - color: ImmichColor.secondary, - onPressed: () {}, - ), - ], - ), - ), - ], - ), - ); - } -} diff --git a/mobile/packages/ui/showcase/lib/pages/components/examples/formatted_text_bold_text.dart b/mobile/packages/ui/showcase/lib/pages/components/examples/formatted_text_bold_text.dart deleted file mode 100644 index 7e36ac7537..0000000000 --- a/mobile/packages/ui/showcase/lib/pages/components/examples/formatted_text_bold_text.dart +++ /dev/null @@ -1,11 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:immich_ui/immich_ui.dart'; - -class FormattedTextBoldText extends StatelessWidget { - const FormattedTextBoldText({super.key}); - - @override - Widget build(BuildContext context) { - return ImmichFormattedText('This is bold text.'); - } -} diff --git a/mobile/packages/ui/showcase/lib/pages/components/examples/formatted_text_links.dart b/mobile/packages/ui/showcase/lib/pages/components/examples/formatted_text_links.dart deleted file mode 100644 index 3910a5117a..0000000000 --- a/mobile/packages/ui/showcase/lib/pages/components/examples/formatted_text_links.dart +++ /dev/null @@ -1,24 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:immich_ui/immich_ui.dart'; - -class FormattedTextLinks extends StatelessWidget { - const FormattedTextLinks({super.key}); - - @override - Widget build(BuildContext context) { - return ImmichFormattedText( - 'Read the documentation or visit GitHub.', - spanBuilder: (tag) => FormattedSpan( - onTap: switch (tag) { - 'docs-link' => () => ScaffoldMessenger.of( - context, - ).showSnackBar(const SnackBar(content: Text('Docs link clicked!'))), - 'github-link' => () => ScaffoldMessenger.of( - context, - ).showSnackBar(const SnackBar(content: Text('GitHub link clicked!'))), - _ => null, - }, - ), - ); - } -} diff --git a/mobile/packages/ui/showcase/lib/pages/components/examples/formatted_text_mixed_tags.dart b/mobile/packages/ui/showcase/lib/pages/components/examples/formatted_text_mixed_tags.dart deleted file mode 100644 index 3490b1c386..0000000000 --- a/mobile/packages/ui/showcase/lib/pages/components/examples/formatted_text_mixed_tags.dart +++ /dev/null @@ -1,23 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:immich_ui/immich_ui.dart'; - -class FormattedTextMixedContent extends StatelessWidget { - const FormattedTextMixedContent({super.key}); - - @override - Widget build(BuildContext context) { - return ImmichFormattedText( - 'You can use bold text and links together.', - spanBuilder: (tag) => switch (tag) { - 'b' => const FormattedSpan( - style: TextStyle(fontWeight: FontWeight.bold), - ), - _ => FormattedSpan( - onTap: () => ScaffoldMessenger.of( - context, - ).showSnackBar(const SnackBar(content: Text('Link clicked!'))), - ), - }, - ); - } -} diff --git a/mobile/packages/ui/showcase/lib/pages/components/form_page.dart b/mobile/packages/ui/showcase/lib/pages/components/form_page.dart deleted file mode 100644 index f4480026b3..0000000000 --- a/mobile/packages/ui/showcase/lib/pages/components/form_page.dart +++ /dev/null @@ -1,80 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:immich_ui/immich_ui.dart'; -import 'package:showcase/routes.dart'; -import 'package:showcase/widgets/component_examples.dart'; -import 'package:showcase/widgets/example_card.dart'; -import 'package:showcase/widgets/page_title.dart'; - -class FormPage extends StatefulWidget { - const FormPage({super.key}); - - @override - State createState() => _FormPageState(); -} - -class _FormPageState extends State { - final _emailController = TextEditingController(); - final _passwordController = TextEditingController(); - String _result = ''; - - @override - Widget build(BuildContext context) { - return PageTitle( - title: AppRoute.form.name, - child: ComponentExamples( - title: 'ImmichForm', - subtitle: - 'Form container with built-in validation and submit handling.', - examples: [ - ExampleCard( - title: 'Login Form', - preview: Column( - children: [ - ImmichForm( - submitText: 'Login', - submitIcon: Icons.login, - onSubmit: () async { - await Future.delayed(const Duration(seconds: 1)); - setState(() { - _result = 'Form submitted!'; - }); - }, - builder: (context, form) => Column( - spacing: 10, - children: [ - ImmichTextInput( - label: 'Email', - controller: _emailController, - keyboardType: TextInputType.emailAddress, - validator: (value) => - value?.isEmpty ?? true ? 'Required' : null, - ), - ImmichPasswordInput( - label: 'Password', - controller: _passwordController, - validator: (value) => - value?.isEmpty ?? true ? 'Required' : null, - onSubmit: (_) => form.submit(), - ), - ], - ), - ), - if (_result.isNotEmpty) ...[ - const SizedBox(height: 16), - Text(_result, style: const TextStyle(color: Colors.green)), - ], - ], - ), - ), - ], - ), - ); - } - - @override - void dispose() { - _emailController.dispose(); - _passwordController.dispose(); - super.dispose(); - } -} diff --git a/mobile/packages/ui/showcase/lib/pages/components/formatted_text_page.dart b/mobile/packages/ui/showcase/lib/pages/components/formatted_text_page.dart deleted file mode 100644 index b827e0340b..0000000000 --- a/mobile/packages/ui/showcase/lib/pages/components/formatted_text_page.dart +++ /dev/null @@ -1,40 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:showcase/pages/components/examples/formatted_text_bold_text.dart'; -import 'package:showcase/pages/components/examples/formatted_text_links.dart'; -import 'package:showcase/pages/components/examples/formatted_text_mixed_tags.dart'; -import 'package:showcase/routes.dart'; -import 'package:showcase/widgets/component_examples.dart'; -import 'package:showcase/widgets/example_card.dart'; -import 'package:showcase/widgets/page_title.dart'; - -class FormattedTextPage extends StatelessWidget { - const FormattedTextPage({super.key}); - - @override - Widget build(BuildContext context) { - return PageTitle( - title: AppRoute.formattedText.name, - child: ComponentExamples( - title: 'ImmichFormattedText', - subtitle: 'Render text with HTML formatting (bold, links).', - examples: [ - ExampleCard( - title: 'Bold Text', - preview: const FormattedTextBoldText(), - code: 'formatted_text_bold_text.dart', - ), - ExampleCard( - title: 'Links', - preview: const FormattedTextLinks(), - code: 'formatted_text_links.dart', - ), - ExampleCard( - title: 'Mixed Content', - preview: const FormattedTextMixedContent(), - code: 'formatted_text_mixed_tags.dart', - ), - ], - ), - ); - } -} diff --git a/mobile/packages/ui/showcase/lib/pages/components/icon_button_page.dart b/mobile/packages/ui/showcase/lib/pages/components/icon_button_page.dart deleted file mode 100644 index 4418b1de4f..0000000000 --- a/mobile/packages/ui/showcase/lib/pages/components/icon_button_page.dart +++ /dev/null @@ -1,52 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:immich_ui/immich_ui.dart'; -import 'package:showcase/routes.dart'; -import 'package:showcase/widgets/component_examples.dart'; -import 'package:showcase/widgets/example_card.dart'; -import 'package:showcase/widgets/page_title.dart'; - -class IconButtonPage extends StatelessWidget { - const IconButtonPage({super.key}); - - @override - Widget build(BuildContext context) { - return PageTitle( - title: AppRoute.iconButton.name, - child: ComponentExamples( - title: 'ImmichIconButton', - subtitle: 'Icon-only button with customizable styling.', - examples: [ - ExampleCard( - title: 'Variants & Colors', - preview: Wrap( - spacing: 12, - runSpacing: 12, - children: [ - ImmichIconButton( - icon: Icons.add, - onPressed: () {}, - variant: ImmichVariant.filled, - ), - ImmichIconButton( - icon: Icons.edit, - onPressed: () {}, - variant: ImmichVariant.ghost, - ), - ImmichIconButton( - icon: Icons.delete, - onPressed: () {}, - color: ImmichColor.secondary, - ), - ImmichIconButton( - icon: Icons.settings, - onPressed: () {}, - disabled: true, - ), - ], - ), - ), - ], - ), - ); - } -} diff --git a/mobile/packages/ui/showcase/lib/pages/components/password_input_page.dart b/mobile/packages/ui/showcase/lib/pages/components/password_input_page.dart deleted file mode 100644 index 772dd7882f..0000000000 --- a/mobile/packages/ui/showcase/lib/pages/components/password_input_page.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:immich_ui/immich_ui.dart'; -import 'package:showcase/routes.dart'; -import 'package:showcase/widgets/component_examples.dart'; -import 'package:showcase/widgets/example_card.dart'; -import 'package:showcase/widgets/page_title.dart'; - -class PasswordInputPage extends StatelessWidget { - const PasswordInputPage({super.key}); - - @override - Widget build(BuildContext context) { - return PageTitle( - title: AppRoute.passwordInput.name, - child: ComponentExamples( - title: 'ImmichPasswordInput', - subtitle: 'Password field with visibility toggle.', - examples: [ - ExampleCard( - title: 'Password Input', - preview: ImmichPasswordInput( - label: 'Password', - hintText: 'Enter your password', - validator: (value) { - if (value == null || value.isEmpty) { - return 'Password is required'; - } - if (value.length < 8) { - return 'Password must be at least 8 characters'; - } - return null; - }, - ), - ), - ], - ), - ); - } -} diff --git a/mobile/packages/ui/showcase/lib/pages/components/text_button_page.dart b/mobile/packages/ui/showcase/lib/pages/components/text_button_page.dart deleted file mode 100644 index 59e5b86294..0000000000 --- a/mobile/packages/ui/showcase/lib/pages/components/text_button_page.dart +++ /dev/null @@ -1,140 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:immich_ui/immich_ui.dart'; -import 'package:showcase/routes.dart'; -import 'package:showcase/widgets/component_examples.dart'; -import 'package:showcase/widgets/example_card.dart'; -import 'package:showcase/widgets/page_title.dart'; - -class TextButtonPage extends StatefulWidget { - const TextButtonPage({super.key}); - - @override - State createState() => _TextButtonPageState(); -} - -class _TextButtonPageState extends State { - bool _isLoading = false; - @override - Widget build(BuildContext context) { - return PageTitle( - title: AppRoute.textButton.name, - child: ComponentExamples( - title: 'ImmichTextButton', - subtitle: - 'A versatile button component with multiple variants and color options.', - examples: [ - ExampleCard( - title: 'Variants', - description: - 'Filled and ghost variants for different visual hierarchy', - preview: Wrap( - spacing: 12, - runSpacing: 12, - children: [ - ImmichTextButton( - onPressed: () {}, - labelText: 'Filled', - variant: ImmichVariant.filled, - expanded: false, - ), - ImmichTextButton( - onPressed: () {}, - labelText: 'Ghost', - variant: ImmichVariant.ghost, - expanded: false, - ), - ], - ), - ), - ExampleCard( - title: 'Colors', - description: 'Primary and secondary color options', - preview: Wrap( - spacing: 12, - runSpacing: 12, - children: [ - ImmichTextButton( - onPressed: () {}, - labelText: 'Primary', - color: ImmichColor.primary, - expanded: false, - ), - ImmichTextButton( - onPressed: () {}, - labelText: 'Secondary', - color: ImmichColor.secondary, - expanded: false, - ), - ], - ), - ), - ExampleCard( - title: 'With Icons', - description: 'Add leading icons', - preview: Wrap( - spacing: 12, - runSpacing: 12, - children: [ - ImmichTextButton( - onPressed: () {}, - labelText: 'With Icon', - icon: Icons.add, - expanded: false, - ), - ImmichTextButton( - onPressed: () {}, - labelText: 'Download', - icon: Icons.download, - variant: ImmichVariant.ghost, - expanded: false, - ), - ], - ), - ), - ExampleCard( - title: 'Loading State', - description: 'Shows loading indicator during async operations', - preview: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - ImmichTextButton( - onPressed: () async { - setState(() => _isLoading = true); - await Future.delayed(const Duration(seconds: 2)); - if (mounted) setState(() => _isLoading = false); - }, - labelText: _isLoading ? 'Loading...' : 'Click Me', - loading: _isLoading, - expanded: false, - ), - ], - ), - ), - ExampleCard( - title: 'Disabled State', - description: 'Buttons can be disabled', - preview: Wrap( - spacing: 12, - runSpacing: 12, - children: [ - ImmichTextButton( - onPressed: () {}, - labelText: 'Disabled', - disabled: true, - expanded: false, - ), - ImmichTextButton( - onPressed: () {}, - labelText: 'Disabled Ghost', - variant: ImmichVariant.ghost, - disabled: true, - expanded: false, - ), - ], - ), - ), - ], - ), - ); - } -} diff --git a/mobile/packages/ui/showcase/lib/pages/components/text_input_page.dart b/mobile/packages/ui/showcase/lib/pages/components/text_input_page.dart deleted file mode 100644 index 5a0bfec6cd..0000000000 --- a/mobile/packages/ui/showcase/lib/pages/components/text_input_page.dart +++ /dev/null @@ -1,65 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:immich_ui/immich_ui.dart'; -import 'package:showcase/routes.dart'; -import 'package:showcase/widgets/component_examples.dart'; -import 'package:showcase/widgets/example_card.dart'; -import 'package:showcase/widgets/page_title.dart'; - -class TextInputPage extends StatefulWidget { - const TextInputPage({super.key}); - - @override - State createState() => _TextInputPageState(); -} - -class _TextInputPageState extends State { - final _controller1 = TextEditingController(); - final _controller2 = TextEditingController(); - - @override - Widget build(BuildContext context) { - return PageTitle( - title: AppRoute.textInput.name, - child: ComponentExamples( - title: 'ImmichTextInput', - subtitle: 'Text field with validation support.', - examples: [ - ExampleCard( - title: 'Basic Usage', - preview: Column( - children: [ - ImmichTextInput( - label: 'Email', - hintText: 'Enter your email', - controller: _controller1, - keyboardType: TextInputType.emailAddress, - ), - const SizedBox(height: 16), - ImmichTextInput( - label: 'Username', - controller: _controller2, - validator: (value) { - if (value == null || value.isEmpty) { - return 'Username is required'; - } - if (value.length < 3) { - return 'Username must be at least 3 characters'; - } - return null; - }, - ), - ], - ), - ), - ], - ), - ); - } - - @override - void dispose() { - _controller1.dispose(); - _controller2.dispose(); - super.dispose(); - } -} diff --git a/mobile/packages/ui/showcase/lib/pages/design_system/constants_page.dart b/mobile/packages/ui/showcase/lib/pages/design_system/constants_page.dart deleted file mode 100644 index 17de02d80a..0000000000 --- a/mobile/packages/ui/showcase/lib/pages/design_system/constants_page.dart +++ /dev/null @@ -1,396 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:immich_ui/immich_ui.dart'; -import 'package:showcase/routes.dart'; -import 'package:showcase/widgets/component_examples.dart'; -import 'package:showcase/widgets/example_card.dart'; -import 'package:showcase/widgets/page_title.dart'; - -class ConstantsPage extends StatefulWidget { - const ConstantsPage({super.key}); - - @override - State createState() => _ConstantsPageState(); -} - -class _ConstantsPageState extends State { - @override - Widget build(BuildContext context) { - return PageTitle( - title: AppRoute.constants.name, - child: ComponentExamples( - title: 'Constants', - subtitle: 'Consistent spacing, sizing, and styling constants.', - expand: true, - examples: [ - const ExampleCard( - title: 'Spacing', - description: 'ImmichSpacing (4.0 → 48.0)', - preview: Column( - children: [ - _SpacingBox(label: 'xs', size: ImmichSpacing.xs), - _SpacingBox(label: 'sm', size: ImmichSpacing.sm), - _SpacingBox(label: 'md', size: ImmichSpacing.md), - _SpacingBox(label: 'lg', size: ImmichSpacing.lg), - _SpacingBox(label: 'xl', size: ImmichSpacing.xl), - _SpacingBox(label: 'xxl', size: ImmichSpacing.xxl), - _SpacingBox(label: 'xxxl', size: ImmichSpacing.xxxl), - ], - ), - ), - const ExampleCard( - title: 'Border Radius', - description: 'ImmichRadius (0.0 → 24.0)', - preview: Wrap( - spacing: 12, - runSpacing: 12, - children: [ - _RadiusBox(label: 'none', radius: ImmichRadius.none), - _RadiusBox(label: 'xs', radius: ImmichRadius.xs), - _RadiusBox(label: 'sm', radius: ImmichRadius.sm), - _RadiusBox(label: 'md', radius: ImmichRadius.md), - _RadiusBox(label: 'lg', radius: ImmichRadius.lg), - _RadiusBox(label: 'xl', radius: ImmichRadius.xl), - _RadiusBox(label: 'xxl', radius: ImmichRadius.xxl), - ], - ), - ), - const ExampleCard( - title: 'Icon Sizes', - description: 'ImmichIconSize (16.0 → 48.0)', - preview: Wrap( - spacing: 16, - runSpacing: 16, - alignment: WrapAlignment.start, - children: [ - _IconSizeBox(label: 'xs', size: ImmichIconSize.xs), - _IconSizeBox(label: 'sm', size: ImmichIconSize.sm), - _IconSizeBox(label: 'md', size: ImmichIconSize.md), - _IconSizeBox(label: 'lg', size: ImmichIconSize.lg), - _IconSizeBox(label: 'xl', size: ImmichIconSize.xl), - _IconSizeBox(label: 'xxl', size: ImmichIconSize.xxl), - ], - ), - ), - const ExampleCard( - title: 'Text Sizes', - description: 'ImmichTextSize (10.0 → 60.0)', - preview: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Caption', - style: TextStyle(fontSize: ImmichTextSize.caption), - ), - Text('Label', style: TextStyle(fontSize: ImmichTextSize.label)), - Text('Body', style: TextStyle(fontSize: ImmichTextSize.body)), - Text('H6', style: TextStyle(fontSize: ImmichTextSize.h6)), - Text('H5', style: TextStyle(fontSize: ImmichTextSize.h5)), - Text('H4', style: TextStyle(fontSize: ImmichTextSize.h4)), - Text('H3', style: TextStyle(fontSize: ImmichTextSize.h3)), - Text('H2', style: TextStyle(fontSize: ImmichTextSize.h2)), - Text('H1', style: TextStyle(fontSize: ImmichTextSize.h1)), - ], - ), - ), - const ExampleCard( - title: 'Elevation', - description: 'ImmichElevation (0.0 → 16.0)', - preview: Wrap( - spacing: 12, - runSpacing: 12, - children: [ - _ElevationBox(label: 'none', elevation: ImmichElevation.none), - _ElevationBox(label: 'xs', elevation: ImmichElevation.xs), - _ElevationBox(label: 'sm', elevation: ImmichElevation.sm), - _ElevationBox(label: 'md', elevation: ImmichElevation.md), - _ElevationBox(label: 'lg', elevation: ImmichElevation.lg), - _ElevationBox(label: 'xl', elevation: ImmichElevation.xl), - _ElevationBox(label: 'xxl', elevation: ImmichElevation.xxl), - ], - ), - ), - const ExampleCard( - title: 'Border Width', - description: 'ImmichBorderWidth (0.5 → 4.0)', - preview: Column( - children: [ - _BorderBox( - label: 'hairline', - borderWidth: ImmichBorderWidth.hairline, - ), - _BorderBox(label: 'base', borderWidth: ImmichBorderWidth.base), - _BorderBox(label: 'md', borderWidth: ImmichBorderWidth.md), - _BorderBox(label: 'lg', borderWidth: ImmichBorderWidth.lg), - _BorderBox(label: 'xl', borderWidth: ImmichBorderWidth.xl), - ], - ), - ), - const ExampleCard( - title: 'Animation Durations', - description: 'ImmichDuration (100ms → 700ms)', - preview: Column( - crossAxisAlignment: CrossAxisAlignment.start, - spacing: 8, - children: [ - _AnimatedDurationBox( - label: 'Extra Fast', - duration: ImmichDuration.extraFast, - ), - _AnimatedDurationBox( - label: 'Fast', - duration: ImmichDuration.fast, - ), - _AnimatedDurationBox( - label: 'Normal', - duration: ImmichDuration.normal, - ), - _AnimatedDurationBox( - label: 'Slow', - duration: ImmichDuration.slow, - ), - _AnimatedDurationBox( - label: 'Extra Slow', - duration: ImmichDuration.extraSlow, - ), - ], - ), - ), - ], - ), - ); - } -} - -class _SpacingBox extends StatelessWidget { - final String label; - final double size; - - const _SpacingBox({required this.label, required this.size}); - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.symmetric(vertical: 4), - child: Row( - children: [ - SizedBox( - width: 60, - child: Text( - label, - style: const TextStyle(fontFamily: 'GoogleSansCode'), - ), - ), - Container( - width: size, - height: 24, - color: Theme.of(context).colorScheme.primary, - ), - const SizedBox(width: 8), - Text('${size.toStringAsFixed(1)}px'), - ], - ), - ); - } -} - -class _RadiusBox extends StatelessWidget { - final String label; - final double radius; - - const _RadiusBox({required this.label, required this.radius}); - - @override - Widget build(BuildContext context) { - return Column( - children: [ - Container( - width: 60, - height: 60, - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.primary, - borderRadius: BorderRadius.circular(radius), - ), - ), - const SizedBox(height: 4), - Text(label, style: const TextStyle(fontSize: 12)), - ], - ); - } -} - -class _IconSizeBox extends StatelessWidget { - final String label; - final double size; - - const _IconSizeBox({required this.label, required this.size}); - - @override - Widget build(BuildContext context) { - return Column( - children: [ - Icon(Icons.palette_rounded, size: size), - const SizedBox(height: 4), - Text(label, style: const TextStyle(fontSize: 12)), - Text( - '${size.toStringAsFixed(0)}px', - style: const TextStyle(fontSize: 10, color: Colors.grey), - ), - ], - ); - } -} - -class _ElevationBox extends StatelessWidget { - final String label; - final double elevation; - - const _ElevationBox({required this.label, required this.elevation}); - - @override - Widget build(BuildContext context) { - return Column( - children: [ - Material( - elevation: elevation, - borderRadius: const BorderRadius.all(Radius.circular(8)), - child: Container( - width: 60, - height: 60, - alignment: Alignment.center, - child: Text(label, style: const TextStyle(fontSize: 12)), - ), - ), - const SizedBox(height: 4), - Text( - elevation.toStringAsFixed(1), - style: const TextStyle(fontSize: 10), - ), - ], - ); - } -} - -class _BorderBox extends StatelessWidget { - final String label; - final double borderWidth; - - const _BorderBox({required this.label, required this.borderWidth}); - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.symmetric(vertical: 4), - child: Row( - children: [ - SizedBox( - width: 80, - child: Text( - label, - style: const TextStyle(fontFamily: 'GoogleSansCode'), - ), - ), - Expanded( - child: Container( - height: 40, - decoration: BoxDecoration( - border: Border.all( - color: Theme.of(context).colorScheme.primary, - width: borderWidth, - ), - borderRadius: const BorderRadius.all(Radius.circular(4)), - ), - ), - ), - const SizedBox(width: 8), - Text('${borderWidth.toStringAsFixed(1)}px'), - ], - ), - ); - } -} - -class _AnimatedDurationBox extends StatefulWidget { - final String label; - final Duration duration; - - const _AnimatedDurationBox({required this.label, required this.duration}); - - @override - State<_AnimatedDurationBox> createState() => _AnimatedDurationBoxState(); -} - -class _AnimatedDurationBoxState extends State<_AnimatedDurationBox> { - bool _atEnd = false; - bool _isAnimating = false; - - void _playAnimation() async { - if (_isAnimating) return; - setState(() => _isAnimating = true); - setState(() => _atEnd = true); - await Future.delayed(widget.duration); - if (!mounted) return; - setState(() => _atEnd = false); - await Future.delayed(widget.duration); - if (!mounted) return; - setState(() => _isAnimating = false); - } - - @override - Widget build(BuildContext context) { - final colorScheme = Theme.of(context).colorScheme; - return Row( - children: [ - SizedBox( - width: 90, - child: Text( - widget.label, - style: const TextStyle(fontFamily: 'GoogleSansCode', fontSize: 12), - ), - ), - Expanded( - child: Container( - height: 32, - decoration: BoxDecoration( - color: colorScheme.surfaceContainerHighest.withValues(alpha: 0.5), - borderRadius: BorderRadius.circular(6), - ), - child: AnimatedAlign( - duration: widget.duration, - curve: Curves.easeInOut, - alignment: _atEnd ? Alignment.centerRight : Alignment.centerLeft, - child: Container( - width: 60, - height: 28, - margin: const EdgeInsets.symmetric(horizontal: 2), - decoration: BoxDecoration( - color: colorScheme.primary, - borderRadius: BorderRadius.circular(4), - ), - alignment: Alignment.center, - child: Text( - '${widget.duration.inMilliseconds}ms', - style: TextStyle( - fontSize: 11, - color: colorScheme.onPrimary, - fontWeight: FontWeight.w500, - ), - ), - ), - ), - ), - ), - const SizedBox(width: 8), - IconButton( - onPressed: _isAnimating ? null : _playAnimation, - icon: Icon( - Icons.play_arrow_rounded, - color: _isAnimating ? colorScheme.outline : colorScheme.primary, - ), - iconSize: 24, - padding: EdgeInsets.zero, - constraints: const BoxConstraints(minWidth: 32, minHeight: 32), - ), - ], - ); - } -} diff --git a/mobile/packages/ui/showcase/lib/pages/home_page.dart b/mobile/packages/ui/showcase/lib/pages/home_page.dart deleted file mode 100644 index de7af6c26b..0000000000 --- a/mobile/packages/ui/showcase/lib/pages/home_page.dart +++ /dev/null @@ -1,118 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; -import 'package:showcase/constants.dart'; -import 'package:showcase/routes.dart'; - -class HomePage extends StatelessWidget { - final VoidCallback onThemeToggle; - - const HomePage({super.key, required this.onThemeToggle}); - - @override - Widget build(BuildContext context) { - return Title( - title: appTitle, - color: Theme.of(context).colorScheme.primary, - child: ListView( - padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 32), - children: [ - Text( - appTitle, - style: Theme.of(context).textTheme.displaySmall?.copyWith( - fontWeight: FontWeight.bold, - color: Theme.of(context).colorScheme.onSurface, - ), - ), - const SizedBox(height: 12), - Text( - 'A collection of Flutter components that are shared across all Immich projects', - style: Theme.of(context).textTheme.titleMedium?.copyWith( - color: Theme.of(context).colorScheme.onSurfaceVariant, - fontWeight: FontWeight.w400, - height: 1.5, - ), - ), - const SizedBox(height: 48), - ...routesByCategory.entries.map((entry) { - if (entry.key == AppRouteCategory.root) { - return const SizedBox.shrink(); - } - - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - entry.key.displayName, - style: Theme.of(context).textTheme.headlineSmall?.copyWith( - fontWeight: FontWeight.w600, - color: Theme.of(context).colorScheme.onSurface, - ), - ), - const SizedBox(height: 16), - GridView.builder( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 3, - crossAxisSpacing: LayoutConstants.gridSpacing, - mainAxisSpacing: LayoutConstants.gridSpacing, - childAspectRatio: LayoutConstants.gridAspectRatio, - ), - itemCount: entry.value.length, - itemBuilder: (context, index) { - return _ComponentCard(route: entry.value[index]); - }, - ), - const SizedBox(height: 48), - ], - ); - }), - ], - ), - ); - } -} - -class _ComponentCard extends StatelessWidget { - final AppRoute route; - - const _ComponentCard({required this.route}); - - @override - Widget build(BuildContext context) { - return InkWell( - onTap: () => context.go(route.path), - borderRadius: const BorderRadius.all(Radius.circular(LayoutConstants.borderRadiusLarge)), - child: Card( - child: Padding( - padding: const EdgeInsets.all(20), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Icon(route.icon, size: 32, color: Theme.of(context).colorScheme.primary), - const SizedBox(height: 16), - Text( - route.name, - style: Theme.of(context).textTheme.titleMedium?.copyWith( - fontWeight: FontWeight.w600, - color: Theme.of(context).colorScheme.onSurface, - ), - ), - - const SizedBox(height: 8), - Text( - route.description, - style: Theme.of( - context, - ).textTheme.bodyMedium?.copyWith(color: Theme.of(context).colorScheme.onSurfaceVariant, height: 1.4), - maxLines: 2, - overflow: TextOverflow.ellipsis, - ), - ], - ), - ), - ), - ); - } -} diff --git a/mobile/packages/ui/showcase/lib/router.dart b/mobile/packages/ui/showcase/lib/router.dart deleted file mode 100644 index 34393da508..0000000000 --- a/mobile/packages/ui/showcase/lib/router.dart +++ /dev/null @@ -1,48 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; -import 'package:showcase/pages/components/close_button_page.dart'; -import 'package:showcase/pages/components/form_page.dart'; -import 'package:showcase/pages/components/formatted_text_page.dart'; -import 'package:showcase/pages/components/icon_button_page.dart'; -import 'package:showcase/pages/components/password_input_page.dart'; -import 'package:showcase/pages/components/text_button_page.dart'; -import 'package:showcase/pages/components/text_input_page.dart'; -import 'package:showcase/pages/design_system/constants_page.dart'; -import 'package:showcase/pages/home_page.dart'; -import 'package:showcase/routes.dart'; -import 'package:showcase/widgets/shell_layout.dart'; - -class AppRouter { - static GoRouter createRouter(VoidCallback onThemeToggle) { - return GoRouter( - initialLocation: AppRoute.home.path, - routes: [ - ShellRoute( - builder: (context, state, child) => - ShellLayout(onThemeToggle: onThemeToggle, child: child), - routes: AppRoute.values - .map( - (route) => GoRoute( - path: route.path, - pageBuilder: (context, state) => NoTransitionPage( - key: state.pageKey, - child: switch (route) { - AppRoute.home => HomePage(onThemeToggle: onThemeToggle), - AppRoute.textButton => const TextButtonPage(), - AppRoute.iconButton => const IconButtonPage(), - AppRoute.closeButton => const CloseButtonPage(), - AppRoute.textInput => const TextInputPage(), - AppRoute.passwordInput => const PasswordInputPage(), - AppRoute.form => const FormPage(), - AppRoute.formattedText => const FormattedTextPage(), - AppRoute.constants => const ConstantsPage(), - }, - ), - ), - ) - .toList(), - ), - ], - ); - } -} diff --git a/mobile/packages/ui/showcase/lib/routes.dart b/mobile/packages/ui/showcase/lib/routes.dart deleted file mode 100644 index 4feeeafdb6..0000000000 --- a/mobile/packages/ui/showcase/lib/routes.dart +++ /dev/null @@ -1,97 +0,0 @@ -import 'package:flutter/material.dart'; - -enum AppRouteCategory { - root(''), - forms('Forms'), - buttons('Buttons'), - designSystem('Design System'); - - final String displayName; - const AppRouteCategory(this.displayName); -} - -enum AppRoute { - home( - name: 'Home', - description: 'Home page', - path: '/', - category: AppRouteCategory.root, - icon: Icons.home_outlined, - ), - textButton( - name: 'Text Button', - description: 'Versatile button with filled and ghost variants', - path: '/text-button', - category: AppRouteCategory.buttons, - icon: Icons.smart_button_rounded, - ), - iconButton( - name: 'Icon Button', - description: 'Icon-only button with customizable styling', - path: '/icon-button', - category: AppRouteCategory.buttons, - icon: Icons.radio_button_unchecked_rounded, - ), - closeButton( - name: 'Close Button', - description: 'Pre-configured close button for dialogs', - path: '/close-button', - category: AppRouteCategory.buttons, - icon: Icons.close_rounded, - ), - textInput( - name: 'Text Input', - description: 'Text field with validation support', - path: '/text-input', - category: AppRouteCategory.forms, - icon: Icons.text_fields_outlined, - ), - passwordInput( - name: 'Password Input', - description: 'Password field with visibility toggle', - path: '/password-input', - category: AppRouteCategory.forms, - icon: Icons.password_outlined, - ), - form( - name: 'Form', - description: 'Form container with built-in validation', - path: '/form', - category: AppRouteCategory.forms, - icon: Icons.description_outlined, - ), - formattedText( - name: 'Formatted Text', - description: 'Render text with HTML formatting', - path: '/formatted-text', - category: AppRouteCategory.forms, - icon: Icons.code_rounded, - ), - constants( - name: 'Constants', - description: 'Spacing, colors, typography, and more', - path: '/constants', - category: AppRouteCategory.designSystem, - icon: Icons.palette_outlined, - ); - - final String name; - final String description; - final String path; - final AppRouteCategory category; - final IconData icon; - - const AppRoute({ - required this.name, - required this.description, - required this.path, - required this.category, - required this.icon, - }); -} - -final routesByCategory = AppRoute.values - .fold>>({}, (map, route) { - map.putIfAbsent(route.category, () => []).add(route); - return map; - }); diff --git a/mobile/packages/ui/showcase/lib/widgets/component_examples.dart b/mobile/packages/ui/showcase/lib/widgets/component_examples.dart deleted file mode 100644 index 21e6516079..0000000000 --- a/mobile/packages/ui/showcase/lib/widgets/component_examples.dart +++ /dev/null @@ -1,85 +0,0 @@ -import 'package:flutter/material.dart'; - -class ComponentExamples extends StatelessWidget { - final String title; - final String? subtitle; - final List examples; - final bool expand; - - const ComponentExamples({ - super.key, - required this.title, - this.subtitle, - required this.examples, - this.expand = false, - }); - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.fromLTRB(10, 24, 24, 24), - child: CustomScrollView( - slivers: [ - SliverToBoxAdapter( - child: _PageHeader(title: title, subtitle: subtitle), - ), - const SliverPadding(padding: EdgeInsets.only(top: 24)), - if (expand) - SliverList.builder( - itemCount: examples.length, - itemBuilder: (context, index) => examples[index], - ) - else - SliverLayoutBuilder( - builder: (context, constraints) { - return SliverList.builder( - itemCount: examples.length, - itemBuilder: (context, index) => Align( - alignment: Alignment.centerLeft, - child: ConstrainedBox( - constraints: BoxConstraints( - minWidth: constraints.crossAxisExtent * 0.6, - maxWidth: constraints.crossAxisExtent, - ), - child: IntrinsicWidth(child: examples[index]), - ), - ), - ); - }, - ), - ], - ), - ); - } -} - -class _PageHeader extends StatelessWidget { - final String title; - final String? subtitle; - - const _PageHeader({required this.title, this.subtitle}); - - @override - Widget build(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - title, - style: Theme.of( - context, - ).textTheme.headlineLarge?.copyWith(fontWeight: FontWeight.bold), - ), - if (subtitle != null) ...[ - const SizedBox(height: 8), - Text( - subtitle!, - style: Theme.of(context).textTheme.bodyLarge?.copyWith( - color: Theme.of(context).colorScheme.onSurfaceVariant, - ), - ), - ], - ], - ); - } -} diff --git a/mobile/packages/ui/showcase/lib/widgets/example_card.dart b/mobile/packages/ui/showcase/lib/widgets/example_card.dart deleted file mode 100644 index fea561afb6..0000000000 --- a/mobile/packages/ui/showcase/lib/widgets/example_card.dart +++ /dev/null @@ -1,237 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:showcase/constants.dart'; -import 'package:syntax_highlight/syntax_highlight.dart'; - -late final Highlighter _codeHighlighter; - -Future initializeCodeHighlighter() async { - await Highlighter.initialize(['dart']); - final darkTheme = await HighlighterTheme.loadFromAssets([ - 'assets/themes/github_dark.json', - ], const TextStyle(color: Color(0xFFe1e4e8))); - - _codeHighlighter = Highlighter(language: 'dart', theme: darkTheme); -} - -class ExampleCard extends StatefulWidget { - final String title; - final String? description; - final Widget preview; - final String? code; - - const ExampleCard({ - super.key, - required this.title, - this.description, - required this.preview, - this.code, - }); - - @override - State createState() => _ExampleCardState(); -} - -class _ExampleCardState extends State { - bool _showPreview = true; - String? code; - - @override - void initState() { - super.initState(); - if (widget.code != null) { - rootBundle - .loadString('lib/pages/components/examples/${widget.code!}') - .then((value) { - setState(() { - code = value; - }); - }); - } - } - - @override - Widget build(BuildContext context) { - return Card( - elevation: 1, - margin: const EdgeInsets.only(bottom: 16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.all(16.0), - child: Row( - children: [ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - widget.title, - style: Theme.of(context).textTheme.titleMedium - ?.copyWith(fontWeight: FontWeight.bold), - ), - const SizedBox(height: 4), - if (widget.description != null) - Text( - widget.description!, - style: Theme.of(context).textTheme.bodyMedium - ?.copyWith( - color: Theme.of( - context, - ).colorScheme.onSurfaceVariant, - ), - ), - ], - ), - ), - if (code != null) ...[ - const SizedBox(width: 16), - Row( - children: [ - _ToggleButton( - icon: Icons.visibility_rounded, - label: 'Preview', - isSelected: _showPreview, - onTap: () => setState(() => _showPreview = true), - ), - const SizedBox(width: 8), - _ToggleButton( - icon: Icons.code_rounded, - label: 'Code', - isSelected: !_showPreview, - onTap: () => setState(() => _showPreview = false), - ), - ], - ), - ], - ], - ), - ), - const Divider(height: 1), - if (_showPreview) - Padding( - padding: const EdgeInsets.all(16.0), - child: SizedBox(width: double.infinity, child: widget.preview), - ) - else - Container( - width: double.infinity, - decoration: const BoxDecoration( - color: Color(0xFF24292e), - borderRadius: BorderRadius.only( - bottomLeft: Radius.circular( - LayoutConstants.borderRadiusMedium, - ), - bottomRight: Radius.circular( - LayoutConstants.borderRadiusMedium, - ), - ), - ), - child: _CodeCard(code: code!), - ), - ], - ), - ); - } -} - -class _ToggleButton extends StatelessWidget { - final IconData icon; - final String label; - final bool isSelected; - final VoidCallback onTap; - - const _ToggleButton({ - required this.icon, - required this.label, - required this.isSelected, - required this.onTap, - }); - - @override - Widget build(BuildContext context) { - return InkWell( - onTap: onTap, - borderRadius: const BorderRadius.all(Radius.circular(24)), - child: Container( - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10), - decoration: BoxDecoration( - color: isSelected - ? Theme.of(context).colorScheme.primary.withValues(alpha: 0.7) - : Theme.of(context).colorScheme.primary, - borderRadius: const BorderRadius.all(Radius.circular(24)), - ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Icon( - icon, - size: 16, - color: Theme.of(context).colorScheme.onPrimary, - ), - const SizedBox(width: 6), - Text( - label, - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: Theme.of(context).colorScheme.onPrimary, - fontWeight: isSelected ? FontWeight.w600 : FontWeight.w400, - ), - ), - ], - ), - ), - ); - } -} - -class _CodeCard extends StatelessWidget { - final String code; - - const _CodeCard({required this.code}); - - @override - Widget build(BuildContext context) { - final lines = code.split('\n'); - final lineNumberColor = Colors.white.withValues(alpha: 0.4); - - return SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: Padding( - padding: const EdgeInsets.only(left: 12, top: 8, bottom: 8), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.end, - children: List.generate( - lines.length, - (index) => SizedBox( - height: 20, - child: Text( - '${index + 1}', - style: TextStyle( - fontFamily: 'GoogleSansCode', - fontSize: 13, - color: lineNumberColor, - height: 1.5, - ), - ), - ), - ), - ), - const SizedBox(width: 16), - SelectableText.rich( - _codeHighlighter.highlight(code), - style: const TextStyle( - fontFamily: 'GoogleSansCode', - fontSize: 13, - height: 1.54, - ), - ), - ], - ), - ), - ); - } -} diff --git a/mobile/packages/ui/showcase/lib/widgets/page_title.dart b/mobile/packages/ui/showcase/lib/widgets/page_title.dart deleted file mode 100644 index eae3bf6ffb..0000000000 --- a/mobile/packages/ui/showcase/lib/widgets/page_title.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'package:flutter/material.dart'; - -class PageTitle extends StatelessWidget { - final String title; - final Widget child; - - const PageTitle({super.key, required this.title, required this.child}); - - @override - Widget build(BuildContext context) { - return Title( - title: '$title | @immich/ui', - color: Theme.of(context).colorScheme.primary, - child: child, - ); - } -} diff --git a/mobile/packages/ui/showcase/lib/widgets/shell_layout.dart b/mobile/packages/ui/showcase/lib/widgets/shell_layout.dart deleted file mode 100644 index 8bcb687e75..0000000000 --- a/mobile/packages/ui/showcase/lib/widgets/shell_layout.dart +++ /dev/null @@ -1,59 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:showcase/constants.dart'; -import 'package:showcase/widgets/sidebar_navigation.dart'; - -class ShellLayout extends StatelessWidget { - final Widget child; - final VoidCallback onThemeToggle; - - const ShellLayout({ - super.key, - required this.child, - required this.onThemeToggle, - }); - - @override - Widget build(BuildContext context) { - final isDark = Theme.of(context).brightness == Brightness.dark; - - return Scaffold( - appBar: AppBar( - title: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Image.asset('assets/immich_logo.png', height: 32, width: 32), - const SizedBox(width: 8), - Image.asset( - isDark - ? 'assets/immich-text-dark.png' - : 'assets/immich-text-light.png', - height: 24, - filterQuality: FilterQuality.none, - isAntiAlias: true, - ), - ], - ), - actions: [ - IconButton( - icon: Icon( - isDark ? Icons.light_mode_outlined : Icons.dark_mode_outlined, - size: LayoutConstants.iconSizeLarge, - ), - onPressed: onThemeToggle, - tooltip: 'Toggle theme', - ), - ], - shape: Border( - bottom: BorderSide(color: Theme.of(context).dividerColor, width: 1), - ), - ), - body: Row( - children: [ - const SidebarNavigation(), - const VerticalDivider(), - Expanded(child: child), - ], - ), - ); - } -} diff --git a/mobile/packages/ui/showcase/lib/widgets/sidebar_navigation.dart b/mobile/packages/ui/showcase/lib/widgets/sidebar_navigation.dart deleted file mode 100644 index 10eba170e6..0000000000 --- a/mobile/packages/ui/showcase/lib/widgets/sidebar_navigation.dart +++ /dev/null @@ -1,117 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; -import 'package:showcase/constants.dart'; -import 'package:showcase/routes.dart'; - -class SidebarNavigation extends StatelessWidget { - const SidebarNavigation({super.key}); - - @override - Widget build(BuildContext context) { - return Container( - width: LayoutConstants.sidebarWidth, - decoration: BoxDecoration(color: Theme.of(context).colorScheme.surface), - child: ListView( - padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 16), - children: [ - ...routesByCategory.entries.expand((entry) { - final category = entry.key; - final routes = entry.value; - return [ - if (category != AppRouteCategory.root) _CategoryHeader(category), - ...routes.map((route) => _NavItem(route)), - const SizedBox(height: 24), - ]; - }), - ], - ), - ); - } -} - -class _CategoryHeader extends StatelessWidget { - final AppRouteCategory category; - - const _CategoryHeader(this.category); - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.only(left: 12, top: 8, bottom: 8), - child: Text( - category.displayName, - style: Theme.of(context).textTheme.bodySmall?.copyWith( - color: Theme.of(context).colorScheme.onSurfaceVariant, - fontWeight: FontWeight.w600, - letterSpacing: 0.5, - ), - ), - ); - } -} - -class _NavItem extends StatelessWidget { - final AppRoute route; - - const _NavItem(this.route); - - @override - Widget build(BuildContext context) { - final currentRoute = GoRouterState.of(context).uri.toString(); - final isSelected = currentRoute == route.path; - final isDark = Theme.of(context).brightness == Brightness.dark; - - return Padding( - padding: const EdgeInsets.symmetric(vertical: 4), - child: Material( - color: Colors.transparent, - child: InkWell( - onTap: () { - context.go(route.path); - }, - borderRadius: BorderRadius.circular( - LayoutConstants.borderRadiusMedium, - ), - child: Container( - padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), - decoration: BoxDecoration( - color: isSelected - ? (isDark - ? Colors.white.withValues(alpha: 0.1) - : Theme.of( - context, - ).colorScheme.primaryContainer.withValues(alpha: 0.5)) - : Colors.transparent, - borderRadius: BorderRadius.circular( - LayoutConstants.borderRadiusMedium, - ), - ), - child: Row( - children: [ - Icon( - route.icon, - size: 20, - color: isSelected - ? Theme.of(context).colorScheme.primary - : Theme.of(context).colorScheme.onSurfaceVariant, - ), - const SizedBox(width: 16), - Expanded( - child: Text( - route.name, - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: isSelected - ? Theme.of(context).colorScheme.primary - : Theme.of(context).colorScheme.onSurface, - fontWeight: FontWeight.w500, - ), - ), - ), - ], - ), - ), - ), - ), - ); - } -} diff --git a/mobile/packages/ui/showcase/pubspec.lock b/mobile/packages/ui/showcase/pubspec.lock deleted file mode 100644 index d375d4b05b..0000000000 --- a/mobile/packages/ui/showcase/pubspec.lock +++ /dev/null @@ -1,377 +0,0 @@ -# Generated by pub -# See https://dart.dev/tools/pub/glossary#lockfile -packages: - async: - dependency: transitive - description: - name: async - sha256: e2eb0491ba5ddb6177742d2da23904574082139b07c1e33b8503b9f46f3e1a37 - url: "https://pub.dev" - source: hosted - version: "2.13.1" - boolean_selector: - dependency: transitive - description: - name: boolean_selector - sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" - url: "https://pub.dev" - source: hosted - version: "2.1.2" - characters: - dependency: transitive - description: - name: characters - sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b - url: "https://pub.dev" - source: hosted - version: "1.4.1" - clock: - dependency: transitive - description: - name: clock - sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b - url: "https://pub.dev" - source: hosted - version: "1.1.2" - collection: - dependency: transitive - description: - name: collection - sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" - url: "https://pub.dev" - source: hosted - version: "1.19.1" - crypto: - dependency: transitive - description: - name: crypto - sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf - url: "https://pub.dev" - source: hosted - version: "3.0.7" - device_info_plus: - dependency: transitive - description: - name: device_info_plus - sha256: "98f28b42168cc509abc92f88518882fd58061ea372d7999aecc424345c7bff6a" - url: "https://pub.dev" - source: hosted - version: "11.5.0" - device_info_plus_platform_interface: - dependency: transitive - description: - name: device_info_plus_platform_interface - sha256: e1ea89119e34903dca74b883d0dd78eb762814f97fb6c76f35e9ff74d261a18f - url: "https://pub.dev" - source: hosted - version: "7.0.3" - fake_async: - dependency: transitive - description: - name: fake_async - sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" - url: "https://pub.dev" - source: hosted - version: "1.3.3" - ffi: - dependency: transitive - description: - name: ffi - sha256: "6d7fd89431262d8f3125e81b50d3847a091d846eafcd4fdb88dd06f36d705a45" - url: "https://pub.dev" - source: hosted - version: "2.2.0" - file: - dependency: transitive - description: - name: file - sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 - url: "https://pub.dev" - source: hosted - version: "7.0.1" - fixnum: - dependency: transitive - description: - name: fixnum - sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be - url: "https://pub.dev" - source: hosted - version: "1.1.1" - flutter: - dependency: "direct main" - description: flutter - source: sdk - version: "0.0.0" - flutter_lints: - dependency: "direct dev" - description: - name: flutter_lints - sha256: "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1" - url: "https://pub.dev" - source: hosted - version: "6.0.0" - flutter_test: - dependency: "direct dev" - description: flutter - source: sdk - version: "0.0.0" - flutter_web_plugins: - dependency: transitive - description: flutter - source: sdk - version: "0.0.0" - go_router: - dependency: "direct main" - description: - name: go_router - sha256: "92d8cee7c57dff0a6c409c05597b460002434eccf7424a712283225b3962d03f" - url: "https://pub.dev" - source: hosted - version: "17.2.3" - immich_ui: - dependency: "direct main" - description: - path: ".." - relative: true - source: path - version: "0.0.0" - irondash_engine_context: - dependency: transitive - description: - name: irondash_engine_context - sha256: "2bb0bc13dfda9f5aaef8dde06ecc5feb1379f5bb387d59716d799554f3f305d7" - url: "https://pub.dev" - source: hosted - version: "0.5.5" - irondash_message_channel: - dependency: transitive - description: - name: irondash_message_channel - sha256: b4101669776509c76133b8917ab8cfc704d3ad92a8c450b92934dd8884a2f060 - url: "https://pub.dev" - source: hosted - version: "0.7.0" - leak_tracker: - dependency: transitive - description: - name: leak_tracker - sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" - url: "https://pub.dev" - source: hosted - version: "11.0.2" - leak_tracker_flutter_testing: - dependency: transitive - description: - name: leak_tracker_flutter_testing - sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" - url: "https://pub.dev" - source: hosted - version: "3.0.10" - leak_tracker_testing: - dependency: transitive - description: - name: leak_tracker_testing - sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" - url: "https://pub.dev" - source: hosted - version: "3.0.2" - lints: - dependency: transitive - description: - name: lints - sha256: "12f842a479589fea194fe5c5a3095abc7be0c1f2ddfa9a0e76aed1dbd26a87df" - url: "https://pub.dev" - source: hosted - version: "6.1.0" - logging: - dependency: transitive - description: - name: logging - sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 - url: "https://pub.dev" - source: hosted - version: "1.3.0" - matcher: - dependency: transitive - description: - name: matcher - sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861 - url: "https://pub.dev" - source: hosted - version: "0.12.19" - material_color_utilities: - dependency: transitive - description: - name: material_color_utilities - sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b" - url: "https://pub.dev" - source: hosted - version: "0.13.0" - meta: - dependency: transitive - description: - name: meta - sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349" - url: "https://pub.dev" - source: hosted - version: "1.18.0" - path: - dependency: transitive - description: - name: path - sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" - url: "https://pub.dev" - source: hosted - version: "1.9.1" - pixel_snap: - dependency: transitive - description: - name: pixel_snap - sha256: "677410ea37b07cd37ecb6d5e6c0d8d7615a7cf3bd92ba406fd1ac57e937d1fb0" - url: "https://pub.dev" - source: hosted - version: "0.1.5" - plugin_platform_interface: - dependency: transitive - description: - name: plugin_platform_interface - sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" - url: "https://pub.dev" - source: hosted - version: "2.1.8" - sky_engine: - dependency: transitive - description: flutter - source: sdk - version: "0.0.0" - source_span: - dependency: transitive - description: - name: source_span - sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab" - url: "https://pub.dev" - source: hosted - version: "1.10.2" - stack_trace: - dependency: transitive - description: - name: stack_trace - sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" - url: "https://pub.dev" - source: hosted - version: "1.12.1" - stream_channel: - dependency: transitive - description: - name: stream_channel - sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" - url: "https://pub.dev" - source: hosted - version: "2.1.4" - string_scanner: - dependency: transitive - description: - name: string_scanner - sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" - url: "https://pub.dev" - source: hosted - version: "1.4.1" - super_clipboard: - dependency: transitive - description: - name: super_clipboard - sha256: e73f3bb7e66cc9260efa1dc507f979138e7e106c3521e2dda2d0311f6d728a16 - url: "https://pub.dev" - source: hosted - version: "0.9.1" - super_native_extensions: - dependency: transitive - description: - name: super_native_extensions - sha256: b9611dcb68f1047d6f3ef11af25e4e68a21b1a705bbcc3eb8cb4e9f5c3148569 - url: "https://pub.dev" - source: hosted - version: "0.9.1" - syntax_highlight: - dependency: "direct main" - description: - name: syntax_highlight - sha256: "4d3ba40658cadba6ba55d697f29f00b43538ebb6eb4a0ca0e895c568eaced138" - url: "https://pub.dev" - source: hosted - version: "0.5.0" - term_glyph: - dependency: transitive - description: - name: term_glyph - sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" - url: "https://pub.dev" - source: hosted - version: "1.2.2" - test_api: - dependency: transitive - description: - name: test_api - sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e" - url: "https://pub.dev" - source: hosted - version: "0.7.11" - typed_data: - dependency: transitive - description: - name: typed_data - sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 - url: "https://pub.dev" - source: hosted - version: "1.4.0" - uuid: - dependency: transitive - description: - name: uuid - sha256: "1fef9e8e11e2991bb773070d4656b7bd5d850967a2456cfc83cf47925ba79489" - url: "https://pub.dev" - source: hosted - version: "4.5.3" - vector_math: - dependency: transitive - description: - name: vector_math - sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b - url: "https://pub.dev" - source: hosted - version: "2.2.0" - vm_service: - dependency: transitive - description: - name: vm_service - sha256: "0016aef94fc66495ac78af5859181e3f3bf2026bd8eecc72b9565601e19ab360" - url: "https://pub.dev" - source: hosted - version: "15.2.0" - web: - dependency: transitive - description: - name: web - sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" - url: "https://pub.dev" - source: hosted - version: "1.1.1" - win32: - dependency: transitive - description: - name: win32 - sha256: d7cb55e04cd34096cd3a79b3330245f54cb96a370a1c27adb3c84b917de8b08e - url: "https://pub.dev" - source: hosted - version: "5.15.0" - win32_registry: - dependency: transitive - description: - name: win32_registry - sha256: "6f1b564492d0147b330dd794fee8f512cec4977957f310f9951b5f9d83618dae" - url: "https://pub.dev" - source: hosted - version: "2.1.0" -sdks: - dart: ">=3.11.0 <4.0.0" - flutter: ">=3.35.0" diff --git a/mobile/packages/ui/showcase/pubspec.yaml b/mobile/packages/ui/showcase/pubspec.yaml deleted file mode 100644 index 6353600ce3..0000000000 --- a/mobile/packages/ui/showcase/pubspec.yaml +++ /dev/null @@ -1,47 +0,0 @@ -name: showcase -publish_to: 'none' - -version: 1.0.0+1 - -environment: - sdk: ^3.11.0 - -dependencies: - flutter: - sdk: flutter - immich_ui: - path: ../ - go_router: ^17.2.1 - syntax_highlight: ^0.5.0 - -dev_dependencies: - flutter_test: - sdk: flutter - flutter_lints: ^6.0.0 - -flutter: - uses-material-design: true - assets: - - assets/ - - assets/themes/ - - lib/pages/components/examples/ - - fonts: - - family: GoogleSans - fonts: - - asset: ../../../fonts/GoogleSans/GoogleSans-Regular.ttf - - asset: ../../../fonts/GoogleSans/GoogleSans-Italic.ttf - style: italic - - asset: ../../../fonts/GoogleSans/GoogleSans-Medium.ttf - weight: 500 - - asset: ../../../fonts/GoogleSans/GoogleSans-SemiBold.ttf - weight: 600 - - asset: ../../../fonts/GoogleSans/GoogleSans-Bold.ttf - weight: 700 - - family: GoogleSansCode - fonts: - - asset: ../../../fonts/GoogleSansCode/GoogleSansCode-Regular.ttf - - asset: ../../../fonts/GoogleSansCode/GoogleSansCode-Medium.ttf - weight: 500 - - asset: ../../../fonts/GoogleSansCode/GoogleSansCode-SemiBold.ttf - weight: 600 \ No newline at end of file diff --git a/mobile/packages/ui/showcase/web/favicon.ico b/mobile/packages/ui/showcase/web/favicon.ico deleted file mode 100644 index 7ec34e9e53..0000000000 Binary files a/mobile/packages/ui/showcase/web/favicon.ico and /dev/null differ diff --git a/mobile/packages/ui/showcase/web/icons/Icon-maskable-192.png b/mobile/packages/ui/showcase/web/icons/Icon-maskable-192.png deleted file mode 100644 index 49fd3ae289..0000000000 Binary files a/mobile/packages/ui/showcase/web/icons/Icon-maskable-192.png and /dev/null differ diff --git a/mobile/packages/ui/showcase/web/icons/Icon-maskable-512.png b/mobile/packages/ui/showcase/web/icons/Icon-maskable-512.png deleted file mode 100644 index a7220554bc..0000000000 Binary files a/mobile/packages/ui/showcase/web/icons/Icon-maskable-512.png and /dev/null differ diff --git a/mobile/packages/ui/showcase/web/icons/apple-icon-180.png b/mobile/packages/ui/showcase/web/icons/apple-icon-180.png deleted file mode 100644 index 4e642631a3..0000000000 Binary files a/mobile/packages/ui/showcase/web/icons/apple-icon-180.png and /dev/null differ diff --git a/mobile/packages/ui/showcase/web/index.html b/mobile/packages/ui/showcase/web/index.html deleted file mode 100644 index abf42ad1fd..0000000000 --- a/mobile/packages/ui/showcase/web/index.html +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - - - - - - - - - - - @immich/ui - - - - - - diff --git a/mobile/packages/ui/showcase/web/manifest.json b/mobile/packages/ui/showcase/web/manifest.json deleted file mode 100644 index 25b44bd1ae..0000000000 --- a/mobile/packages/ui/showcase/web/manifest.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "name": "@immich/ui Showcase", - "short_name": "@immich/ui", - "start_url": ".", - "display": "standalone", - "background_color": "#FCFCFD", - "theme_color": "#4250AF", - "description": "Immich UI component library showcase and documentation", - "orientation": "landscape", - "prefer_related_applications": false, - "icons": [ - { - "src": "icons/Icon-maskable-192.png", - "sizes": "192x192", - "type": "image/png", - "purpose": "any" - }, - { - "src": "icons/Icon-maskable-192.png", - "sizes": "192x192", - "type": "image/png", - "purpose": "maskable" - }, - { - "src": "icons/Icon-maskable-512.png", - "sizes": "512x512", - "type": "image/png", - "purpose": "any" - }, - { - "src": "icons/Icon-maskable-512.png", - "sizes": "512x512", - "type": "image/png", - "purpose": "maskable" - } - ] -} diff --git a/mobile/pubspec.lock b/mobile/pubspec.lock index 2708e80c78..72bdc6b298 100644 --- a/mobile/pubspec.lock +++ b/mobile/pubspec.lock @@ -1997,4 +1997,4 @@ packages: version: "3.1.3" sdks: dart: ">=3.12.0 <4.0.0" - flutter: "3.44.0" + flutter: "3.44.1" diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index 5070cd3af9..206f9ac2b3 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -6,7 +6,7 @@ version: 3.0.0+3047 environment: sdk: '>=3.12.0 <4.0.0' - flutter: 3.44.0 + flutter: 3.44.1 dependencies: async: ^2.13.1 diff --git a/mobile/test/domain/services/log_service_test.dart b/mobile/test/domain/services/log_service_test.dart index f442b9514c..6a82d1dce3 100644 --- a/mobile/test/domain/services/log_service_test.dart +++ b/mobile/test/domain/services/log_service_test.dart @@ -3,7 +3,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:immich_mobile/constants/constants.dart'; import 'package:immich_mobile/domain/models/config/app_config.dart'; import 'package:immich_mobile/domain/models/log.model.dart'; -import 'package:immich_mobile/domain/models/metadata_key.dart'; +import 'package:immich_mobile/domain/models/settings_key.dart'; import 'package:immich_mobile/domain/services/log.service.dart'; import 'package:immich_mobile/infrastructure/repositories/log.repository.dart'; import 'package:logging/logging.dart'; @@ -29,23 +29,23 @@ final _kWarnLog = LogMessage( void main() { late LogService sut; late LogRepository mockLogRepo; - late MockMetadataRepository mockMetadataRepository; + late MockSettingsRepository mockSettingsRepository; setUp(() async { mockLogRepo = MockLogRepository(); - mockMetadataRepository = MockMetadataRepository(); + mockSettingsRepository = MockSettingsRepository(); registerFallbackValue(_kInfoLog); registerFallbackValue(LogLevel.info); when(() => mockLogRepo.truncate(limit: any(named: 'limit'))).thenAnswer((_) async => {}); - when(() => mockMetadataRepository.appConfig).thenReturn(const AppConfig(logLevel: LogLevel.fine)); - when(() => mockMetadataRepository.write(MetadataKey.logLevel, any())).thenAnswer((_) async {}); + when(() => mockSettingsRepository.appConfig).thenReturn(const AppConfig(logLevel: LogLevel.fine)); + when(() => mockSettingsRepository.write(SettingsKey.logLevel, any())).thenAnswer((_) async {}); 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, metadataRepository: mockMetadataRepository); + sut = await LogService.create(logRepository: mockLogRepo, settingsRepository: mockSettingsRepository); }); tearDown(() async { @@ -59,7 +59,7 @@ void main() { }); test('Sets log level based on the metadata repository', () { - verify(() => mockMetadataRepository.appConfig).called(1); + verify(() => mockSettingsRepository.appConfig).called(1); expect(Logger.root.level, Level.FINE); }); }); @@ -71,7 +71,7 @@ void main() { test('Updates the log level via metadata repository', () { final captured = verify( - () => mockMetadataRepository.write(MetadataKey.logLevel, captureAny()), + () => mockSettingsRepository.write(SettingsKey.logLevel, captureAny()), ).captured.firstOrNull; expect(captured, LogLevel.shout); }); @@ -86,7 +86,7 @@ void main() { TestUtils.fakeAsync((time) async { sut = await LogService.create( logRepository: mockLogRepo, - metadataRepository: mockMetadataRepository, + settingsRepository: mockSettingsRepository, shouldBuffer: true, ); @@ -104,7 +104,7 @@ void main() { TestUtils.fakeAsync((time) async { sut = await LogService.create( logRepository: mockLogRepo, - metadataRepository: mockMetadataRepository, + settingsRepository: mockSettingsRepository, shouldBuffer: true, ); @@ -125,7 +125,7 @@ void main() { TestUtils.fakeAsync((time) async { sut = await LogService.create( logRepository: mockLogRepo, - metadataRepository: mockMetadataRepository, + settingsRepository: mockSettingsRepository, shouldBuffer: false, ); @@ -159,7 +159,7 @@ void main() { TestUtils.fakeAsync((time) async { sut = await LogService.create( logRepository: mockLogRepo, - metadataRepository: mockMetadataRepository, + settingsRepository: mockSettingsRepository, shouldBuffer: true, ); diff --git a/mobile/test/domain/services/sync_stream_service_test.dart b/mobile/test/domain/services/sync_stream_service_test.dart index ef29997e0b..80272d9310 100644 --- a/mobile/test/domain/services/sync_stream_service_test.dart +++ b/mobile/test/domain/services/sync_stream_service_test.dart @@ -116,7 +116,7 @@ void main() { when(() => mockApi.serverInfoApi).thenReturn(mockServerApi); when( () => mockServerApi.getServerVersion(), - ).thenAnswer((_) async => ServerVersionResponseDto(major: 1, minor: 132, patch_: 0)); + ).thenAnswer((_) async => ServerVersionResponseDto(major: 1, minor: 132, patch_: 0, prerelease: null)); when(() => mockSyncStreamRepo.updateUsersV1(any())).thenAnswer(successHandler); when(() => mockSyncStreamRepo.deleteUsersV1(any())).thenAnswer(successHandler); @@ -559,7 +559,7 @@ void main() { await Store.put(StoreKey.syncMigrationStatus, "[]"); when( () => mockServerApi.getServerVersion(), - ).thenAnswer((_) async => ServerVersionResponseDto(major: 2, minor: 4, patch_: 1)); + ).thenAnswer((_) async => ServerVersionResponseDto(major: 2, minor: 4, patch_: 1, prerelease: null)); await sut.sync(); @@ -587,7 +587,7 @@ void main() { await Store.put(StoreKey.syncMigrationStatus, "[]"); when( () => mockServerApi.getServerVersion(), - ).thenAnswer((_) async => ServerVersionResponseDto(major: 2, minor: 5, patch_: 0)); + ).thenAnswer((_) async => ServerVersionResponseDto(major: 2, minor: 5, patch_: 0, prerelease: null)); await sut.sync(); verifyInOrder([ @@ -617,7 +617,7 @@ void main() { when( () => mockServerApi.getServerVersion(), - ).thenAnswer((_) async => ServerVersionResponseDto(major: 2, minor: 4, patch_: 1)); + ).thenAnswer((_) async => ServerVersionResponseDto(major: 2, minor: 4, patch_: 1, prerelease: null)); await sut.sync(); diff --git a/mobile/test/drift/main/generated/schema.dart b/mobile/test/drift/main/generated/schema.dart index a1bae8f6dd..c5d57e9a4b 100644 --- a/mobile/test/drift/main/generated/schema.dart +++ b/mobile/test/drift/main/generated/schema.dart @@ -30,6 +30,7 @@ import 'schema_v23.dart' as v23; import 'schema_v24.dart' as v24; import 'schema_v25.dart' as v25; import 'schema_v26.dart' as v26; +import 'schema_v27.dart' as v27; class GeneratedHelper implements SchemaInstantiationHelper { @override @@ -87,6 +88,8 @@ class GeneratedHelper implements SchemaInstantiationHelper { return v25.DatabaseAtV25(db); case 26: return v26.DatabaseAtV26(db); + case 27: + return v27.DatabaseAtV27(db); default: throw MissingSchemaException(version, versions); } @@ -119,5 +122,6 @@ class GeneratedHelper implements SchemaInstantiationHelper { 24, 25, 26, + 27, ]; } diff --git a/mobile/test/drift/main/generated/schema_v27.dart b/mobile/test/drift/main/generated/schema_v27.dart new file mode 100644 index 0000000000..2b02946175 --- /dev/null +++ b/mobile/test/drift/main/generated/schema_v27.dart @@ -0,0 +1,9384 @@ +// dart format width=80 +import 'dart:typed_data' as i2; +// GENERATED BY drift_dev, DO NOT MODIFY. +// ignore_for_file: type=lint,unused_import +// +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, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn email = GeneratedColumn( + 'email', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn hasProfileImage = GeneratedColumn( + 'has_profile_image', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT 0 CHECK (has_profile_image IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn profileChangedAt = GeneratedColumn( + 'profile_changed_at', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT CURRENT_TIMESTAMP', + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn avatarColor = GeneratedColumn( + 'avatar_color', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0', + 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.int, + data['${effectivePrefix}has_profile_image'], + )!, + profileChangedAt: attachedDatabase.typeMapping.read( + DriftSqlType.string, + 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; + @override + List get customConstraints => const ['PRIMARY KEY(id)']; + @override + bool get dontWriteConstraints => true; +} + +class UserEntityData extends DataClass implements Insertable { + final String id; + final String name; + final String email; + final int hasProfileImage; + final String 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, + int? hasProfileImage, + String? 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, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT CURRENT_TIMESTAMP', + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT CURRENT_TIMESTAMP', + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn durationMs = GeneratedColumn( + 'duration_ms', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0 CHECK (is_favorite IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL REFERENCES user_entity(id)ON DELETE CASCADE', + ); + late final GeneratedColumn localDateTime = GeneratedColumn( + 'local_date_time', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn thumbHash = GeneratedColumn( + 'thumb_hash', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn uploadedAt = GeneratedColumn( + 'uploaded_at', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn livePhotoVideoId = GeneratedColumn( + 'live_photo_video_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn visibility = GeneratedColumn( + 'visibility', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn stackId = GeneratedColumn( + 'stack_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn libraryId = GeneratedColumn( + 'library_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn isEdited = GeneratedColumn( + 'is_edited', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0 CHECK (is_edited IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + @override + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationMs, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + uploadedAt, + livePhotoVideoId, + visibility, + stackId, + libraryId, + isEdited, + ]; + @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.string, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationMs: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_ms'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + )!, + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}is_favorite'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + localDateTime: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}local_date_time'], + ), + thumbHash: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumb_hash'], + ), + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}deleted_at'], + ), + uploadedAt: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}uploaded_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'], + ), + isEdited: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}is_edited'], + )!, + ); + } + + @override + RemoteAssetEntity createAlias(String alias) { + return RemoteAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; + @override + List get customConstraints => const ['PRIMARY KEY(id)']; + @override + bool get dontWriteConstraints => true; +} + +class RemoteAssetEntityData extends DataClass + implements Insertable { + final String name; + final int type; + final String createdAt; + final String updatedAt; + final int? width; + final int? height; + final int? durationMs; + final String id; + final String checksum; + final int isFavorite; + final String ownerId; + final String? localDateTime; + final String? thumbHash; + final String? deletedAt; + final String? uploadedAt; + final String? livePhotoVideoId; + final int visibility; + final String? stackId; + final String? libraryId; + final int isEdited; + const RemoteAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationMs, + required this.id, + required this.checksum, + required this.isFavorite, + required this.ownerId, + this.localDateTime, + this.thumbHash, + this.deletedAt, + this.uploadedAt, + this.livePhotoVideoId, + required this.visibility, + this.stackId, + this.libraryId, + required this.isEdited, + }); + @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 || durationMs != null) { + map['duration_ms'] = Variable(durationMs); + } + 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 || uploadedAt != null) { + map['uploaded_at'] = Variable(uploadedAt); + } + 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); + } + map['is_edited'] = Variable(isEdited); + 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']), + durationMs: serializer.fromJson(json['durationMs']), + 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']), + uploadedAt: serializer.fromJson(json['uploadedAt']), + livePhotoVideoId: serializer.fromJson(json['livePhotoVideoId']), + visibility: serializer.fromJson(json['visibility']), + stackId: serializer.fromJson(json['stackId']), + libraryId: serializer.fromJson(json['libraryId']), + isEdited: serializer.fromJson(json['isEdited']), + ); + } + @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), + 'durationMs': serializer.toJson(durationMs), + '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), + 'uploadedAt': serializer.toJson(uploadedAt), + 'livePhotoVideoId': serializer.toJson(livePhotoVideoId), + 'visibility': serializer.toJson(visibility), + 'stackId': serializer.toJson(stackId), + 'libraryId': serializer.toJson(libraryId), + 'isEdited': serializer.toJson(isEdited), + }; + } + + RemoteAssetEntityData copyWith({ + String? name, + int? type, + String? createdAt, + String? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationMs = const Value.absent(), + String? id, + String? checksum, + int? isFavorite, + String? ownerId, + Value localDateTime = const Value.absent(), + Value thumbHash = const Value.absent(), + Value deletedAt = const Value.absent(), + Value uploadedAt = const Value.absent(), + Value livePhotoVideoId = const Value.absent(), + int? visibility, + Value stackId = const Value.absent(), + Value libraryId = const Value.absent(), + int? isEdited, + }) => 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, + durationMs: durationMs.present ? durationMs.value : this.durationMs, + 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, + uploadedAt: uploadedAt.present ? uploadedAt.value : this.uploadedAt, + 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, + isEdited: isEdited ?? this.isEdited, + ); + 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, + durationMs: data.durationMs.present + ? data.durationMs.value + : this.durationMs, + 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, + uploadedAt: data.uploadedAt.present + ? data.uploadedAt.value + : this.uploadedAt, + 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, + isEdited: data.isEdited.present ? data.isEdited.value : this.isEdited, + ); + } + + @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('durationMs: $durationMs, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('ownerId: $ownerId, ') + ..write('localDateTime: $localDateTime, ') + ..write('thumbHash: $thumbHash, ') + ..write('deletedAt: $deletedAt, ') + ..write('uploadedAt: $uploadedAt, ') + ..write('livePhotoVideoId: $livePhotoVideoId, ') + ..write('visibility: $visibility, ') + ..write('stackId: $stackId, ') + ..write('libraryId: $libraryId, ') + ..write('isEdited: $isEdited') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationMs, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + uploadedAt, + livePhotoVideoId, + visibility, + stackId, + libraryId, + isEdited, + ); + @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.durationMs == this.durationMs && + 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.uploadedAt == this.uploadedAt && + other.livePhotoVideoId == this.livePhotoVideoId && + other.visibility == this.visibility && + other.stackId == this.stackId && + other.libraryId == this.libraryId && + other.isEdited == this.isEdited); +} + +class RemoteAssetEntityCompanion + extends UpdateCompanion { + final Value name; + final Value type; + final Value createdAt; + final Value updatedAt; + final Value width; + final Value height; + final Value durationMs; + final Value id; + final Value checksum; + final Value isFavorite; + final Value ownerId; + final Value localDateTime; + final Value thumbHash; + final Value deletedAt; + final Value uploadedAt; + final Value livePhotoVideoId; + final Value visibility; + final Value stackId; + final Value libraryId; + final Value isEdited; + 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.durationMs = 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.uploadedAt = const Value.absent(), + this.livePhotoVideoId = const Value.absent(), + this.visibility = const Value.absent(), + this.stackId = const Value.absent(), + this.libraryId = const Value.absent(), + this.isEdited = 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.durationMs = 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.uploadedAt = const Value.absent(), + this.livePhotoVideoId = const Value.absent(), + required int visibility, + this.stackId = const Value.absent(), + this.libraryId = const Value.absent(), + this.isEdited = 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? durationMs, + Expression? id, + Expression? checksum, + Expression? isFavorite, + Expression? ownerId, + Expression? localDateTime, + Expression? thumbHash, + Expression? deletedAt, + Expression? uploadedAt, + Expression? livePhotoVideoId, + Expression? visibility, + Expression? stackId, + Expression? libraryId, + Expression? isEdited, + }) { + 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 (durationMs != null) 'duration_ms': durationMs, + 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 (uploadedAt != null) 'uploaded_at': uploadedAt, + 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, + if (isEdited != null) 'is_edited': isEdited, + }); + } + + RemoteAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationMs, + Value? id, + Value? checksum, + Value? isFavorite, + Value? ownerId, + Value? localDateTime, + Value? thumbHash, + Value? deletedAt, + Value? uploadedAt, + Value? livePhotoVideoId, + Value? visibility, + Value? stackId, + Value? libraryId, + Value? isEdited, + }) { + 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, + durationMs: durationMs ?? this.durationMs, + 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, + uploadedAt: uploadedAt ?? this.uploadedAt, + livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId ?? this.stackId, + libraryId: libraryId ?? this.libraryId, + isEdited: isEdited ?? this.isEdited, + ); + } + + @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 (durationMs.present) { + map['duration_ms'] = Variable(durationMs.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 (uploadedAt.present) { + map['uploaded_at'] = Variable(uploadedAt.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); + } + if (isEdited.present) { + map['is_edited'] = Variable(isEdited.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('durationMs: $durationMs, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('ownerId: $ownerId, ') + ..write('localDateTime: $localDateTime, ') + ..write('thumbHash: $thumbHash, ') + ..write('deletedAt: $deletedAt, ') + ..write('uploadedAt: $uploadedAt, ') + ..write('livePhotoVideoId: $livePhotoVideoId, ') + ..write('visibility: $visibility, ') + ..write('stackId: $stackId, ') + ..write('libraryId: $libraryId, ') + ..write('isEdited: $isEdited') + ..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, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT CURRENT_TIMESTAMP', + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT CURRENT_TIMESTAMP', + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL REFERENCES user_entity(id)ON DELETE CASCADE', + ); + late final GeneratedColumn primaryAssetId = GeneratedColumn( + 'primary_asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + @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.string, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.string, + 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; + @override + List get customConstraints => const ['PRIMARY KEY(id)']; + @override + bool get dontWriteConstraints => true; +} + +class StackEntityData extends DataClass implements Insertable { + final String id; + final String createdAt; + final String 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, + String? createdAt, + String? 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, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT CURRENT_TIMESTAMP', + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT CURRENT_TIMESTAMP', + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn durationMs = GeneratedColumn( + 'duration_ms', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0 CHECK (is_favorite IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn iCloudId = GeneratedColumn( + 'i_cloud_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn adjustmentTime = GeneratedColumn( + 'adjustment_time', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn latitude = GeneratedColumn( + 'latitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn longitude = GeneratedColumn( + 'longitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn playbackStyle = GeneratedColumn( + 'playback_style', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0', + defaultValue: const CustomExpression('0'), + ); + @override + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationMs, + id, + checksum, + isFavorite, + orientation, + iCloudId, + adjustmentTime, + latitude, + longitude, + playbackStyle, + ]; + @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.string, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationMs: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_ms'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + ), + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}is_favorite'], + )!, + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}orientation'], + )!, + iCloudId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}i_cloud_id'], + ), + adjustmentTime: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}adjustment_time'], + ), + latitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}latitude'], + ), + longitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}longitude'], + ), + playbackStyle: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}playback_style'], + )!, + ); + } + + @override + LocalAssetEntity createAlias(String alias) { + return LocalAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; + @override + List get customConstraints => const ['PRIMARY KEY(id)']; + @override + bool get dontWriteConstraints => true; +} + +class LocalAssetEntityData extends DataClass + implements Insertable { + final String name; + final int type; + final String createdAt; + final String updatedAt; + final int? width; + final int? height; + final int? durationMs; + final String id; + final String? checksum; + final int isFavorite; + final int orientation; + final String? iCloudId; + final String? adjustmentTime; + final double? latitude; + final double? longitude; + final int playbackStyle; + const LocalAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationMs, + required this.id, + this.checksum, + required this.isFavorite, + required this.orientation, + this.iCloudId, + this.adjustmentTime, + this.latitude, + this.longitude, + required this.playbackStyle, + }); + @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 || durationMs != null) { + map['duration_ms'] = Variable(durationMs); + } + map['id'] = Variable(id); + if (!nullToAbsent || checksum != null) { + map['checksum'] = Variable(checksum); + } + map['is_favorite'] = Variable(isFavorite); + map['orientation'] = Variable(orientation); + if (!nullToAbsent || iCloudId != null) { + map['i_cloud_id'] = Variable(iCloudId); + } + if (!nullToAbsent || adjustmentTime != null) { + map['adjustment_time'] = Variable(adjustmentTime); + } + if (!nullToAbsent || latitude != null) { + map['latitude'] = Variable(latitude); + } + if (!nullToAbsent || longitude != null) { + map['longitude'] = Variable(longitude); + } + map['playback_style'] = Variable(playbackStyle); + 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']), + durationMs: serializer.fromJson(json['durationMs']), + id: serializer.fromJson(json['id']), + checksum: serializer.fromJson(json['checksum']), + isFavorite: serializer.fromJson(json['isFavorite']), + orientation: serializer.fromJson(json['orientation']), + iCloudId: serializer.fromJson(json['iCloudId']), + adjustmentTime: serializer.fromJson(json['adjustmentTime']), + latitude: serializer.fromJson(json['latitude']), + longitude: serializer.fromJson(json['longitude']), + playbackStyle: serializer.fromJson(json['playbackStyle']), + ); + } + @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), + 'durationMs': serializer.toJson(durationMs), + 'id': serializer.toJson(id), + 'checksum': serializer.toJson(checksum), + 'isFavorite': serializer.toJson(isFavorite), + 'orientation': serializer.toJson(orientation), + 'iCloudId': serializer.toJson(iCloudId), + 'adjustmentTime': serializer.toJson(adjustmentTime), + 'latitude': serializer.toJson(latitude), + 'longitude': serializer.toJson(longitude), + 'playbackStyle': serializer.toJson(playbackStyle), + }; + } + + LocalAssetEntityData copyWith({ + String? name, + int? type, + String? createdAt, + String? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationMs = const Value.absent(), + String? id, + Value checksum = const Value.absent(), + int? isFavorite, + int? orientation, + Value iCloudId = const Value.absent(), + Value adjustmentTime = const Value.absent(), + Value latitude = const Value.absent(), + Value longitude = const Value.absent(), + int? playbackStyle, + }) => 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, + durationMs: durationMs.present ? durationMs.value : this.durationMs, + id: id ?? this.id, + checksum: checksum.present ? checksum.value : this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + iCloudId: iCloudId.present ? iCloudId.value : this.iCloudId, + adjustmentTime: adjustmentTime.present + ? adjustmentTime.value + : this.adjustmentTime, + latitude: latitude.present ? latitude.value : this.latitude, + longitude: longitude.present ? longitude.value : this.longitude, + playbackStyle: playbackStyle ?? this.playbackStyle, + ); + 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, + durationMs: data.durationMs.present + ? data.durationMs.value + : this.durationMs, + 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, + iCloudId: data.iCloudId.present ? data.iCloudId.value : this.iCloudId, + adjustmentTime: data.adjustmentTime.present + ? data.adjustmentTime.value + : this.adjustmentTime, + latitude: data.latitude.present ? data.latitude.value : this.latitude, + longitude: data.longitude.present ? data.longitude.value : this.longitude, + playbackStyle: data.playbackStyle.present + ? data.playbackStyle.value + : this.playbackStyle, + ); + } + + @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('durationMs: $durationMs, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('orientation: $orientation, ') + ..write('iCloudId: $iCloudId, ') + ..write('adjustmentTime: $adjustmentTime, ') + ..write('latitude: $latitude, ') + ..write('longitude: $longitude, ') + ..write('playbackStyle: $playbackStyle') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationMs, + id, + checksum, + isFavorite, + orientation, + iCloudId, + adjustmentTime, + latitude, + longitude, + playbackStyle, + ); + @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.durationMs == this.durationMs && + other.id == this.id && + other.checksum == this.checksum && + other.isFavorite == this.isFavorite && + other.orientation == this.orientation && + other.iCloudId == this.iCloudId && + other.adjustmentTime == this.adjustmentTime && + other.latitude == this.latitude && + other.longitude == this.longitude && + other.playbackStyle == this.playbackStyle); +} + +class LocalAssetEntityCompanion extends UpdateCompanion { + final Value name; + final Value type; + final Value createdAt; + final Value updatedAt; + final Value width; + final Value height; + final Value durationMs; + final Value id; + final Value checksum; + final Value isFavorite; + final Value orientation; + final Value iCloudId; + final Value adjustmentTime; + final Value latitude; + final Value longitude; + final Value playbackStyle; + 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.durationMs = const Value.absent(), + this.id = const Value.absent(), + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.orientation = const Value.absent(), + this.iCloudId = const Value.absent(), + this.adjustmentTime = const Value.absent(), + this.latitude = const Value.absent(), + this.longitude = const Value.absent(), + this.playbackStyle = 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.durationMs = const Value.absent(), + required String id, + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.orientation = const Value.absent(), + this.iCloudId = const Value.absent(), + this.adjustmentTime = const Value.absent(), + this.latitude = const Value.absent(), + this.longitude = const Value.absent(), + this.playbackStyle = 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? durationMs, + Expression? id, + Expression? checksum, + Expression? isFavorite, + Expression? orientation, + Expression? iCloudId, + Expression? adjustmentTime, + Expression? latitude, + Expression? longitude, + Expression? playbackStyle, + }) { + 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 (durationMs != null) 'duration_ms': durationMs, + if (id != null) 'id': id, + if (checksum != null) 'checksum': checksum, + if (isFavorite != null) 'is_favorite': isFavorite, + if (orientation != null) 'orientation': orientation, + if (iCloudId != null) 'i_cloud_id': iCloudId, + if (adjustmentTime != null) 'adjustment_time': adjustmentTime, + if (latitude != null) 'latitude': latitude, + if (longitude != null) 'longitude': longitude, + if (playbackStyle != null) 'playback_style': playbackStyle, + }); + } + + LocalAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationMs, + Value? id, + Value? checksum, + Value? isFavorite, + Value? orientation, + Value? iCloudId, + Value? adjustmentTime, + Value? latitude, + Value? longitude, + Value? playbackStyle, + }) { + 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, + durationMs: durationMs ?? this.durationMs, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + iCloudId: iCloudId ?? this.iCloudId, + adjustmentTime: adjustmentTime ?? this.adjustmentTime, + latitude: latitude ?? this.latitude, + longitude: longitude ?? this.longitude, + playbackStyle: playbackStyle ?? this.playbackStyle, + ); + } + + @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 (durationMs.present) { + map['duration_ms'] = Variable(durationMs.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); + } + if (iCloudId.present) { + map['i_cloud_id'] = Variable(iCloudId.value); + } + if (adjustmentTime.present) { + map['adjustment_time'] = Variable(adjustmentTime.value); + } + if (latitude.present) { + map['latitude'] = Variable(latitude.value); + } + if (longitude.present) { + map['longitude'] = Variable(longitude.value); + } + if (playbackStyle.present) { + map['playback_style'] = Variable(playbackStyle.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('durationMs: $durationMs, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('orientation: $orientation, ') + ..write('iCloudId: $iCloudId, ') + ..write('adjustmentTime: $adjustmentTime, ') + ..write('latitude: $latitude, ') + ..write('longitude: $longitude, ') + ..write('playbackStyle: $playbackStyle') + ..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, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT \'\'', + defaultValue: const CustomExpression('\'\''), + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT CURRENT_TIMESTAMP', + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT CURRENT_TIMESTAMP', + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn thumbnailAssetId = GeneratedColumn( + 'thumbnail_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: + 'NULL REFERENCES remote_asset_entity(id)ON DELETE SET NULL', + ); + late final GeneratedColumn isActivityEnabled = GeneratedColumn( + 'is_activity_enabled', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT 1 CHECK (is_activity_enabled IN (0, 1))', + defaultValue: const CustomExpression('1'), + ); + late final GeneratedColumn order = GeneratedColumn( + 'order', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + @override + List get $columns => [ + id, + name, + description, + createdAt, + updatedAt, + 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.string, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}updated_at'], + )!, + thumbnailAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumbnail_asset_id'], + ), + isActivityEnabled: attachedDatabase.typeMapping.read( + DriftSqlType.int, + 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; + @override + List get customConstraints => const ['PRIMARY KEY(id)']; + @override + bool get dontWriteConstraints => true; +} + +class RemoteAlbumEntityData extends DataClass + implements Insertable { + final String id; + final String name; + final String description; + final String createdAt; + final String updatedAt; + final String? thumbnailAssetId; + final int isActivityEnabled; + final int order; + const RemoteAlbumEntityData({ + required this.id, + required this.name, + required this.description, + required this.createdAt, + required this.updatedAt, + 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); + 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']), + 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), + 'thumbnailAssetId': serializer.toJson(thumbnailAssetId), + 'isActivityEnabled': serializer.toJson(isActivityEnabled), + 'order': serializer.toJson(order), + }; + } + + RemoteAlbumEntityData copyWith({ + String? id, + String? name, + String? description, + String? createdAt, + String? updatedAt, + Value thumbnailAssetId = const Value.absent(), + int? isActivityEnabled, + int? order, + }) => RemoteAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + 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, + 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('thumbnailAssetId: $thumbnailAssetId, ') + ..write('isActivityEnabled: $isActivityEnabled, ') + ..write('order: $order') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + description, + createdAt, + updatedAt, + 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.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 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.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(), + this.thumbnailAssetId = const Value.absent(), + this.isActivityEnabled = const Value.absent(), + required int order, + }) : id = Value(id), + name = Value(name), + order = Value(order); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? description, + Expression? createdAt, + Expression? updatedAt, + 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 (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? 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, + 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 (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('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, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT CURRENT_TIMESTAMP', + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn backupSelection = GeneratedColumn( + 'backup_selection', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn isIosSharedAlbum = GeneratedColumn( + 'is_ios_shared_album', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT 0 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, + $customConstraints: + 'NULL REFERENCES remote_album_entity(id)ON DELETE SET NULL', + ); + late final GeneratedColumn marker = GeneratedColumn( + 'marker', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL 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.string, + data['${effectivePrefix}updated_at'], + )!, + backupSelection: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}backup_selection'], + )!, + isIosSharedAlbum: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}is_ios_shared_album'], + )!, + linkedRemoteAlbumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}linked_remote_album_id'], + ), + marker: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}marker'], + ), + ); + } + + @override + LocalAlbumEntity createAlias(String alias) { + return LocalAlbumEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; + @override + List get customConstraints => const ['PRIMARY KEY(id)']; + @override + bool get dontWriteConstraints => true; +} + +class LocalAlbumEntityData extends DataClass + implements Insertable { + final String id; + final String name; + final String updatedAt; + final int backupSelection; + final int isIosSharedAlbum; + final String? linkedRemoteAlbumId; + final int? 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, + String? updatedAt, + int? backupSelection, + int? 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, + $customConstraints: + 'NOT NULL REFERENCES local_asset_entity(id)ON DELETE CASCADE', + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: + 'NOT NULL REFERENCES local_album_entity(id)ON DELETE CASCADE', + ); + late final GeneratedColumn marker = GeneratedColumn( + 'marker', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL 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.int, + data['${effectivePrefix}marker'], + ), + ); + } + + @override + LocalAlbumAssetEntity createAlias(String alias) { + return LocalAlbumAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; + @override + List get customConstraints => const [ + 'PRIMARY KEY(asset_id, album_id)', + ]; + @override + bool get dontWriteConstraints => true; +} + +class LocalAlbumAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String albumId; + final int? 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, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn email = GeneratedColumn( + 'email', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn isAdmin = GeneratedColumn( + 'is_admin', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0 CHECK (is_admin IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn hasProfileImage = GeneratedColumn( + 'has_profile_image', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT 0 CHECK (has_profile_image IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn profileChangedAt = GeneratedColumn( + 'profile_changed_at', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT CURRENT_TIMESTAMP', + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn avatarColor = GeneratedColumn( + 'avatar_color', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn quotaSizeInBytes = GeneratedColumn( + 'quota_size_in_bytes', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn quotaUsageInBytes = GeneratedColumn( + 'quota_usage_in_bytes', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn pinCode = GeneratedColumn( + 'pin_code', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + @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.int, + data['${effectivePrefix}is_admin'], + )!, + hasProfileImage: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}has_profile_image'], + )!, + profileChangedAt: attachedDatabase.typeMapping.read( + DriftSqlType.string, + 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; + @override + List get customConstraints => const ['PRIMARY KEY(id)']; + @override + bool get dontWriteConstraints => true; +} + +class AuthUserEntityData extends DataClass + implements Insertable { + final String id; + final String name; + final String email; + final int isAdmin; + final int hasProfileImage; + final String 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, + int? isAdmin, + int? hasProfileImage, + String? 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, + $customConstraints: 'NOT NULL REFERENCES user_entity(id)ON DELETE CASCADE', + ); + late final GeneratedColumn key = GeneratedColumn( + 'key', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn value = + GeneratedColumn( + 'value', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + @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; + @override + List get customConstraints => const ['PRIMARY KEY(user_id, "key")']; + @override + bool get dontWriteConstraints => true; +} + +class UserMetadataEntityData extends DataClass + implements Insertable { + final String userId; + final int key; + final i2.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, + i2.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 i2.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, + $customConstraints: 'NOT NULL REFERENCES user_entity(id)ON DELETE CASCADE', + ); + late final GeneratedColumn sharedWithId = GeneratedColumn( + 'shared_with_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL REFERENCES user_entity(id)ON DELETE CASCADE', + ); + late final GeneratedColumn inTimeline = GeneratedColumn( + 'in_timeline', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0 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.int, + data['${effectivePrefix}in_timeline'], + )!, + ); + } + + @override + PartnerEntity createAlias(String alias) { + return PartnerEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; + @override + List get customConstraints => const [ + 'PRIMARY KEY(shared_by_id, shared_with_id)', + ]; + @override + bool get dontWriteConstraints => true; +} + +class PartnerEntityData extends DataClass + implements Insertable { + final String sharedById; + final String sharedWithId; + final int 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, + int? 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, + $customConstraints: + 'NOT NULL REFERENCES remote_asset_entity(id)ON DELETE CASCADE', + ); + late final GeneratedColumn city = GeneratedColumn( + 'city', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn state = GeneratedColumn( + 'state', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn country = GeneratedColumn( + 'country', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn dateTimeOriginal = GeneratedColumn( + 'date_time_original', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn exposureTime = GeneratedColumn( + 'exposure_time', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn fNumber = GeneratedColumn( + 'f_number', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn fileSize = GeneratedColumn( + 'file_size', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn focalLength = GeneratedColumn( + 'focal_length', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn latitude = GeneratedColumn( + 'latitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn longitude = GeneratedColumn( + 'longitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn iso = GeneratedColumn( + 'iso', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn make = GeneratedColumn( + 'make', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn model = GeneratedColumn( + 'model', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn lens = GeneratedColumn( + 'lens', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn timeZone = GeneratedColumn( + 'time_zone', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn rating = GeneratedColumn( + 'rating', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn projectionType = GeneratedColumn( + 'projection_type', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + @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.string, + 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; + @override + List get customConstraints => const ['PRIMARY KEY(asset_id)']; + @override + bool get dontWriteConstraints => true; +} + +class RemoteExifEntityData extends DataClass + implements Insertable { + final String assetId; + final String? city; + final String? state; + final String? country; + final String? 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, + $customConstraints: + 'NOT NULL REFERENCES remote_asset_entity(id)ON DELETE CASCADE', + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: + 'NOT NULL 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; + @override + List get customConstraints => const [ + 'PRIMARY KEY(asset_id, album_id)', + ]; + @override + bool get dontWriteConstraints => 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, + $customConstraints: + 'NOT NULL REFERENCES remote_album_entity(id)ON DELETE CASCADE', + ); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL REFERENCES user_entity(id)ON DELETE CASCADE', + ); + late final GeneratedColumn role = GeneratedColumn( + 'role', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + @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; + @override + List get customConstraints => const [ + 'PRIMARY KEY(album_id, user_id)', + ]; + @override + bool get dontWriteConstraints => 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 RemoteAssetCloudIdEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAssetCloudIdEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: + 'NOT NULL REFERENCES remote_asset_entity(id)ON DELETE CASCADE', + ); + late final GeneratedColumn cloudId = GeneratedColumn( + 'cloud_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn adjustmentTime = GeneratedColumn( + 'adjustment_time', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn latitude = GeneratedColumn( + 'latitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn longitude = GeneratedColumn( + 'longitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + @override + List get $columns => [ + assetId, + cloudId, + createdAt, + adjustmentTime, + latitude, + longitude, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_asset_cloud_id_entity'; + @override + Set get $primaryKey => {assetId}; + @override + RemoteAssetCloudIdEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAssetCloudIdEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + cloudId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}cloud_id'], + ), + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}created_at'], + ), + adjustmentTime: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}adjustment_time'], + ), + latitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}latitude'], + ), + longitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}longitude'], + ), + ); + } + + @override + RemoteAssetCloudIdEntity createAlias(String alias) { + return RemoteAssetCloudIdEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; + @override + List get customConstraints => const ['PRIMARY KEY(asset_id)']; + @override + bool get dontWriteConstraints => true; +} + +class RemoteAssetCloudIdEntityData extends DataClass + implements Insertable { + final String assetId; + final String? cloudId; + final String? createdAt; + final String? adjustmentTime; + final double? latitude; + final double? longitude; + const RemoteAssetCloudIdEntityData({ + required this.assetId, + this.cloudId, + this.createdAt, + this.adjustmentTime, + this.latitude, + this.longitude, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + if (!nullToAbsent || cloudId != null) { + map['cloud_id'] = Variable(cloudId); + } + if (!nullToAbsent || createdAt != null) { + map['created_at'] = Variable(createdAt); + } + if (!nullToAbsent || adjustmentTime != null) { + map['adjustment_time'] = Variable(adjustmentTime); + } + if (!nullToAbsent || latitude != null) { + map['latitude'] = Variable(latitude); + } + if (!nullToAbsent || longitude != null) { + map['longitude'] = Variable(longitude); + } + return map; + } + + factory RemoteAssetCloudIdEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAssetCloudIdEntityData( + assetId: serializer.fromJson(json['assetId']), + cloudId: serializer.fromJson(json['cloudId']), + createdAt: serializer.fromJson(json['createdAt']), + adjustmentTime: serializer.fromJson(json['adjustmentTime']), + latitude: serializer.fromJson(json['latitude']), + longitude: serializer.fromJson(json['longitude']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'cloudId': serializer.toJson(cloudId), + 'createdAt': serializer.toJson(createdAt), + 'adjustmentTime': serializer.toJson(adjustmentTime), + 'latitude': serializer.toJson(latitude), + 'longitude': serializer.toJson(longitude), + }; + } + + RemoteAssetCloudIdEntityData copyWith({ + String? assetId, + Value cloudId = const Value.absent(), + Value createdAt = const Value.absent(), + Value adjustmentTime = const Value.absent(), + Value latitude = const Value.absent(), + Value longitude = const Value.absent(), + }) => RemoteAssetCloudIdEntityData( + assetId: assetId ?? this.assetId, + cloudId: cloudId.present ? cloudId.value : this.cloudId, + createdAt: createdAt.present ? createdAt.value : this.createdAt, + adjustmentTime: adjustmentTime.present + ? adjustmentTime.value + : this.adjustmentTime, + latitude: latitude.present ? latitude.value : this.latitude, + longitude: longitude.present ? longitude.value : this.longitude, + ); + RemoteAssetCloudIdEntityData copyWithCompanion( + RemoteAssetCloudIdEntityCompanion data, + ) { + return RemoteAssetCloudIdEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + cloudId: data.cloudId.present ? data.cloudId.value : this.cloudId, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + adjustmentTime: data.adjustmentTime.present + ? data.adjustmentTime.value + : this.adjustmentTime, + latitude: data.latitude.present ? data.latitude.value : this.latitude, + longitude: data.longitude.present ? data.longitude.value : this.longitude, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAssetCloudIdEntityData(') + ..write('assetId: $assetId, ') + ..write('cloudId: $cloudId, ') + ..write('createdAt: $createdAt, ') + ..write('adjustmentTime: $adjustmentTime, ') + ..write('latitude: $latitude, ') + ..write('longitude: $longitude') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + assetId, + cloudId, + createdAt, + adjustmentTime, + latitude, + longitude, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAssetCloudIdEntityData && + other.assetId == this.assetId && + other.cloudId == this.cloudId && + other.createdAt == this.createdAt && + other.adjustmentTime == this.adjustmentTime && + other.latitude == this.latitude && + other.longitude == this.longitude); +} + +class RemoteAssetCloudIdEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value cloudId; + final Value createdAt; + final Value adjustmentTime; + final Value latitude; + final Value longitude; + const RemoteAssetCloudIdEntityCompanion({ + this.assetId = const Value.absent(), + this.cloudId = const Value.absent(), + this.createdAt = const Value.absent(), + this.adjustmentTime = const Value.absent(), + this.latitude = const Value.absent(), + this.longitude = const Value.absent(), + }); + RemoteAssetCloudIdEntityCompanion.insert({ + required String assetId, + this.cloudId = const Value.absent(), + this.createdAt = const Value.absent(), + this.adjustmentTime = const Value.absent(), + this.latitude = const Value.absent(), + this.longitude = const Value.absent(), + }) : assetId = Value(assetId); + static Insertable custom({ + Expression? assetId, + Expression? cloudId, + Expression? createdAt, + Expression? adjustmentTime, + Expression? latitude, + Expression? longitude, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (cloudId != null) 'cloud_id': cloudId, + if (createdAt != null) 'created_at': createdAt, + if (adjustmentTime != null) 'adjustment_time': adjustmentTime, + if (latitude != null) 'latitude': latitude, + if (longitude != null) 'longitude': longitude, + }); + } + + RemoteAssetCloudIdEntityCompanion copyWith({ + Value? assetId, + Value? cloudId, + Value? createdAt, + Value? adjustmentTime, + Value? latitude, + Value? longitude, + }) { + return RemoteAssetCloudIdEntityCompanion( + assetId: assetId ?? this.assetId, + cloudId: cloudId ?? this.cloudId, + createdAt: createdAt ?? this.createdAt, + adjustmentTime: adjustmentTime ?? this.adjustmentTime, + latitude: latitude ?? this.latitude, + longitude: longitude ?? this.longitude, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (cloudId.present) { + map['cloud_id'] = Variable(cloudId.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (adjustmentTime.present) { + map['adjustment_time'] = Variable(adjustmentTime.value); + } + if (latitude.present) { + map['latitude'] = Variable(latitude.value); + } + if (longitude.present) { + map['longitude'] = Variable(longitude.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAssetCloudIdEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('cloudId: $cloudId, ') + ..write('createdAt: $createdAt, ') + ..write('adjustmentTime: $adjustmentTime, ') + ..write('latitude: $latitude, ') + ..write('longitude: $longitude') + ..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, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT CURRENT_TIMESTAMP', + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT CURRENT_TIMESTAMP', + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL REFERENCES user_entity(id)ON DELETE CASCADE', + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn data = GeneratedColumn( + 'data', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn isSaved = GeneratedColumn( + 'is_saved', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0 CHECK (is_saved IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn memoryAt = GeneratedColumn( + 'memory_at', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn seenAt = GeneratedColumn( + 'seen_at', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn showAt = GeneratedColumn( + 'show_at', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn hideAt = GeneratedColumn( + 'hide_at', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + @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.string, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}updated_at'], + )!, + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.string, + 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.int, + data['${effectivePrefix}is_saved'], + )!, + memoryAt: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}memory_at'], + )!, + seenAt: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}seen_at'], + ), + showAt: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}show_at'], + ), + hideAt: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}hide_at'], + ), + ); + } + + @override + MemoryEntity createAlias(String alias) { + return MemoryEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; + @override + List get customConstraints => const ['PRIMARY KEY(id)']; + @override + bool get dontWriteConstraints => true; +} + +class MemoryEntityData extends DataClass + implements Insertable { + final String id; + final String createdAt; + final String updatedAt; + final String? deletedAt; + final String ownerId; + final int type; + final String data; + final int isSaved; + final String memoryAt; + final String? seenAt; + final String? showAt; + final String? 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, + String? createdAt, + String? updatedAt, + Value deletedAt = const Value.absent(), + String? ownerId, + int? type, + String? data, + int? isSaved, + String? 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 String 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, + $customConstraints: + 'NOT NULL REFERENCES remote_asset_entity(id)ON DELETE CASCADE', + ); + late final GeneratedColumn memoryId = GeneratedColumn( + 'memory_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: + 'NOT NULL 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; + @override + List get customConstraints => const [ + 'PRIMARY KEY(asset_id, memory_id)', + ]; + @override + bool get dontWriteConstraints => 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, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT CURRENT_TIMESTAMP', + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT CURRENT_TIMESTAMP', + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL REFERENCES user_entity(id)ON DELETE CASCADE', + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn faceAssetId = GeneratedColumn( + 'face_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL CHECK (is_favorite IN (0, 1))', + ); + late final GeneratedColumn isHidden = GeneratedColumn( + 'is_hidden', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL CHECK (is_hidden IN (0, 1))', + ); + late final GeneratedColumn color = GeneratedColumn( + 'color', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn birthDate = GeneratedColumn( + 'birth_date', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + @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.string, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.string, + 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.int, + data['${effectivePrefix}is_favorite'], + )!, + isHidden: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}is_hidden'], + )!, + color: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}color'], + ), + birthDate: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}birth_date'], + ), + ); + } + + @override + PersonEntity createAlias(String alias) { + return PersonEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; + @override + List get customConstraints => const ['PRIMARY KEY(id)']; + @override + bool get dontWriteConstraints => true; +} + +class PersonEntityData extends DataClass + implements Insertable { + final String id; + final String createdAt; + final String updatedAt; + final String ownerId; + final String name; + final String? faceAssetId; + final int isFavorite; + final int isHidden; + final String? color; + final String? 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, + String? createdAt, + String? updatedAt, + String? ownerId, + String? name, + Value faceAssetId = const Value.absent(), + int? isFavorite, + int? 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 int isFavorite, + required int 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, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: + 'NOT NULL REFERENCES remote_asset_entity(id)ON DELETE CASCADE', + ); + late final GeneratedColumn personId = GeneratedColumn( + 'person_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL REFERENCES person_entity(id)ON DELETE SET NULL', + ); + late final GeneratedColumn imageWidth = GeneratedColumn( + 'image_width', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn imageHeight = GeneratedColumn( + 'image_height', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn boundingBoxX1 = GeneratedColumn( + 'bounding_box_x1', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn boundingBoxY1 = GeneratedColumn( + 'bounding_box_y1', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn boundingBoxX2 = GeneratedColumn( + 'bounding_box_x2', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn boundingBoxY2 = GeneratedColumn( + 'bounding_box_y2', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn sourceType = GeneratedColumn( + 'source_type', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn isVisible = GeneratedColumn( + 'is_visible', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 1 CHECK (is_visible IN (0, 1))', + defaultValue: const CustomExpression('1'), + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + @override + List get $columns => [ + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType, + isVisible, + deletedAt, + ]; + @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'], + )!, + isVisible: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}is_visible'], + )!, + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}deleted_at'], + ), + ); + } + + @override + AssetFaceEntity createAlias(String alias) { + return AssetFaceEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; + @override + List get customConstraints => const ['PRIMARY KEY(id)']; + @override + bool get dontWriteConstraints => 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; + final int isVisible; + final String? deletedAt; + 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, + required this.isVisible, + this.deletedAt, + }); + @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); + map['is_visible'] = Variable(isVisible); + if (!nullToAbsent || deletedAt != null) { + map['deleted_at'] = Variable(deletedAt); + } + 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']), + isVisible: serializer.fromJson(json['isVisible']), + deletedAt: serializer.fromJson(json['deletedAt']), + ); + } + @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), + 'isVisible': serializer.toJson(isVisible), + 'deletedAt': serializer.toJson(deletedAt), + }; + } + + 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, + int? isVisible, + Value deletedAt = const Value.absent(), + }) => 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, + isVisible: isVisible ?? this.isVisible, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + ); + 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, + isVisible: data.isVisible.present ? data.isVisible.value : this.isVisible, + deletedAt: data.deletedAt.present ? data.deletedAt.value : this.deletedAt, + ); + } + + @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('isVisible: $isVisible, ') + ..write('deletedAt: $deletedAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType, + isVisible, + deletedAt, + ); + @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 && + other.isVisible == this.isVisible && + other.deletedAt == this.deletedAt); +} + +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; + final Value isVisible; + final Value deletedAt; + 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(), + this.isVisible = const Value.absent(), + this.deletedAt = 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, + this.isVisible = const Value.absent(), + this.deletedAt = const Value.absent(), + }) : 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, + Expression? isVisible, + Expression? deletedAt, + }) { + 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, + if (isVisible != null) 'is_visible': isVisible, + if (deletedAt != null) 'deleted_at': deletedAt, + }); + } + + AssetFaceEntityCompanion copyWith({ + Value? id, + Value? assetId, + Value? personId, + Value? imageWidth, + Value? imageHeight, + Value? boundingBoxX1, + Value? boundingBoxY1, + Value? boundingBoxX2, + Value? boundingBoxY2, + Value? sourceType, + Value? isVisible, + Value? deletedAt, + }) { + 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, + isVisible: isVisible ?? this.isVisible, + deletedAt: deletedAt ?? this.deletedAt, + ); + } + + @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); + } + if (isVisible.present) { + map['is_visible'] = Variable(isVisible.value); + } + if (deletedAt.present) { + map['deleted_at'] = Variable(deletedAt.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('isVisible: $isVisible, ') + ..write('deletedAt: $deletedAt') + ..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, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn stringValue = GeneratedColumn( + 'string_value', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn intValue = GeneratedColumn( + 'int_value', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + @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; + @override + List get customConstraints => const ['PRIMARY KEY(id)']; + @override + bool get dontWriteConstraints => 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 TrashedLocalAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + TrashedLocalAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT CURRENT_TIMESTAMP', + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT CURRENT_TIMESTAMP', + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn durationMs = GeneratedColumn( + 'duration_ms', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NULL', + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0 CHECK (is_favorite IN (0, 1))', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0', + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn source = GeneratedColumn( + 'source', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn playbackStyle = GeneratedColumn( + 'playback_style', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 0', + defaultValue: const CustomExpression('0'), + ); + @override + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationMs, + id, + albumId, + checksum, + isFavorite, + orientation, + source, + playbackStyle, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'trashed_local_asset_entity'; + @override + Set get $primaryKey => {id, albumId}; + @override + TrashedLocalAssetEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return TrashedLocalAssetEntityData( + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationMs: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_ms'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + ), + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}is_favorite'], + )!, + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}orientation'], + )!, + source: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}source'], + )!, + playbackStyle: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}playback_style'], + )!, + ); + } + + @override + TrashedLocalAssetEntity createAlias(String alias) { + return TrashedLocalAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; + @override + List get customConstraints => const ['PRIMARY KEY(id, album_id)']; + @override + bool get dontWriteConstraints => true; +} + +class TrashedLocalAssetEntityData extends DataClass + implements Insertable { + final String name; + final int type; + final String createdAt; + final String updatedAt; + final int? width; + final int? height; + final int? durationMs; + final String id; + final String albumId; + final String? checksum; + final int isFavorite; + final int orientation; + final int source; + final int playbackStyle; + const TrashedLocalAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationMs, + required this.id, + required this.albumId, + this.checksum, + required this.isFavorite, + required this.orientation, + required this.source, + required this.playbackStyle, + }); + @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 || durationMs != null) { + map['duration_ms'] = Variable(durationMs); + } + map['id'] = Variable(id); + map['album_id'] = Variable(albumId); + if (!nullToAbsent || checksum != null) { + map['checksum'] = Variable(checksum); + } + map['is_favorite'] = Variable(isFavorite); + map['orientation'] = Variable(orientation); + map['source'] = Variable(source); + map['playback_style'] = Variable(playbackStyle); + return map; + } + + factory TrashedLocalAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return TrashedLocalAssetEntityData( + 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']), + durationMs: serializer.fromJson(json['durationMs']), + id: serializer.fromJson(json['id']), + albumId: serializer.fromJson(json['albumId']), + checksum: serializer.fromJson(json['checksum']), + isFavorite: serializer.fromJson(json['isFavorite']), + orientation: serializer.fromJson(json['orientation']), + source: serializer.fromJson(json['source']), + playbackStyle: serializer.fromJson(json['playbackStyle']), + ); + } + @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), + 'durationMs': serializer.toJson(durationMs), + 'id': serializer.toJson(id), + 'albumId': serializer.toJson(albumId), + 'checksum': serializer.toJson(checksum), + 'isFavorite': serializer.toJson(isFavorite), + 'orientation': serializer.toJson(orientation), + 'source': serializer.toJson(source), + 'playbackStyle': serializer.toJson(playbackStyle), + }; + } + + TrashedLocalAssetEntityData copyWith({ + String? name, + int? type, + String? createdAt, + String? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationMs = const Value.absent(), + String? id, + String? albumId, + Value checksum = const Value.absent(), + int? isFavorite, + int? orientation, + int? source, + int? playbackStyle, + }) => TrashedLocalAssetEntityData( + 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, + durationMs: durationMs.present ? durationMs.value : this.durationMs, + id: id ?? this.id, + albumId: albumId ?? this.albumId, + checksum: checksum.present ? checksum.value : this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + source: source ?? this.source, + playbackStyle: playbackStyle ?? this.playbackStyle, + ); + TrashedLocalAssetEntityData copyWithCompanion( + TrashedLocalAssetEntityCompanion data, + ) { + return TrashedLocalAssetEntityData( + 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, + durationMs: data.durationMs.present + ? data.durationMs.value + : this.durationMs, + id: data.id.present ? data.id.value : this.id, + albumId: data.albumId.present ? data.albumId.value : this.albumId, + 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, + source: data.source.present ? data.source.value : this.source, + playbackStyle: data.playbackStyle.present + ? data.playbackStyle.value + : this.playbackStyle, + ); + } + + @override + String toString() { + return (StringBuffer('TrashedLocalAssetEntityData(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationMs: $durationMs, ') + ..write('id: $id, ') + ..write('albumId: $albumId, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('orientation: $orientation, ') + ..write('source: $source, ') + ..write('playbackStyle: $playbackStyle') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationMs, + id, + albumId, + checksum, + isFavorite, + orientation, + source, + playbackStyle, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is TrashedLocalAssetEntityData && + 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.durationMs == this.durationMs && + other.id == this.id && + other.albumId == this.albumId && + other.checksum == this.checksum && + other.isFavorite == this.isFavorite && + other.orientation == this.orientation && + other.source == this.source && + other.playbackStyle == this.playbackStyle); +} + +class TrashedLocalAssetEntityCompanion + extends UpdateCompanion { + final Value name; + final Value type; + final Value createdAt; + final Value updatedAt; + final Value width; + final Value height; + final Value durationMs; + final Value id; + final Value albumId; + final Value checksum; + final Value isFavorite; + final Value orientation; + final Value source; + final Value playbackStyle; + const TrashedLocalAssetEntityCompanion({ + 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.durationMs = const Value.absent(), + this.id = const Value.absent(), + this.albumId = const Value.absent(), + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.orientation = const Value.absent(), + this.source = const Value.absent(), + this.playbackStyle = const Value.absent(), + }); + TrashedLocalAssetEntityCompanion.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.durationMs = const Value.absent(), + required String id, + required String albumId, + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.orientation = const Value.absent(), + required int source, + this.playbackStyle = const Value.absent(), + }) : name = Value(name), + type = Value(type), + id = Value(id), + albumId = Value(albumId), + source = Value(source); + static Insertable custom({ + Expression? name, + Expression? type, + Expression? createdAt, + Expression? updatedAt, + Expression? width, + Expression? height, + Expression? durationMs, + Expression? id, + Expression? albumId, + Expression? checksum, + Expression? isFavorite, + Expression? orientation, + Expression? source, + Expression? playbackStyle, + }) { + 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 (durationMs != null) 'duration_ms': durationMs, + if (id != null) 'id': id, + if (albumId != null) 'album_id': albumId, + if (checksum != null) 'checksum': checksum, + if (isFavorite != null) 'is_favorite': isFavorite, + if (orientation != null) 'orientation': orientation, + if (source != null) 'source': source, + if (playbackStyle != null) 'playback_style': playbackStyle, + }); + } + + TrashedLocalAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationMs, + Value? id, + Value? albumId, + Value? checksum, + Value? isFavorite, + Value? orientation, + Value? source, + Value? playbackStyle, + }) { + return TrashedLocalAssetEntityCompanion( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width ?? this.width, + height: height ?? this.height, + durationMs: durationMs ?? this.durationMs, + id: id ?? this.id, + albumId: albumId ?? this.albumId, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + source: source ?? this.source, + playbackStyle: playbackStyle ?? this.playbackStyle, + ); + } + + @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 (durationMs.present) { + map['duration_ms'] = Variable(durationMs.value); + } + if (id.present) { + map['id'] = Variable(id.value); + } + if (albumId.present) { + map['album_id'] = Variable(albumId.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); + } + if (source.present) { + map['source'] = Variable(source.value); + } + if (playbackStyle.present) { + map['playback_style'] = Variable(playbackStyle.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('TrashedLocalAssetEntityCompanion(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationMs: $durationMs, ') + ..write('id: $id, ') + ..write('albumId: $albumId, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('orientation: $orientation, ') + ..write('source: $source, ') + ..write('playbackStyle: $playbackStyle') + ..write(')')) + .toString(); + } +} + +class AssetEditEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + AssetEditEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: + 'NOT NULL REFERENCES remote_asset_entity(id)ON DELETE CASCADE', + ); + late final GeneratedColumn action = GeneratedColumn( + 'action', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn parameters = + GeneratedColumn( + 'parameters', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn sequence = GeneratedColumn( + 'sequence', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + @override + List get $columns => [ + id, + assetId, + action, + parameters, + sequence, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'asset_edit_entity'; + @override + Set get $primaryKey => {id}; + @override + AssetEditEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return AssetEditEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + action: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}action'], + )!, + parameters: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}parameters'], + )!, + sequence: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}sequence'], + )!, + ); + } + + @override + AssetEditEntity createAlias(String alias) { + return AssetEditEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; + @override + List get customConstraints => const ['PRIMARY KEY(id)']; + @override + bool get dontWriteConstraints => true; +} + +class AssetEditEntityData extends DataClass + implements Insertable { + final String id; + final String assetId; + final int action; + final i2.Uint8List parameters; + final int sequence; + const AssetEditEntityData({ + required this.id, + required this.assetId, + required this.action, + required this.parameters, + required this.sequence, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['asset_id'] = Variable(assetId); + map['action'] = Variable(action); + map['parameters'] = Variable(parameters); + map['sequence'] = Variable(sequence); + return map; + } + + factory AssetEditEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return AssetEditEntityData( + id: serializer.fromJson(json['id']), + assetId: serializer.fromJson(json['assetId']), + action: serializer.fromJson(json['action']), + parameters: serializer.fromJson(json['parameters']), + sequence: serializer.fromJson(json['sequence']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'assetId': serializer.toJson(assetId), + 'action': serializer.toJson(action), + 'parameters': serializer.toJson(parameters), + 'sequence': serializer.toJson(sequence), + }; + } + + AssetEditEntityData copyWith({ + String? id, + String? assetId, + int? action, + i2.Uint8List? parameters, + int? sequence, + }) => AssetEditEntityData( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + action: action ?? this.action, + parameters: parameters ?? this.parameters, + sequence: sequence ?? this.sequence, + ); + AssetEditEntityData copyWithCompanion(AssetEditEntityCompanion data) { + return AssetEditEntityData( + id: data.id.present ? data.id.value : this.id, + assetId: data.assetId.present ? data.assetId.value : this.assetId, + action: data.action.present ? data.action.value : this.action, + parameters: data.parameters.present + ? data.parameters.value + : this.parameters, + sequence: data.sequence.present ? data.sequence.value : this.sequence, + ); + } + + @override + String toString() { + return (StringBuffer('AssetEditEntityData(') + ..write('id: $id, ') + ..write('assetId: $assetId, ') + ..write('action: $action, ') + ..write('parameters: $parameters, ') + ..write('sequence: $sequence') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + assetId, + action, + $driftBlobEquality.hash(parameters), + sequence, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is AssetEditEntityData && + other.id == this.id && + other.assetId == this.assetId && + other.action == this.action && + $driftBlobEquality.equals(other.parameters, this.parameters) && + other.sequence == this.sequence); +} + +class AssetEditEntityCompanion extends UpdateCompanion { + final Value id; + final Value assetId; + final Value action; + final Value parameters; + final Value sequence; + const AssetEditEntityCompanion({ + this.id = const Value.absent(), + this.assetId = const Value.absent(), + this.action = const Value.absent(), + this.parameters = const Value.absent(), + this.sequence = const Value.absent(), + }); + AssetEditEntityCompanion.insert({ + required String id, + required String assetId, + required int action, + required i2.Uint8List parameters, + required int sequence, + }) : id = Value(id), + assetId = Value(assetId), + action = Value(action), + parameters = Value(parameters), + sequence = Value(sequence); + static Insertable custom({ + Expression? id, + Expression? assetId, + Expression? action, + Expression? parameters, + Expression? sequence, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (assetId != null) 'asset_id': assetId, + if (action != null) 'action': action, + if (parameters != null) 'parameters': parameters, + if (sequence != null) 'sequence': sequence, + }); + } + + AssetEditEntityCompanion copyWith({ + Value? id, + Value? assetId, + Value? action, + Value? parameters, + Value? sequence, + }) { + return AssetEditEntityCompanion( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + action: action ?? this.action, + parameters: parameters ?? this.parameters, + sequence: sequence ?? this.sequence, + ); + } + + @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 (action.present) { + map['action'] = Variable(action.value); + } + if (parameters.present) { + map['parameters'] = Variable(parameters.value); + } + if (sequence.present) { + map['sequence'] = Variable(sequence.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('AssetEditEntityCompanion(') + ..write('id: $id, ') + ..write('assetId: $assetId, ') + ..write('action: $action, ') + ..write('parameters: $parameters, ') + ..write('sequence: $sequence') + ..write(')')) + .toString(); + } +} + +class Settings extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + Settings(this.attachedDatabase, [this._alias]); + late final GeneratedColumn key = GeneratedColumn( + 'key', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn value = GeneratedColumn( + 'value', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT CURRENT_TIMESTAMP', + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + @override + List get $columns => [key, value, updatedAt]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'settings'; + @override + Set get $primaryKey => {key}; + @override + SettingsData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return SettingsData( + key: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}key'], + )!, + value: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}value'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}updated_at'], + )!, + ); + } + + @override + Settings createAlias(String alias) { + return Settings(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; + @override + List get customConstraints => const ['PRIMARY KEY("key")']; + @override + bool get dontWriteConstraints => true; +} + +class SettingsData extends DataClass implements Insertable { + final String key; + final String value; + final String updatedAt; + const SettingsData({ + required this.key, + required this.value, + required this.updatedAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['key'] = Variable(key); + map['value'] = Variable(value); + map['updated_at'] = Variable(updatedAt); + return map; + } + + factory SettingsData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return SettingsData( + key: serializer.fromJson(json['key']), + value: serializer.fromJson(json['value']), + updatedAt: serializer.fromJson(json['updatedAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'key': serializer.toJson(key), + 'value': serializer.toJson(value), + 'updatedAt': serializer.toJson(updatedAt), + }; + } + + SettingsData copyWith({String? key, String? value, String? updatedAt}) => + SettingsData( + key: key ?? this.key, + value: value ?? this.value, + updatedAt: updatedAt ?? this.updatedAt, + ); + SettingsData copyWithCompanion(SettingsCompanion data) { + return SettingsData( + key: data.key.present ? data.key.value : this.key, + value: data.value.present ? data.value.value : this.value, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ); + } + + @override + String toString() { + return (StringBuffer('SettingsData(') + ..write('key: $key, ') + ..write('value: $value, ') + ..write('updatedAt: $updatedAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(key, value, updatedAt); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is SettingsData && + other.key == this.key && + other.value == this.value && + other.updatedAt == this.updatedAt); +} + +class SettingsCompanion extends UpdateCompanion { + final Value key; + final Value value; + final Value updatedAt; + const SettingsCompanion({ + this.key = const Value.absent(), + this.value = const Value.absent(), + this.updatedAt = const Value.absent(), + }); + SettingsCompanion.insert({ + required String key, + required String value, + this.updatedAt = const Value.absent(), + }) : key = Value(key), + value = Value(value); + static Insertable custom({ + Expression? key, + Expression? value, + Expression? updatedAt, + }) { + return RawValuesInsertable({ + if (key != null) 'key': key, + if (value != null) 'value': value, + if (updatedAt != null) 'updated_at': updatedAt, + }); + } + + SettingsCompanion copyWith({ + Value? key, + Value? value, + Value? updatedAt, + }) { + return SettingsCompanion( + key: key ?? this.key, + value: value ?? this.value, + updatedAt: updatedAt ?? this.updatedAt, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (key.present) { + map['key'] = Variable(key.value); + } + if (value.present) { + map['value'] = Variable(value.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('SettingsCompanion(') + ..write('key: $key, ') + ..write('value: $value, ') + ..write('updatedAt: $updatedAt') + ..write(')')) + .toString(); + } +} + +class DatabaseAtV27 extends GeneratedDatabase { + DatabaseAtV27(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 idxLocalAlbumAssetAlbumAsset = Index( + 'idx_local_album_asset_album_asset', + 'CREATE INDEX IF NOT EXISTS idx_local_album_asset_album_asset ON local_album_asset_entity (album_id, asset_id)', + ); + 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 idxLocalAssetCloudId = Index( + 'idx_local_asset_cloud_id', + 'CREATE INDEX IF NOT EXISTS idx_local_asset_cloud_id ON local_asset_entity (i_cloud_id)', + ); + late final Index idxStackPrimaryAssetId = Index( + 'idx_stack_primary_asset_id', + 'CREATE INDEX IF NOT EXISTS idx_stack_primary_asset_id ON stack_entity (primary_asset_id)', + ); + 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 Index idxRemoteAssetStackId = Index( + 'idx_remote_asset_stack_id', + 'CREATE INDEX IF NOT EXISTS idx_remote_asset_stack_id ON remote_asset_entity (stack_id)', + ); + late final Index idxRemoteAssetOwnerVisibilityDeletedCreated = Index( + 'idx_remote_asset_owner_visibility_deleted_created', + 'CREATE INDEX IF NOT EXISTS idx_remote_asset_owner_visibility_deleted_created ON remote_asset_entity (owner_id, visibility, deleted_at, created_at DESC)', + ); + 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 RemoteAssetCloudIdEntity remoteAssetCloudIdEntity = + RemoteAssetCloudIdEntity(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 TrashedLocalAssetEntity trashedLocalAssetEntity = + TrashedLocalAssetEntity(this); + late final AssetEditEntity assetEditEntity = AssetEditEntity(this); + late final Settings settings = Settings(this); + late final Index idxPartnerSharedWithId = Index( + 'idx_partner_shared_with_id', + 'CREATE INDEX IF NOT EXISTS idx_partner_shared_with_id ON partner_entity (shared_with_id)', + ); + late final Index idxLatLng = Index( + 'idx_lat_lng', + 'CREATE INDEX IF NOT EXISTS idx_lat_lng ON remote_exif_entity (latitude, longitude)', + ); + late final Index idxRemoteExifCity = Index( + 'idx_remote_exif_city', + 'CREATE INDEX IF NOT EXISTS idx_remote_exif_city ON remote_exif_entity (city) WHERE city IS NOT NULL', + ); + late final Index idxRemoteAlbumAssetAlbumAsset = Index( + 'idx_remote_album_asset_album_asset', + 'CREATE INDEX IF NOT EXISTS idx_remote_album_asset_album_asset ON remote_album_asset_entity (album_id, asset_id)', + ); + late final Index idxRemoteAssetCloudId = Index( + 'idx_remote_asset_cloud_id', + 'CREATE INDEX IF NOT EXISTS idx_remote_asset_cloud_id ON remote_asset_cloud_id_entity (cloud_id)', + ); + late final Index idxPersonOwnerId = Index( + 'idx_person_owner_id', + 'CREATE INDEX IF NOT EXISTS idx_person_owner_id ON person_entity (owner_id)', + ); + late final Index idxAssetFacePersonId = Index( + 'idx_asset_face_person_id', + 'CREATE INDEX IF NOT EXISTS idx_asset_face_person_id ON asset_face_entity (person_id)', + ); + late final Index idxAssetFaceAssetId = Index( + 'idx_asset_face_asset_id', + 'CREATE INDEX IF NOT EXISTS idx_asset_face_asset_id ON asset_face_entity (asset_id)', + ); + late final Index idxAssetFaceVisiblePerson = Index( + 'idx_asset_face_visible_person', + 'CREATE INDEX IF NOT EXISTS idx_asset_face_visible_person ON asset_face_entity (person_id, asset_id) WHERE is_visible = 1 AND deleted_at IS NULL', + ); + late final Index idxTrashedLocalAssetChecksum = Index( + 'idx_trashed_local_asset_checksum', + 'CREATE INDEX IF NOT EXISTS idx_trashed_local_asset_checksum ON trashed_local_asset_entity (checksum)', + ); + late final Index idxTrashedLocalAssetAlbum = Index( + 'idx_trashed_local_asset_album', + 'CREATE INDEX IF NOT EXISTS idx_trashed_local_asset_album ON trashed_local_asset_entity (album_id)', + ); + late final Index idxAssetEditAssetId = Index( + 'idx_asset_edit_asset_id', + 'CREATE INDEX IF NOT EXISTS idx_asset_edit_asset_id ON asset_edit_entity (asset_id)', + ); + @override + Iterable> get allTables => + allSchemaEntities.whereType>(); + @override + List get allSchemaEntities => [ + userEntity, + remoteAssetEntity, + stackEntity, + localAssetEntity, + remoteAlbumEntity, + localAlbumEntity, + localAlbumAssetEntity, + idxLocalAlbumAssetAlbumAsset, + idxLocalAssetChecksum, + idxLocalAssetCloudId, + idxStackPrimaryAssetId, + uQRemoteAssetsOwnerChecksum, + uQRemoteAssetsOwnerLibraryChecksum, + idxRemoteAssetChecksum, + idxRemoteAssetStackId, + idxRemoteAssetOwnerVisibilityDeletedCreated, + authUserEntity, + userMetadataEntity, + partnerEntity, + remoteExifEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + remoteAssetCloudIdEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + assetFaceEntity, + storeEntity, + trashedLocalAssetEntity, + assetEditEntity, + settings, + idxPartnerSharedWithId, + idxLatLng, + idxRemoteExifCity, + idxRemoteAlbumAssetAlbumAsset, + idxRemoteAssetCloudId, + idxPersonOwnerId, + idxAssetFacePersonId, + idxAssetFaceAssetId, + idxAssetFaceVisiblePerson, + idxTrashedLocalAssetChecksum, + idxTrashedLocalAssetAlbum, + idxAssetEditAssetId, + ]; + @override + StreamQueryUpdateRules get streamUpdateRules => const StreamQueryUpdateRules([ + WritePropagation( + on: TableUpdateQuery.onTableName( + 'user_entity', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('remote_asset_entity', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'user_entity', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('stack_entity', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'remote_asset_entity', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('remote_album_entity', kind: UpdateKind.update)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'remote_album_entity', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('local_album_entity', kind: UpdateKind.update)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'local_asset_entity', + limitUpdateKind: UpdateKind.delete, + ), + result: [ + TableUpdate('local_album_asset_entity', kind: UpdateKind.delete), + ], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'local_album_entity', + limitUpdateKind: UpdateKind.delete, + ), + result: [ + TableUpdate('local_album_asset_entity', kind: UpdateKind.delete), + ], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'user_entity', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('user_metadata_entity', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'user_entity', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('partner_entity', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'user_entity', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('partner_entity', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'remote_asset_entity', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('remote_exif_entity', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'remote_asset_entity', + limitUpdateKind: UpdateKind.delete, + ), + result: [ + TableUpdate('remote_album_asset_entity', kind: UpdateKind.delete), + ], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'remote_album_entity', + limitUpdateKind: UpdateKind.delete, + ), + result: [ + TableUpdate('remote_album_asset_entity', kind: UpdateKind.delete), + ], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'remote_album_entity', + limitUpdateKind: UpdateKind.delete, + ), + result: [ + TableUpdate('remote_album_user_entity', kind: UpdateKind.delete), + ], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'user_entity', + limitUpdateKind: UpdateKind.delete, + ), + result: [ + TableUpdate('remote_album_user_entity', kind: UpdateKind.delete), + ], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'remote_asset_entity', + limitUpdateKind: UpdateKind.delete, + ), + result: [ + TableUpdate('remote_asset_cloud_id_entity', kind: UpdateKind.delete), + ], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'user_entity', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('memory_entity', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'remote_asset_entity', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('memory_asset_entity', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'memory_entity', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('memory_asset_entity', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'user_entity', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('person_entity', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'remote_asset_entity', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('asset_face_entity', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'person_entity', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('asset_face_entity', kind: UpdateKind.update)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'remote_asset_entity', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('asset_edit_entity', kind: UpdateKind.delete)], + ), + ]); + @override + int get schemaVersion => 27; + @override + DriftDatabaseOptions get options => + const DriftDatabaseOptions(storeDateTimeAsText: true); +} diff --git a/mobile/test/infrastructure/repository.mock.dart b/mobile/test/infrastructure/repository.mock.dart index 74ecf39038..9c1cdae416 100644 --- a/mobile/test/infrastructure/repository.mock.dart +++ b/mobile/test/infrastructure/repository.mock.dart @@ -2,7 +2,7 @@ import 'package:immich_mobile/infrastructure/repositories/backup.repository.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/log.repository.dart'; -import 'package:immich_mobile/infrastructure/repositories/metadata.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/settings.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/remote_album.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/remote_asset.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart'; @@ -18,7 +18,7 @@ import 'package:mocktail/mocktail.dart'; class MockDriftStoreRepository extends Mock implements DriftStoreRepository {} -class MockMetadataRepository extends Mock implements MetadataRepository {} +class MockSettingsRepository extends Mock implements SettingsRepository {} class MockLogRepository extends Mock implements LogRepository {} diff --git a/mobile/test/medium/repositories/metadata_repository_test.dart b/mobile/test/medium/repositories/settings_repository_test.dart similarity index 74% rename from mobile/test/medium/repositories/metadata_repository_test.dart rename to mobile/test/medium/repositories/settings_repository_test.dart index 8662e8bdd0..6a3f79badb 100644 --- a/mobile/test/medium/repositories/metadata_repository_test.dart +++ b/mobile/test/medium/repositories/settings_repository_test.dart @@ -2,19 +2,19 @@ import 'package:drift/drift.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:immich_mobile/domain/models/log.model.dart'; -import 'package:immich_mobile/domain/models/metadata_key.dart'; -import 'package:immich_mobile/infrastructure/entities/metadata.entity.drift.dart'; -import 'package:immich_mobile/infrastructure/repositories/metadata.repository.dart'; +import 'package:immich_mobile/domain/models/settings_key.dart'; +import 'package:immich_mobile/infrastructure/entities/settings.entity.drift.dart'; +import 'package:immich_mobile/infrastructure/repositories/settings.repository.dart'; import '../repository_context.dart'; void main() { late MediumRepositoryContext ctx; - late MetadataRepository sut; + late SettingsRepository sut; setUpAll(() async { ctx = MediumRepositoryContext(); - sut = await MetadataRepository.ensureInitialized(ctx.db); + sut = await SettingsRepository.ensureInitialized(ctx.db); }); tearDownAll(() async { @@ -22,8 +22,8 @@ void main() { }); setUp(() async { - await ctx.db.delete(ctx.db.metadataEntity).go(); - await MetadataRepository.instance.refresh(); + await ctx.db.delete(ctx.db.settingsEntity).go(); + await SettingsRepository.instance.refresh(); }); group('defaults', () { @@ -56,7 +56,7 @@ void main() { await sut.write(.themeMode, ThemeMode.system); expect(sut.appConfig.theme.mode, ThemeMode.system); - final rows = await ctx.db.select(ctx.db.metadataEntity).get(); + final rows = await ctx.db.select(ctx.db.settingsEntity).get(); expect(rows, isEmpty); }); }); @@ -66,10 +66,10 @@ void main() { group('sync', () { test('picks up rows that were inserted directly into the DB', () async { await ctx.db - .into(ctx.db.metadataEntity) + .into(ctx.db.settingsEntity) .insert( - MetadataEntityCompanion.insert( - key: MetadataKey.themeMode.name, + SettingsEntityCompanion.insert( + key: SettingsKey.themeMode.name, value: ThemeMode.dark.name, updatedAt: Value(DateTime.now()), ), @@ -78,32 +78,32 @@ void main() { // Cache hasn't seen this row yet — view still returns the default. expect(sut.appConfig.theme.mode, ThemeMode.system); - await MetadataRepository.instance.refresh(); + await SettingsRepository.instance.refresh(); expect(sut.appConfig.theme.mode, ThemeMode.dark); }); test('drops cached values for rows that were deleted out from under the repo', () async { await sut.write(.themeMode, ThemeMode.dark); // Wipe the row directly. Cache still holds the old value. - await ctx.db.delete(ctx.db.metadataEntity).go(); + await ctx.db.delete(ctx.db.settingsEntity).go(); expect(sut.appConfig.theme.mode, ThemeMode.dark); - await MetadataRepository.instance.refresh(); + await SettingsRepository.instance.refresh(); expect(sut.appConfig.theme.mode, ThemeMode.system); }); - test('skips rows whose key is unknown to MetadataKey', () async { + test('skips rows whose key is unknown to SettingsKey', () async { await ctx.db - .into(ctx.db.metadataEntity) + .into(ctx.db.settingsEntity) .insert( - MetadataEntityCompanion.insert( + SettingsEntityCompanion.insert( key: 'app-config.unknown.future-key', value: 'whatever', updatedAt: Value(DateTime.now()), ), ); - await MetadataRepository.instance.refresh(); + await SettingsRepository.instance.refresh(); expect(sut.appConfig.theme.mode, ThemeMode.system); }); }); @@ -111,13 +111,13 @@ void main() { group('watch', () { test('watchAppConfig emits the new value after a write', () async { final expectation = expectLater(sut.watchConfig().map((c) => c.theme.mode), emitsThrough(ThemeMode.dark)); - await sut.write(MetadataKey.themeMode, ThemeMode.dark); + await sut.write(SettingsKey.themeMode, ThemeMode.dark); await expectation; }); test('watchConfig emits the new value after a write', () async { final expectation = expectLater(sut.watchConfig().map((c) => c.logLevel), emitsThrough(LogLevel.warning)); - await sut.write(MetadataKey.logLevel, LogLevel.warning); + await sut.write(SettingsKey.logLevel, LogLevel.warning); await expectation; }); }); diff --git a/mobile/test/services/background_upload.service_test.dart b/mobile/test/services/background_upload.service_test.dart index dd19f2b1cc..310f2f4d49 100644 --- a/mobile/test/services/background_upload.service_test.dart +++ b/mobile/test/services/background_upload.service_test.dart @@ -11,7 +11,7 @@ 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/metadata.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/settings.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/store.repository.dart'; import 'package:immich_mobile/services/background_upload.service.dart'; import 'package:mocktail/mocktail.dart'; @@ -38,7 +38,7 @@ void main() { ); db = Drift(DatabaseConnection(NativeDatabase.memory(), closeStreamsSynchronously: true)); await StoreService.init(storeRepository: DriftStoreRepository(db)); - await MetadataRepository.ensureInitialized(db); + await SettingsRepository.ensureInitialized(db); await Store.put(StoreKey.serverEndpoint, 'http://test-server.com'); await Store.put(StoreKey.deviceId, 'test-device-id'); diff --git a/mobile/test/unit/repositories/metadata_repository_test.dart b/mobile/test/unit/repositories/settings_repository_test.dart similarity index 76% rename from mobile/test/unit/repositories/metadata_repository_test.dart rename to mobile/test/unit/repositories/settings_repository_test.dart index e51b21f238..80214dd298 100644 --- a/mobile/test/unit/repositories/metadata_repository_test.dart +++ b/mobile/test/unit/repositories/settings_repository_test.dart @@ -1,10 +1,10 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:immich_mobile/domain/models/config/app_config.dart'; -import 'package:immich_mobile/domain/models/metadata_key.dart'; +import 'package:immich_mobile/domain/models/settings_key.dart'; void main() { - group('MetadataKey', () { - for (final key in MetadataKey.values) { + group('SettingsKey', () { + for (final key in SettingsKey.values) { test('verify codec for $key', () { final defaultValue = defaultConfig.read(key); final encoded = key.encode(defaultValue); diff --git a/mobile/test/unit/utils/semver_test.dart b/mobile/test/unit/utils/semver_test.dart index 8f1958a879..1e534af593 100644 --- a/mobile/test/unit/utils/semver_test.dart +++ b/mobile/test/unit/utils/semver_test.dart @@ -88,5 +88,71 @@ void main() { expect(version2.minor, 2); expect(version2.patch, 3); }); + + test('Orders later prerelease above earlier prerelease', () { + const rc1 = SemVer(major: 1, minor: 151, patch: 0, prerelease: 1); + const rc2 = SemVer(major: 1, minor: 151, patch: 0, prerelease: 2); + expect(rc2 > rc1, isTrue); + expect(rc1 < rc2, isTrue); + expect(rc1 == rc2, isFalse); + }); + + test('Final release outranks its prerelease of the same version', () { + const rc = SemVer(major: 1, minor: 151, patch: 0, prerelease: 1); + const release = SemVer(major: 1, minor: 151, patch: 0); + expect(release > rc, isTrue); + expect(rc < release, isTrue); + }); + + test('Higher major outranks a prerelease regardless of ordinal', () { + const rc = SemVer(major: 1, minor: 151, patch: 0, prerelease: 9); + const next = SemVer(major: 2, minor: 0, patch: 0); + expect(next > rc, isTrue); + }); + + test('Equal prerelease versions compare as equal', () { + const a = SemVer(major: 1, minor: 151, patch: 0, prerelease: 3); + const b = SemVer(major: 1, minor: 151, patch: 0, prerelease: 3); + expect(a == b, isTrue); + expect(a > b, isFalse); + expect(a < b, isFalse); + }); + + test('Reports prerelease difference type', () { + const rc1 = SemVer(major: 1, minor: 151, patch: 0, prerelease: 1); + const rc2 = SemVer(major: 1, minor: 151, patch: 0, prerelease: 2); + expect(rc1.differenceType(rc2), SemVerType.prerelease); + }); + + test('toString includes prerelease suffix when present', () { + const rc = SemVer(major: 1, minor: 151, patch: 0, prerelease: 2); + expect(rc.toString(), '1.151.0-rc.2'); + }); + + test('Parses prerelease ordinal from -rc strings', () { + final dotted = SemVer.fromString('1.151.0-rc.2'); + expect(dotted.major, 1); + expect(dotted.minor, 151); + expect(dotted.patch, 0); + expect(dotted.prerelease, 2); + + expect(SemVer.fromString('v1.151.0-rc.3').prerelease, 3); + expect(SemVer.fromString('1.2.3-rc.2+build.5').prerelease, 2); + }); + + test('Plain version string has null prerelease', () { + expect(SemVer.fromString('3.0.0').prerelease, isNull); + }); + + test('Invalid rc suffixes parse without error and have null prerelease', () { + final debug = SemVer.fromString('1.2.3-debug'); + expect(debug.major, 1); + expect(debug.minor, 2); + expect(debug.patch, 3); + expect(debug.prerelease, isNull); + + expect(SemVer.fromString('1.2.3+build.5').prerelease, isNull); + expect(SemVer.fromString('1.151.0-rc4').prerelease, isNull); + }); }); } diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index 74b11e5c8b..90dd80ac1d 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -1627,6 +1627,17 @@ "type": "string" } }, + { + "name": "id", + "required": false, + "in": "query", + "description": "Album ID", + "schema": { + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + } + }, { "name": "isOwned", "required": false, @@ -1644,6 +1655,15 @@ "schema": { "type": "boolean" } + }, + { + "name": "name", + "required": false, + "in": "query", + "description": "Album name (exact match)", + "schema": { + "type": "string" + } } ], "responses": { @@ -4288,6 +4308,351 @@ "x-immich-state": "Stable" } }, + "/assets/{id}/video/stream/main.m3u8": { + "get": { + "description": "Returns an HLS main playlist with all available variants for the asset.", + "operationId": "getMainPlaylist", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + } + }, + { + "name": "key", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "slug", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/vnd.apple.mpegurl": { + "schema": { + "type": "string" + } + } + }, + "description": "" + } + }, + "security": [ + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + } + ], + "summary": "Get HLS main playlist", + "tags": [ + "Assets" + ], + "x-immich-history": [ + { + "version": "v3", + "state": "Added" + }, + { + "version": "v3", + "state": "Alpha" + } + ], + "x-immich-permission": "asset.view", + "x-immich-state": "Alpha" + } + }, + "/assets/{id}/video/stream/{sessionId}": { + "delete": { + "description": "Releases server resources for the streaming session.", + "operationId": "endSession", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + } + }, + { + "name": "key", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "sessionId", + "required": true, + "in": "path", + "schema": { + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + } + }, + { + "name": "slug", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "" + } + }, + "security": [ + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + } + ], + "summary": "End HLS streaming session", + "tags": [ + "Assets" + ], + "x-immich-history": [ + { + "version": "v3", + "state": "Added" + }, + { + "version": "v3", + "state": "Alpha" + } + ], + "x-immich-permission": "asset.view", + "x-immich-state": "Alpha" + } + }, + "/assets/{id}/video/stream/{sessionId}/{variantIndex}/playlist.m3u8": { + "get": { + "description": "Returns an HLS media playlist for one variant of the streaming session.", + "operationId": "getMediaPlaylist", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + } + }, + { + "name": "key", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "sessionId", + "required": true, + "in": "path", + "schema": { + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + } + }, + { + "name": "slug", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "variantIndex", + "required": true, + "in": "path", + "schema": { + "minimum": 0, + "maximum": 9007199254740991, + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/vnd.apple.mpegurl": { + "schema": { + "type": "string" + } + } + }, + "description": "" + } + }, + "security": [ + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + } + ], + "summary": "Get HLS media playlist", + "tags": [ + "Assets" + ], + "x-immich-history": [ + { + "version": "v3", + "state": "Added" + }, + { + "version": "v3", + "state": "Alpha" + } + ], + "x-immich-permission": "asset.view", + "x-immich-state": "Alpha" + } + }, + "/assets/{id}/video/stream/{sessionId}/{variantIndex}/{filename}": { + "get": { + "description": "Streams an HLS init segment (init.mp4) or media segment (seg_N.m4s).", + "operationId": "getSegment", + "parameters": [ + { + "name": "filename", + "required": true, + "in": "path", + "schema": { + "pattern": "^(init\\.mp4|seg_\\d+\\.m4s)$", + "type": "string" + } + }, + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + } + }, + { + "name": "key", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "sessionId", + "required": true, + "in": "path", + "schema": { + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + } + }, + { + "name": "slug", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "variantIndex", + "required": true, + "in": "path", + "schema": { + "minimum": 0, + "maximum": 9007199254740991, + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/octet-stream": { + "schema": { + "format": "binary", + "type": "string" + } + } + }, + "description": "" + } + }, + "security": [ + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + } + ], + "summary": "Get HLS segment or init file", + "tags": [ + "Assets" + ], + "x-immich-history": [ + { + "version": "v3", + "state": "Added" + }, + { + "version": "v3", + "state": "Alpha" + } + ], + "x-immich-permission": "asset.view", + "x-immich-state": "Alpha" + } + }, "/auth/admin-sign-up": { "post": { "description": "Create the first admin user in the system.", @@ -18145,6 +18510,7 @@ "LibrarySyncFilesQueueAll", "LibrarySyncFiles", "LibraryScanQueueAll", + "HlsSessionCleanup", "MemoryCleanup", "MemoryGenerate", "NotificationsCleanup", @@ -18170,7 +18536,7 @@ "VersionCheck", "OcrQueueAll", "Ocr", - "WorkflowAssetCreate" + "WorkflowAssetTrigger" ], "type": "string" }, @@ -19586,6 +19952,12 @@ "description": "Whether people are enabled", "type": "boolean" }, + "minimumFaces": { + "description": "People face threshold", + "maximum": 9007199254740991, + "minimum": 1, + "type": "integer" + }, "sidebarWeb": { "description": "Whether people appear in web sidebar", "type": "boolean" @@ -19647,6 +20019,12 @@ "description": "Whether people are enabled", "type": "boolean" }, + "minimumFaces": { + "description": "People face threshold", + "maximum": 9007199254740991, + "minimum": 1, + "type": "integer" + }, "sidebarWeb": { "description": "Whether people appear in web sidebar", "type": "boolean" @@ -20244,6 +20622,13 @@ "trigger": { "$ref": "#/components/schemas/WorkflowTrigger", "description": "Workflow trigger" + }, + "uiHints": { + "description": "Ui hints, for example \"smart-album\"", + "items": { + "type": "string" + }, + "type": "array" } }, "required": [ @@ -20251,7 +20636,8 @@ "key", "steps", "title", - "trigger" + "trigger", + "uiHints" ], "type": "object" }, @@ -20845,6 +21231,57 @@ ], "type": "string" }, + "ReleaseChannel": { + "description": "Release channel", + "enum": [ + "stable", + "releaseCandidate" + ], + "type": "string" + }, + "ReleaseEventV1": { + "properties": { + "checkedAt": { + "description": "When the server last checked for a latest version. As an ISO timestamp", + "type": "string" + }, + "isAvailable": { + "description": "Whether a new version is available", + "type": "boolean" + }, + "releaseVersion": { + "$ref": "#/components/schemas/ServerVersionResponseDto" + }, + "serverVersion": { + "$ref": "#/components/schemas/ServerVersionResponseDto" + }, + "type": { + "$ref": "#/components/schemas/ReleaseType", + "description": "Release type", + "nullable": true + } + }, + "required": [ + "checkedAt", + "isAvailable", + "releaseVersion", + "serverVersion", + "type" + ], + "type": "object" + }, + "ReleaseType": { + "enum": [ + "major", + "premajor", + "minor", + "preminor", + "patch", + "prepatch", + "prerelease" + ], + "type": "string" + }, "ReverseGeocodingStateResponseDto": { "properties": { "lastImportFileName": { @@ -21224,6 +21661,12 @@ "description": "Map light style URL", "type": "string" }, + "minFaces": { + "description": "People min faces server default", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, "oauthButtonText": { "description": "OAuth button text", "type": "string" @@ -21253,6 +21696,7 @@ "maintenanceMode", "mapDarkStyleUrl", "mapLightStyleUrl", + "minFaces", "oauthButtonText", "publicUsers", "trashDays", @@ -21302,6 +21746,10 @@ "description": "Whether password login is enabled", "type": "boolean" }, + "realtimeTranscoding": { + "description": "Whether real-time transcoding is enabled", + "type": "boolean" + }, "reverseGeocoding": { "description": "Whether reverse geocoding is enabled", "type": "boolean" @@ -21334,6 +21782,7 @@ "oauthAutoLaunch", "ocr", "passwordLogin", + "realtimeTranscoding", "reverseGeocoding", "search", "sidecar", @@ -21514,26 +21963,40 @@ "major": { "description": "Major version number", "maximum": 9007199254740991, - "minimum": -9007199254740991, + "minimum": 0, "type": "integer" }, "minor": { "description": "Minor version number", "maximum": 9007199254740991, - "minimum": -9007199254740991, + "minimum": 0, "type": "integer" }, "patch": { "description": "Patch version number", "maximum": 9007199254740991, - "minimum": -9007199254740991, + "minimum": 0, "type": "integer" + }, + "prerelease": { + "description": "Pre-release version number", + "maximum": 9007199254740991, + "minimum": 0, + "nullable": true, + "type": "integer", + "x-immich-history": [ + { + "version": "v3.0.0", + "state": "Added" + } + ] } }, "required": [ "major", "minor", - "patch" + "patch", + "prerelease" ], "type": "object" }, @@ -24194,6 +24657,9 @@ "description": "Preset", "type": "string" }, + "realtime": { + "$ref": "#/components/schemas/SystemConfigFFmpegRealtimeDto" + }, "refs": { "description": "References", "maximum": 6, @@ -24244,6 +24710,7 @@ "maxBitrate", "preferredHwDevice", "preset", + "realtime", "refs", "targetAudioCodec", "targetResolution", @@ -24256,6 +24723,18 @@ ], "type": "object" }, + "SystemConfigFFmpegRealtimeDto": { + "properties": { + "enabled": { + "description": "Enable real-time HLS transcoding (alpha)", + "type": "boolean" + } + }, + "required": [ + "enabled" + ], + "type": "object" + }, "SystemConfigFacesDto": { "properties": { "import": { @@ -24554,12 +25033,16 @@ }, "SystemConfigNewVersionCheckDto": { "properties": { + "channel": { + "$ref": "#/components/schemas/ReleaseChannel" + }, "enabled": { "description": "Enabled", "type": "boolean" } }, "required": [ + "channel", "enabled" ], "type": "object" @@ -26372,6 +26855,7 @@ "description": "Plugin trigger type", "enum": [ "AssetCreate", + "AssetMetadataExtraction", "PersonRecognized" ], "type": "string" diff --git a/open-api/patch/api_client.dart.patch b/open-api/patch/api_client.dart.patch index 8996e79413..55acb0d3cd 100644 --- a/open-api/patch/api_client.dart.patch +++ b/open-api/patch/api_client.dart.patch @@ -1,21 +1,96 @@ -@@ -143,19 +143,19 @@ - ); +@@ -13,7 +13,7 @@ + class ApiClient { + ApiClient({this.basePath = '/api', this.authentication,}); + +- final String basePath; ++ String basePath; + final Authentication? authentication; + + var _client = Client(); +@@ -44,8 +44,9 @@ + Object? body, + Map headerParams, + Map formParams, +- String? contentType, +- ) async { ++ String? contentType, { ++ Future? abortTrigger, ++ }) async { + await authentication?.applyToParams(queryParams, headerParams); + + headerParams.addAll(_defaultHeaderMap); +@@ -63,7 +64,7 @@ + body is MultipartFile && (contentType == null || + !contentType.toLowerCase().startsWith('multipart/form-data')) + ) { +- final request = StreamedRequest(method, uri); ++ final request = AbortableStreamedRequest(method, uri, abortTrigger: abortTrigger); + request.headers.addAll(headerParams); + request.contentLength = body.length; + body.finalize().listen( +@@ -78,7 +79,7 @@ + } + + if (body is MultipartRequest) { +- final request = MultipartRequest(method, uri); ++ final request = AbortableMultipartRequest(method, uri, abortTrigger: abortTrigger); + request.fields.addAll(body.fields); + request.files.addAll(body.files); + request.headers.addAll(body.headers); +@@ -92,14 +93,19 @@ + : await serializeAsync(body); + final nullableHeaderParams = headerParams.isEmpty ? null : headerParams; + +- switch(method) { +- case 'POST': return await _client.post(uri, headers: nullableHeaderParams, body: msgBody,); +- case 'PUT': return await _client.put(uri, headers: nullableHeaderParams, body: msgBody,); +- case 'DELETE': return await _client.delete(uri, headers: nullableHeaderParams, body: msgBody,); +- case 'PATCH': return await _client.patch(uri, headers: nullableHeaderParams, body: msgBody,); +- case 'HEAD': return await _client.head(uri, headers: nullableHeaderParams,); +- case 'GET': return await _client.get(uri, headers: nullableHeaderParams,); ++ final request = AbortableRequest(method, uri, abortTrigger: abortTrigger); ++ if (nullableHeaderParams != null) { ++ request.headers.addAll(nullableHeaderParams); + } ++ if (msgBody is String) { ++ request.body = msgBody; ++ } else if (msgBody is List) { ++ request.bodyBytes = msgBody; ++ } else if (msgBody is Map) { ++ request.bodyFields = msgBody; ++ } ++ final response = await _client.send(request); ++ return Response.fromStream(response); + } on SocketException catch (error, trace) { + throw ApiException.withInner( + HttpStatus.badRequest, +@@ -136,26 +146,21 @@ + trace, + ); + } +- +- throw ApiException( +- HttpStatus.badRequest, +- 'Invalid HTTP operation: $method $path', +- ); } - + - Future deserializeAsync(String value, String targetType, {bool growable = false,}) async => + Future deserializeAsync(String value, String targetType, {bool growable = false,}) => // ignore: deprecated_member_use_from_same_package deserialize(value, targetType, growable: growable); - + @Deprecated('Scheduled for removal in OpenAPI Generator 6.x. Use deserializeAsync() instead.') - dynamic deserialize(String value, String targetType, {bool growable = false,}) { + Future deserialize(String value, String targetType, {bool growable = false,}) async { // Remove all spaces. Necessary for regular expressions as well. targetType = targetType.replaceAll(' ', ''); // ignore: parameter_assignments - + // If the expected target type is String, nothing to do... return targetType == 'String' ? value - : fromJson(json.decode(value), targetType, growable: growable); + : fromJson(await compute((String j) => json.decode(j), value), targetType, growable: growable); } + + // ignore: deprecated_member_use_from_same_package diff --git a/open-api/templates/mobile/api.mustache b/open-api/templates/mobile/api.mustache index ac32571123..2cd4c0f04e 100644 --- a/open-api/templates/mobile/api.mustache +++ b/open-api/templates/mobile/api.mustache @@ -49,7 +49,7 @@ class {{{classname}}} { /// {{/-last}} {{/allParams}} - Future {{{nickname}}}WithHttpInfo({{#allParams}}{{#required}}{{{dataType}}} {{{paramName}}},{{^-last}} {{/-last}}{{/required}}{{/allParams}}{{#hasOptionalParams}}{ {{#allParams}}{{^required}}{{{dataType}}}? {{{paramName}}},{{^-last}} {{/-last}}{{/required}}{{/allParams}} }{{/hasOptionalParams}}) async { + Future {{{nickname}}}WithHttpInfo({{#allParams}}{{#required}}{{{dataType}}} {{{paramName}}}, {{/required}}{{/allParams}}{ {{#allParams}}{{^required}}{{{dataType}}}? {{{paramName}}}, {{/required}}{{/allParams}}Future? abortTrigger, }) async { // ignore: prefer_const_declarations final apiPath = r'{{{path}}}'{{#pathParams}} .replaceAll({{=<% %>=}}'{<% baseName %>}'<%={{ }}=%>, {{{paramName}}}{{^isString}}.toString(){{/isString}}){{/pathParams}}; @@ -128,6 +128,7 @@ class {{{classname}}} { headerParams, formParams, contentTypes.isEmpty ? null : contentTypes.first, + abortTrigger: abortTrigger, ); } @@ -161,8 +162,8 @@ class {{{classname}}} { /// {{/-last}} {{/allParams}} - Future<{{#returnType}}{{{.}}}?{{/returnType}}{{^returnType}}void{{/returnType}}> {{{nickname}}}({{#allParams}}{{#required}}{{{dataType}}} {{{paramName}}},{{^-last}} {{/-last}}{{/required}}{{/allParams}}{{#hasOptionalParams}}{ {{#allParams}}{{^required}}{{{dataType}}}? {{{paramName}}},{{^-last}} {{/-last}}{{/required}}{{/allParams}} }{{/hasOptionalParams}}) async { - final response = await {{{nickname}}}WithHttpInfo({{#allParams}}{{#required}}{{{paramName}}},{{^-last}} {{/-last}}{{/required}}{{/allParams}}{{#hasOptionalParams}} {{#allParams}}{{^required}}{{{paramName}}}: {{{paramName}}},{{^-last}} {{/-last}}{{/required}}{{/allParams}} {{/hasOptionalParams}}); + Future<{{#returnType}}{{{.}}}?{{/returnType}}{{^returnType}}void{{/returnType}}> {{{nickname}}}({{#allParams}}{{#required}}{{{dataType}}} {{{paramName}}}, {{/required}}{{/allParams}}{ {{#allParams}}{{^required}}{{{dataType}}}? {{{paramName}}}, {{/required}}{{/allParams}}Future? abortTrigger, }) async { + final response = await {{{nickname}}}WithHttpInfo({{#allParams}}{{#required}}{{{paramName}}}, {{/required}}{{/allParams}}{{#allParams}}{{^required}}{{{paramName}}}: {{{paramName}}}, {{/required}}{{/allParams}}abortTrigger: abortTrigger,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/open-api/templates/mobile/api.mustache.patch b/open-api/templates/mobile/api.mustache.patch index e3f888d6d7..feb5f40047 100644 --- a/open-api/templates/mobile/api.mustache.patch +++ b/open-api/templates/mobile/api.mustache.patch @@ -1,8 +1,11 @@ ---- api.mustache 2025-01-22 05:50:25 -+++ api.mustache.modified 2025-01-22 05:52:23 -@@ -51,7 +51,7 @@ +--- api.mustache ++++ api.mustache.modified +@@ -49,9 +49,9 @@ + /// + {{/-last}} {{/allParams}} - Future {{{nickname}}}WithHttpInfo({{#allParams}}{{#required}}{{{dataType}}} {{{paramName}}},{{^-last}} {{/-last}}{{/required}}{{/allParams}}{{#hasOptionalParams}}{ {{#allParams}}{{^required}}{{{dataType}}}? {{{paramName}}},{{^-last}} {{/-last}}{{/required}}{{/allParams}} }{{/hasOptionalParams}}) async { +- Future {{{nickname}}}WithHttpInfo({{#allParams}}{{#required}}{{{dataType}}} {{{paramName}}},{{^-last}} {{/-last}}{{/required}}{{/allParams}}{{#hasOptionalParams}}{ {{#allParams}}{{^required}}{{{dataType}}}? {{{paramName}}},{{^-last}} {{/-last}}{{/required}}{{/allParams}} }{{/hasOptionalParams}}) async { ++ Future {{{nickname}}}WithHttpInfo({{#allParams}}{{#required}}{{{dataType}}} {{{paramName}}}, {{/required}}{{/allParams}}{ {{#allParams}}{{^required}}{{{dataType}}}? {{{paramName}}}, {{/required}}{{/allParams}}Future? abortTrigger, }) async { // ignore: prefer_const_declarations - final path = r'{{{path}}}'{{#pathParams}} + final apiPath = r'{{{path}}}'{{#pathParams}} @@ -18,7 +21,7 @@ {{#formParams}} {{^isFile}} if ({{{paramName}}} != null) { -@@ -121,7 +121,7 @@ +@@ -121,13 +121,14 @@ {{/isMultipart}} return apiClient.invokeAPI( @@ -27,3 +30,21 @@ '{{{httpMethod}}}', queryParams, postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, ++ abortTrigger: abortTrigger, + ); + } + +@@ -161,8 +162,8 @@ + /// + {{/-last}} + {{/allParams}} +- Future<{{#returnType}}{{{.}}}?{{/returnType}}{{^returnType}}void{{/returnType}}> {{{nickname}}}({{#allParams}}{{#required}}{{{dataType}}} {{{paramName}}},{{^-last}} {{/-last}}{{/required}}{{/allParams}}{{#hasOptionalParams}}{ {{#allParams}}{{^required}}{{{dataType}}}? {{{paramName}}},{{^-last}} {{/-last}}{{/required}}{{/allParams}} }{{/hasOptionalParams}}) async { +- final response = await {{{nickname}}}WithHttpInfo({{#allParams}}{{#required}}{{{paramName}}},{{^-last}} {{/-last}}{{/required}}{{/allParams}}{{#hasOptionalParams}} {{#allParams}}{{^required}}{{{paramName}}}: {{{paramName}}},{{^-last}} {{/-last}}{{/required}}{{/allParams}} {{/hasOptionalParams}}); ++ Future<{{#returnType}}{{{.}}}?{{/returnType}}{{^returnType}}void{{/returnType}}> {{{nickname}}}({{#allParams}}{{#required}}{{{dataType}}} {{{paramName}}}, {{/required}}{{/allParams}}{ {{#allParams}}{{^required}}{{{dataType}}}? {{{paramName}}}, {{/required}}{{/allParams}}Future? abortTrigger, }) async { ++ final response = await {{{nickname}}}WithHttpInfo({{#allParams}}{{#required}}{{{paramName}}}, {{/required}}{{/allParams}}{{#allParams}}{{^required}}{{{paramName}}}: {{{paramName}}}, {{/required}}{{/allParams}}abortTrigger: abortTrigger,); + if (response.statusCode >= HttpStatus.badRequest) { + throw ApiException(response.statusCode, await _decodeBodyBytes(response)); + } diff --git a/packages/plugin-core/manifest.json b/packages/plugin-core/manifest.json index 3111678862..48b4bee2c8 100644 --- a/packages/plugin-core/manifest.json +++ b/packages/plugin-core/manifest.json @@ -7,8 +7,8 @@ "wasmPath": "dist/plugin.wasm", "templates": [ { - "name": "auto-archive-screenshots", - "title": "Auto-archive screenshots", + "name": "screenshots-smart-album", + "title": "Archive screenshots", "description": "Archive uploads with \"screenshot\" in the filename and optionally add them to an album", "trigger": "AssetCreate", "steps": [ @@ -20,19 +20,41 @@ "caseSensitive": false } }, - { - "method": "immich-plugin-core#assetAddToAlbums", - "config": { - "albumIds": [] - } - }, { "method": "immich-plugin-core#assetArchive", "config": { "inverse": false } + }, + { + "method": "immich-plugin-core#assetAddToAlbums", + "config": { + "albumName": "Screenshots", + "albumIds": [] + } } - ] + ], + "uiHints": ["SmartAlbum"] + }, + { + "name": "missing-timezone-smart-album", + "title": "Missing timezone", + "description": "Automatically create an album for assets without a time zone", + "trigger": "AssetMetadataExtraction", + "steps": [ + { + "method": "immich-plugin-core#assetMissingTimeZoneFilter", + "config": {} + }, + { + "method": "immich-plugin-core#assetAddToAlbums", + "config": { + "albumName": "Missing time zone", + "albumIds": [] + } + } + ], + "uiHints": ["SmartAlbum"] } ], "methods": [ @@ -65,7 +87,25 @@ }, "required": ["pattern"] }, - "uiHints": ["filter"] + "uiHints": ["Filter"] + }, + { + "name": "assetMissingTimeZoneFilter", + "title": "Filter by missing time zone", + "description": "Filter assets that have no time zone information", + "types": ["AssetV1"], + "schema": { + "type": "object", + "properties": { + "inverse": { + "type": "boolean", + "title": "Inverse", + "description": "Missing by default, set to true to filter assets with a time zone", + "default": false + } + } + }, + "uiHints": ["Filter"] }, { "name": "filterFileType", @@ -85,7 +125,7 @@ }, "required": ["fileTypes"] }, - "uiHints": ["filter"] + "uiHints": ["Filter"] }, { "name": "filterPerson", @@ -99,7 +139,7 @@ "array": true, "title": "Person IDs", "description": "List of person to match", - "uiHint": "personI" + "uiHint": "personId" }, "matchAny": { "type": "boolean", @@ -110,7 +150,7 @@ }, "required": ["personIds"] }, - "uiHints": ["filter"] + "uiHints": ["Filter"] }, { "name": "assetArchive", @@ -187,7 +227,12 @@ "title": "Album IDs", "array": true, "description": "Target album IDs", - "uiHint": "albumId" + "uiHint": "AlbumId" + }, + "albumName": { + "type": "string", + "title": "Album name", + "description": "Use an album with this name if one exists, otherwise create a new one" } }, "required": ["albumIds"] @@ -272,14 +317,14 @@ "type": "string", "title": "Album ID", "description": "Target album ID", - "uiHint": "albumId" + "uiHint": "AlbumId" }, "albumIds": { "type": "string", "title": "Album IDs", "description": "Target album IDs", "array": true, - "uiHint": "albumId" + "uiHint": "AlbumId" } } } diff --git a/packages/plugin-core/package.json b/packages/plugin-core/package.json index 7c0bdf9af2..26b5124426 100644 --- a/packages/plugin-core/package.json +++ b/packages/plugin-core/package.json @@ -13,6 +13,7 @@ "license": "AGPL-3.0", "devDependencies": { "@extism/js-pdk": "^1.0.1", + "@immich/sdk": "workspace:*", "@immich/plugin-sdk": "workspace:*", "esbuild": "^0.28.0", "typescript": "^6.0.0" diff --git a/packages/plugin-core/src/index.d.ts b/packages/plugin-core/src/index.d.ts index ae45184cbe..170fa13102 100644 --- a/packages/plugin-core/src/index.d.ts +++ b/packages/plugin-core/src/index.d.ts @@ -1,14 +1,20 @@ -// copy from -// import '@immich/plugin-sdk/host-functions'; +// keep in sync with plugin-sdk/host-functions.ts'; declare module 'extism:host' { interface user { - albumAddAssets(ptr: PTR): I64; + searchAlbums(ptr: PTR): I64; + createAlbum(ptr: PTR): I64; + addAssetsToAlbum(ptr: PTR): I64; addAssetsToAlbums(ptr: PTR): I64; } } +// keep in sync with manifest.json declare module 'main' { + // filters export function assetFileFilter(): I32; + export function assetMissingTimeZoneFilter(): I32; + + // updates export function assetFavorite(): I32; export function assetVisibility(): I32; export function assetArchive(): I32; diff --git a/packages/plugin-core/src/index.ts b/packages/plugin-core/src/index.ts index 85a4a449e7..bcb05cfa19 100644 --- a/packages/plugin-core/src/index.ts +++ b/packages/plugin-core/src/index.ts @@ -1,4 +1,5 @@ -import { AssetStatus, AssetVisibility, WorkflowType, wrapper } from '@immich/plugin-sdk'; +import { wrapper } from '@immich/plugin-sdk'; +import { AssetVisibility, WorkflowType } from '@immich/sdk'; type AssetFileFilterConfig = { pattern: string; @@ -41,6 +42,14 @@ export const assetFileFilter = () => { }); }; +export const assetMissingTimeZoneFilter = () => { + return wrapper(({ config, data }) => { + const hasTimeZone = !!data.asset?.exifInfo?.timeZone; + const needsTimeZone = config.inverse ? true : false; + return { workflow: { continue: hasTimeZone === needsTimeZone } }; + }); +}; + export const assetFavorite = () => { return wrapper(({ config, data }) => { const target = config.inverse ? false : true; @@ -89,28 +98,35 @@ export const assetLock = () => { }; export const assetTrash = () => { - return wrapper(({ config, data }) => ({ - changes: { - asset: config.inverse - ? { deletedAt: null, status: AssetStatus.Active } - : { deletedAt: new Date(), status: AssetStatus.Trashed }, - }, - })); + // TODO use trash/untrash host functions + return wrapper(() => ({})); }; export const assetAddToAlbums = () => { - return wrapper(({ config, data, functions }) => { + return wrapper(({ config, data, functions }) => { + const assetId = data.asset.id; + if (config.albumIds.length === 0) { - // noop - return {}; + if (!config.albumName) { + return {}; + } + + const [existing] = functions.searchAlbums({ name: config.albumName }); + if (!existing) { + const created = functions.createAlbum({ albumName: config.albumName, assetIds: [assetId] }); + config.albumIds.push(created.id); + return {}; + } + + config.albumIds.push(existing.id); } if (config.albumIds.length === 1) { - functions.albumAddAssets(config.albumIds[0], [data.asset.id]); + functions.addAssetsToAlbum(config.albumIds[0], [assetId]); return {}; } - functions.addAssetsToAlbums({ albumIds: config.albumIds, assetIds: [data.asset.id] }); + functions.addAssetsToAlbums({ albumIds: config.albumIds, assetIds: [assetId] }); return {}; }); }; diff --git a/packages/plugin-sdk/package.json b/packages/plugin-sdk/package.json index f505a6cc0e..7c44368fbc 100644 --- a/packages/plugin-sdk/package.json +++ b/packages/plugin-sdk/package.json @@ -2,7 +2,6 @@ "name": "@immich/plugin-sdk", "version": "0.0.0", "description": "", - "main": "index.js", "type": "module", "exports": { "./host-functions": { @@ -11,7 +10,8 @@ }, ".": { "import": "./dist/index.js", - "types": "./dist/index.d.ts" + "types": "./dist/index.d.ts", + "default": "./dist/index.js" } }, "scripts": { @@ -27,6 +27,7 @@ "packageManager": "pnpm@10.33.4", "devDependencies": { "@extism/js-pdk": "^1.1.1", + "@immich/sdk": "workspace:*", "@types/node": "^24.12.4", "esbuild": "^0.28.0", "tsc-alias": "^1.8.16", diff --git a/packages/plugin-sdk/src/enum.ts b/packages/plugin-sdk/src/enum.ts deleted file mode 100644 index a11dab64da..0000000000 --- a/packages/plugin-sdk/src/enum.ts +++ /dev/null @@ -1,33 +0,0 @@ -export enum WorkflowTrigger { - AssetCreate = 'AssetCreate', - PersonRecognized = 'PersonRecognized', -} - -export enum WorkflowType { - AssetV1 = 'AssetV1', - AssetPersonV1 = 'AssetPersonV1', -} - -export enum AssetType { - Image = 'IMAGE', - Video = 'VIDEO', - Audio = 'AUDIO', - Other = 'OTHER', -} - -export enum AssetStatus { - Active = 'active', - Trashed = 'trashed', - Deleted = 'deleted', -} - -export enum AssetVisibility { - Archive = 'archive', - Timeline = 'timeline', - - /** - * Video part of the LivePhotos and MotionPhotos - */ - Hidden = 'hidden', - Locked = 'locked', -} diff --git a/packages/plugin-sdk/src/host-functions.ts b/packages/plugin-sdk/src/host-functions.ts index d0f8a3ef17..281e27c83c 100644 --- a/packages/plugin-sdk/src/host-functions.ts +++ b/packages/plugin-sdk/src/host-functions.ts @@ -1,12 +1,26 @@ +import { + getAllAlbums, + type AlbumResponseDto, + type BulkIdResponseDto, + type BulkIdsDto, + type CreateAlbumDto, +} from '@immich/sdk'; + +// keep in sync with plugin-core/src/index.d.ts'; declare module 'extism:host' { interface user { - albumAddAssets(ptr: PTR): I64; + searchAlbums(ptr: PTR): I64; + createAlbum(ptr: PTR): I64; + addAssetsToAlbum(ptr: PTR): I64; addAssetsToAlbums(ptr: PTR): I64; } } -const host = Host.getFunctions(); -type HostFunctionName = keyof typeof host; +type AlbumsToAssets = { + assetIds: string[]; + albumIds: string[]; +}; + type HostFunctionSuccessResult = { success: true; response: T }; type HostFunctionErrorResult = { success: false; @@ -17,35 +31,49 @@ type HostFunctionResult = | HostFunctionSuccessResult | HostFunctionErrorResult; -const call = (name: HostFunctionName, authToken: string, args: T) => { - const pointer1 = Memory.fromString(JSON.stringify({ authToken, args })); - const fn = host[name]; - const handler = Memory.find(fn(pointer1.offset)); +type QueryParams any> = Parameters[0]; +type AlbumSearchDto = QueryParams; - try { - const result = JSON.parse(handler.readString()) as HostFunctionResult; +export const hostFunctions = (authToken: string) => { + const host = Host.getFunctions(); + type HostFunctionName = keyof typeof host; - if (result.success) { - return result.response; + const call = (name: HostFunctionName, authToken: string, args: T) => { + const pointer1 = Memory.fromString(JSON.stringify({ authToken, args })); + const fn = host[name]; + const handler = Memory.find(fn(pointer1.offset)); + + try { + const result = JSON.parse(handler.readString()) as HostFunctionResult; + + if (result.success) { + return result.response; + } + + throw new Error( + `Failed to call host function "${String(name)}", received ${result.status} - ${JSON.stringify(result.message)}`, + ); + } finally { + handler.free(); + pointer1.free(); } + }; - throw new Error( - `Failed to call host function "${String(name)}", received ${result.status} - ${JSON.stringify(result.message)}`, - ); - } finally { - handler.free(); - pointer1.free(); - } + return { + // album + searchAlbums: (dto: AlbumSearchDto) => + call<[AlbumSearchDto], AlbumResponseDto[]>('searchAlbums', authToken, [ + dto, + ]), + createAlbum: (dto: CreateAlbumDto) => + call<[CreateAlbumDto], AlbumResponseDto>('createAlbum', authToken, [dto]), + addAssetsToAlbum: (albumId: string, assetIds: string[]) => + call<[string, BulkIdsDto], BulkIdResponseDto[]>( + 'addAssetsToAlbum', + authToken, + [albumId, { ids: assetIds }], + ), + addAssetsToAlbums: ({ assetIds, albumIds }: AlbumsToAssets) => + call('addAssetsToAlbums', authToken, [{ albumIds, assetIds }]), + }; }; - -type AlbumsToAssets = { - assetIds: string[]; - albumIds: string[]; -}; - -export const hostFunctions = (authToken: string) => ({ - albumAddAssets: (albumId: string, assetIds: string[]) => - call('albumAddAssets', authToken, [albumId, { ids: assetIds }]), - addAssetsToAlbums: ({ assetIds, albumIds }: AlbumsToAssets) => - call('addAssetsToAlbums', authToken, [{ albumIds, assetIds }]), -}); diff --git a/packages/plugin-sdk/src/index.ts b/packages/plugin-sdk/src/index.ts index 6d4deb2053..c83369410a 100644 --- a/packages/plugin-sdk/src/index.ts +++ b/packages/plugin-sdk/src/index.ts @@ -1,4 +1,3 @@ -export * from 'src/enum.js'; export * from 'src/host-functions.js'; export * from 'src/sdk.js'; export * from 'src/types.js'; diff --git a/packages/plugin-sdk/src/sdk.ts b/packages/plugin-sdk/src/sdk.ts index f0ff8723a6..283e9c3dd5 100644 --- a/packages/plugin-sdk/src/sdk.ts +++ b/packages/plugin-sdk/src/sdk.ts @@ -1,9 +1,10 @@ -import type { WorkflowType } from 'src/enum.js'; +import type { WorkflowType } from '@immich/sdk'; import { hostFunctions } from 'src/host-functions.js'; import type { ConfigValue, WorkflowEventPayload, WorkflowResponse, + WorkflowStepConfig, } from 'src/types.js'; export const wrapper = < @@ -19,19 +20,28 @@ export const wrapper = < const input = Host.inputString(); try { - const event = JSON.parse(input) as WorkflowEventPayload; - // const debug = event.workflow.debug ?? false; + const payload = JSON.parse(input) as WorkflowEventPayload; + const event = { + ...payload, + functions: hostFunctions(payload.workflow.authToken), + }; + + const eventConfigBefore = JSON.stringify(event.config); console.debug( - `Inputs: trigger=${event.trigger}, event=${event.type}, config=${JSON.stringify(event.config)}`, + `Inputs: trigger=${event.trigger}, event=${event.type}, config=${eventConfigBefore}`, ); - const response = - fn({ ...event, functions: hostFunctions(event.workflow.authToken) }) ?? - {}; + const response = fn(event) ?? {}; + + // if config changed, notify host + const eventConfigAfter = JSON.stringify(event.config); + if (!response.config && eventConfigBefore !== eventConfigAfter) { + response.config = event.config as WorkflowStepConfig; + } console.debug( - `Outputs: workflow=${JSON.stringify(response.workflow)}, changes=${JSON.stringify(response.changes)}, data=${JSON.stringify(response.data)}`, + `Outputs: workflow=${JSON.stringify(response.workflow)}, changes=${JSON.stringify(response.changes)}, data=${JSON.stringify(response.data)}, config=${JSON.stringify(response.config)}`, ); const output = JSON.stringify(response); diff --git a/packages/plugin-sdk/src/types.ts b/packages/plugin-sdk/src/types.ts index 54cca3a5aa..67c179f4a6 100644 --- a/packages/plugin-sdk/src/types.ts +++ b/packages/plugin-sdk/src/types.ts @@ -1,10 +1,4 @@ -import type { - AssetStatus, - AssetType, - AssetVisibility, - WorkflowTrigger, - WorkflowType, -} from 'src/enum.js'; +import type { AssetTypeEnum, AssetVisibility, WorkflowType } from '@immich/sdk'; type DeepPartial = T extends Date ? T @@ -21,6 +15,12 @@ export type WorkflowEventMap = { export type WorkflowEventData = WorkflowEventMap[T]; +export enum WorkflowTrigger { + AssetCreate = 'AssetCreate', + AssetMetadataExtraction = 'AssetMetadataExtraction', + PersonRecognized = 'PersonRecognized', +} + export type WorkflowEventPayload< T extends WorkflowType = WorkflowType, TConfig = WorkflowStepConfig, @@ -48,6 +48,8 @@ export type WorkflowResponse = { changes?: WorkflowChanges; /** data to be passed to the next workflow step */ data?: Record; + /** update step config */ + config?: WorkflowStepConfig; }; export type WorkflowStepConfig = { @@ -66,24 +68,23 @@ export type AssetV1 = { asset: { id: string; ownerId: string; - type: AssetType; + type: AssetTypeEnum; originalPath: string; - fileCreatedAt: Date; - fileModifiedAt: Date; + fileCreatedAt: string; + fileModifiedAt: string; isFavorite: boolean; checksum: Buffer; // sha1 checksum livePhotoVideoId: string | null; - updatedAt: Date; - createdAt: Date; + updatedAt: string; + createdAt: string; originalFileName: string; isOffline: boolean; libraryId: string | null; isExternal: boolean; - deletedAt: Date | null; - localDateTime: Date; + deletedAt: string | null; + localDateTime: string; stackId: string | null; duplicateId: string | null; - status: AssetStatus; visibility: AssetVisibility; isEdited: boolean; exifInfo: { @@ -93,8 +94,8 @@ export type AssetV1 = { exifImageHeight: number | null; fileSizeInByte: number | null; orientation: string | null; - dateTimeOriginal: Date | null; - modifyDate: Date | null; + dateTimeOriginal: string | null; + modifyDate: string | null; lensModel: string | null; fNumber: number | null; focalLength: number | null; @@ -116,7 +117,7 @@ export type AssetV1 = { autoStackId: string | null; rating: number | null; tags: string[] | null; - updatedAt: Date | null; + updatedAt: string | null; } | null; }; }; diff --git a/packages/sdk/src/fetch-client.ts b/packages/sdk/src/fetch-client.ts index 1ad39a0d54..fdae7f9a2f 100644 --- a/packages/sdk/src/fetch-client.ts +++ b/packages/sdk/src/fetch-client.ts @@ -298,6 +298,8 @@ export type MemoriesResponse = { export type PeopleResponse = { /** Whether people are enabled */ enabled: boolean; + /** People face threshold */ + minimumFaces?: number; /** Whether people appear in web sidebar */ sidebarWeb: boolean; }; @@ -375,6 +377,8 @@ export type MemoriesUpdate = { export type PeopleUpdate = { /** Whether people are enabled */ enabled?: boolean; + /** People face threshold */ + minimumFaces?: number; /** Whether people appear in web sidebar */ sidebarWeb?: boolean; }; @@ -1542,6 +1546,8 @@ export type PluginTemplateResponseDto = { title: string; /** Workflow trigger */ trigger: WorkflowTrigger; + /** Ui hints, for example "smart-album" */ + uiHints: string[]; }; export type QueueResponseDto = { /** Whether the queue is paused */ @@ -1968,6 +1974,8 @@ export type ServerConfigDto = { mapDarkStyleUrl: string; /** Map light style URL */ mapLightStyleUrl: string; + /** People min faces server default */ + minFaces: number; /** OAuth button text */ oauthButtonText: string; /** Whether public user registration is enabled */ @@ -1998,6 +2006,8 @@ export type ServerFeaturesDto = { ocr: boolean; /** Whether password login is enabled */ passwordLogin: boolean; + /** Whether real-time transcoding is enabled */ + realtimeTranscoding: boolean; /** Whether reverse geocoding is enabled */ reverseGeocoding: boolean; /** Whether search is enabled */ @@ -2081,6 +2091,8 @@ export type ServerVersionResponseDto = { minor: number; /** Patch version number */ patch: number; + /** Pre-release version number */ + prerelease: number | null; }; export type VersionCheckStateResponseDto = { /** Last check timestamp */ @@ -2256,6 +2268,10 @@ export type DatabaseBackupConfig = { export type SystemConfigBackupsDto = { database: DatabaseBackupConfig; }; +export type SystemConfigFFmpegRealtimeDto = { + /** Enable real-time HLS transcoding (alpha) */ + enabled: boolean; +}; export type SystemConfigFFmpegDto = { accel: TranscodeHWAccel; /** Accelerated decode */ @@ -2279,6 +2295,7 @@ export type SystemConfigFFmpegDto = { preferredHwDevice: string; /** Preset */ preset: string; + realtime: SystemConfigFFmpegRealtimeDto; /** References */ refs: number; targetAudioCodec: AudioCodec; @@ -2428,6 +2445,7 @@ export type SystemConfigMetadataDto = { faces: SystemConfigFacesDto; }; export type SystemConfigNewVersionCheckDto = { + channel: ReleaseChannel; /** Enabled */ enabled: boolean; }; @@ -2773,6 +2791,16 @@ export type WorkflowShareResponseDto = { trigger: WorkflowTrigger; }; export type LicenseResponseDto = UserLicense; +export type ReleaseEventV1 = { + /** When the server last checked for a latest version. As an ISO timestamp */ + checkedAt: string; + /** Whether a new version is available */ + isAvailable: boolean; + releaseVersion: ServerVersionResponseDto; + serverVersion: ServerVersionResponseDto; + /** Release type */ + "type": ReleaseType; +}; export type SyncAckV1 = {}; export type SyncAlbumDeleteV1 = { /** Album ID */ @@ -3604,18 +3632,22 @@ export function getUserStatisticsAdmin({ id, isFavorite, isTrashed, visibility } /** * List all albums */ -export function getAllAlbums({ assetId, isOwned, isShared }: { +export function getAllAlbums({ assetId, id, isOwned, isShared, name }: { assetId?: string; + id?: string; isOwned?: boolean; isShared?: boolean; + name?: string; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; data: AlbumResponseDto[]; }>(`/albums${QS.query(QS.explode({ assetId, + id, isOwned, - isShared + isShared, + name }))}`, { ...opts })); @@ -4213,6 +4245,82 @@ export function playAssetVideo({ id, key, slug }: { ...opts })); } +/** + * Get HLS main playlist + */ +export function getMainPlaylist({ id, key, slug }: { + id: string; + key?: string; + slug?: string; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchBlob<{ + status: 200; + data: string; + }>(`/assets/${encodeURIComponent(id)}/video/stream/main.m3u8${QS.query(QS.explode({ + key, + slug + }))}`, { + ...opts + })); +} +/** + * End HLS streaming session + */ +export function endSession({ id, key, sessionId, slug }: { + id: string; + key?: string; + sessionId: string; + slug?: string; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchText(`/assets/${encodeURIComponent(id)}/video/stream/${encodeURIComponent(sessionId)}${QS.query(QS.explode({ + key, + slug + }))}`, { + ...opts, + method: "DELETE" + })); +} +/** + * Get HLS media playlist + */ +export function getMediaPlaylist({ id, key, sessionId, slug, variantIndex }: { + id: string; + key?: string; + sessionId: string; + slug?: string; + variantIndex: number; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchBlob<{ + status: 200; + data: string; + }>(`/assets/${encodeURIComponent(id)}/video/stream/${encodeURIComponent(sessionId)}/${encodeURIComponent(variantIndex)}/playlist.m3u8${QS.query(QS.explode({ + key, + slug + }))}`, { + ...opts + })); +} +/** + * Get HLS segment or init file + */ +export function getSegment({ filename, id, key, sessionId, slug, variantIndex }: { + filename: string; + id: string; + key?: string; + sessionId: string; + slug?: string; + variantIndex: number; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchBlob<{ + status: 200; + data: Blob; + }>(`/assets/${encodeURIComponent(id)}/video/stream/${encodeURIComponent(sessionId)}/${encodeURIComponent(variantIndex)}/${encodeURIComponent(filename)}${QS.query(QS.explode({ + key, + slug + }))}`, { + ...opts + })); +} /** * Register admin */ @@ -7086,6 +7194,7 @@ export enum WorkflowType { } export enum WorkflowTrigger { AssetCreate = "AssetCreate", + AssetMetadataExtraction = "AssetMetadataExtraction", PersonRecognized = "PersonRecognized" } export enum QueueJobStatus { @@ -7126,6 +7235,7 @@ export enum JobName { LibrarySyncFilesQueueAll = "LibrarySyncFilesQueueAll", LibrarySyncFiles = "LibrarySyncFiles", LibraryScanQueueAll = "LibraryScanQueueAll", + HlsSessionCleanup = "HlsSessionCleanup", MemoryCleanup = "MemoryCleanup", MemoryGenerate = "MemoryGenerate", NotificationsCleanup = "NotificationsCleanup", @@ -7151,7 +7261,7 @@ export enum JobName { VersionCheck = "VersionCheck", OcrQueueAll = "OcrQueueAll", Ocr = "Ocr", - WorkflowAssetCreate = "WorkflowAssetCreate" + WorkflowAssetTrigger = "WorkflowAssetTrigger" } export enum SearchSuggestionType { Country = "country", @@ -7316,6 +7426,10 @@ export enum LogLevel { Error = "error", Fatal = "fatal" } +export enum ReleaseChannel { + Stable = "stable", + ReleaseCandidate = "releaseCandidate" +} export enum OAuthTokenEndpointAuthMethod { ClientSecretPost = "client_secret_post", ClientSecretBasic = "client_secret_basic" @@ -7324,6 +7438,15 @@ export enum AssetOrderBy { TakenAt = "takenAt", CreatedAt = "createdAt" } +export enum ReleaseType { + Major = "major", + Premajor = "premajor", + Minor = "minor", + Preminor = "preminor", + Patch = "patch", + Prepatch = "prepatch", + Prerelease = "prerelease" +} export enum UserMetadataKey { Preferences = "preferences", License = "license", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 64cf0d3d85..6aa01f8ace 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,7 +10,7 @@ overrides: sharp: ^0.34.5 webpackbar: ^7.0.0 -packageExtensionsChecksum: sha256-3l4AQg4iuprBDup+q+2JaPvbPg/7XodWCE0ZteH+s54= +packageExtensionsChecksum: sha256-W6pFzyf+6QXnV91iA6oob0OGVkergPXDN1afLgoF53k= pnpmfileChecksum: sha256-un98do36L0wZyqsjcLozQ3YUadCAn2yz5bXcBbOuyDA= @@ -320,6 +320,9 @@ importers: '@immich/plugin-sdk': specifier: workspace:* version: link:../plugin-sdk + '@immich/sdk': + specifier: workspace:* + version: link:../sdk esbuild: specifier: ^0.28.0 version: 0.28.0 @@ -332,6 +335,9 @@ importers: '@extism/js-pdk': specifier: ^1.1.1 version: 1.1.1 + '@immich/sdk': + specifier: workspace:* + version: link:../sdk '@types/node': specifier: ^24.12.4 version: 24.12.4 @@ -389,7 +395,7 @@ importers: version: 6.1.3(@nestjs/common@11.1.21(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.21) '@nestjs/swagger': specifier: ^11.4.2 - version: 11.4.3(@nestjs/common@11.1.21(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.21)(reflect-metadata@0.2.2) + version: 11.4.3(@nestjs/common@11.1.21(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.21)(reflect-metadata@0.2.2)(typescript@6.0.3) '@nestjs/websockets': specifier: ^11.0.4 version: 11.1.21(@nestjs/common@11.1.21(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.21)(@nestjs/platform-socket.io@11.1.21)(reflect-metadata@0.2.2)(rxjs@7.8.2) @@ -536,7 +542,7 @@ importers: version: 8.0.3(@nestjs/common@11.1.21(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.21) nestjs-zod: specifier: ^5.3.0 - version: 5.4.0(@nestjs/common@11.1.21(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/swagger@11.4.3(@nestjs/common@11.1.21(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.21)(reflect-metadata@0.2.2))(rxjs@7.8.2)(zod@4.3.6) + version: 5.4.0(@nestjs/common@11.1.21(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/swagger@11.4.3(@nestjs/common@11.1.21(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.21)(reflect-metadata@0.2.2)(typescript@6.0.3))(rxjs@7.8.2)(zod@4.3.6) nodemailer: specifier: ^8.0.0 version: 8.0.7 @@ -571,8 +577,8 @@ importers: specifier: ^1.6.3 version: 1.6.4 semver: - specifier: ^7.6.2 - version: 7.8.0 + specifier: ^7.8.1 + version: 7.8.1 sharp: specifier: ^0.34.5 version: 0.34.5 @@ -609,7 +615,7 @@ importers: version: 10.0.1(eslint@10.4.0(jiti@2.7.0)) '@nestjs/cli': specifier: ^11.0.2 - version: 11.0.21(@swc/core@1.15.33(@swc/helpers@0.5.22))(@types/node@24.12.4)(esbuild@0.28.0)(lightningcss@1.32.0)(prettier@3.8.3) + version: 11.0.21(@swc/core@1.15.33(@swc/helpers@0.5.23))(@types/node@24.12.4)(esbuild@0.28.0)(lightningcss@1.32.0)(prettier@3.8.3) '@nestjs/schematics': specifier: ^11.0.0 version: 11.1.0(chokidar@4.0.3)(prettier@3.8.3)(typescript@6.0.3) @@ -618,7 +624,7 @@ importers: version: 11.1.21(@nestjs/common@11.1.21(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.21)(@nestjs/platform-express@11.1.21) '@swc/core': specifier: ^1.4.14 - version: 1.15.33(@swc/helpers@0.5.22) + version: 1.15.33(@swc/helpers@0.5.23) '@types/archiver': specifier: ^7.0.0 version: 7.0.0 @@ -689,8 +695,8 @@ importers: specifier: ^13.15.2 version: 13.15.10 '@vitest/coverage-v8': - specifier: ^3.0.0 - version: 3.2.4(vitest@3.2.4(@types/debug@4.1.13)(@types/node@24.12.4)(happy-dom@20.9.0)(jiti@2.7.0)(jsdom@26.1.0(canvas@3.2.3))(lightningcss@1.32.0)(sass@1.99.0)(terser@5.47.1)(tsx@4.22.3)(yaml@2.9.0)) + specifier: ^4.0.0 + version: 4.1.7(vitest@3.2.4(@types/debug@4.1.13)(@types/node@24.12.4)(happy-dom@20.9.0)(jiti@2.7.0)(jsdom@26.1.0(canvas@3.2.3))(lightningcss@1.32.0)(sass@1.99.0)(terser@5.47.1)(tsx@4.22.3)(yaml@2.9.0)) eslint: specifier: ^10.0.0 version: 10.4.0(jiti@2.7.0) @@ -728,8 +734,8 @@ importers: specifier: ^3.4.0 version: 3.4.19(tsx@4.22.3)(yaml@2.9.0) testcontainers: - specifier: ^11.0.0 - version: 11.14.0 + specifier: ^12.0.0 + version: 12.0.1 typescript: specifier: ^6.0.0 version: 6.0.3 @@ -738,7 +744,7 @@ importers: version: 8.59.4(eslint@10.4.0(jiti@2.7.0))(typescript@6.0.3) unplugin-swc: specifier: ^1.4.5 - version: 1.5.9(@swc/core@1.15.33(@swc/helpers@0.5.22))(rollup@4.60.4) + version: 1.5.9(@swc/core@1.15.33(@swc/helpers@0.5.23))(rollup@4.60.4) vite-tsconfig-paths: specifier: ^6.0.0 version: 6.1.1(typescript@6.0.3)(vite@8.0.13(@types/node@24.12.4)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(tsx@4.22.3)(yaml@2.9.0)) @@ -759,7 +765,7 @@ importers: version: link:../packages/sdk '@immich/ui': specifier: ^0.79.2 - version: 0.79.2(@sveltejs/kit@2.60.1(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.55.8(@typescript-eslint/types@8.59.4))(vite@8.0.13(@types/node@24.12.4)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(tsx@4.22.3)(yaml@2.9.0)))(svelte@5.55.8(@typescript-eslint/types@8.59.4))(typescript@6.0.3)(vite@8.0.13(@types/node@24.12.4)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(tsx@4.22.3)(yaml@2.9.0)))(svelte@5.55.8(@typescript-eslint/types@8.59.4)) + version: 0.79.3(@sveltejs/kit@2.60.1(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.55.8(@typescript-eslint/types@8.59.4))(vite@8.0.13(@types/node@24.12.4)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(tsx@4.22.3)(yaml@2.9.0)))(svelte@5.55.8(@typescript-eslint/types@8.59.4))(typescript@6.0.3)(vite@8.0.13(@types/node@24.12.4)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(tsx@4.22.3)(yaml@2.9.0)))(svelte@5.55.8(@typescript-eslint/types@8.59.4)) '@mapbox/mapbox-gl-rtl-text': specifier: 0.4.0 version: 0.4.0 @@ -814,6 +820,12 @@ importers: happy-dom: specifier: ^20.0.0 version: 20.9.0 + hls-video-element: + specifier: ^1.5.11 + version: 1.5.11 + hls.js: + specifier: ^1.6.16 + version: 1.6.16 intl-messageformat: specifier: ^11.0.0 version: 11.2.6 @@ -916,7 +928,7 @@ importers: version: 14.6.1(@testing-library/dom@10.4.1) '@trivago/prettier-plugin-sort-imports': specifier: ^6.0.2 - version: 6.0.2(prettier-plugin-svelte@3.5.2(prettier@3.8.3)(svelte@5.55.8(@typescript-eslint/types@8.59.4)))(prettier@3.8.3)(svelte@5.55.8(@typescript-eslint/types@8.59.4)) + version: 6.0.2(prettier-plugin-svelte@4.1.0(prettier@3.8.3)(svelte@5.55.8(@typescript-eslint/types@8.59.4)))(prettier@3.8.3)(svelte@5.55.8(@typescript-eslint/types@8.59.4)) '@types/chromecast-caf-sender': specifier: ^1.0.11 version: 1.0.11 @@ -972,8 +984,8 @@ importers: specifier: ^4.1.1 version: 4.2.0(prettier@3.8.3) prettier-plugin-svelte: - specifier: ^3.3.3 - version: 3.5.2(prettier@3.8.3)(svelte@5.55.8(@typescript-eslint/types@8.59.4)) + specifier: ^4.0.0 + version: 4.1.0(prettier@3.8.3)(svelte@5.55.8(@typescript-eslint/types@8.59.4)) rollup-plugin-visualizer: specifier: ^7.0.0 version: 7.0.1(rolldown@1.0.1)(rollup@4.60.4) @@ -1098,10 +1110,6 @@ packages: 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.24': resolution: {integrity: sha512-Kd49warf6U/EyWe5BszF/eebN3zQ3bk7tgfEljAw8q/rX95UUtriJubWvp6pgzHfzBA4jwq8f+QiNZB8eBEXPA==} 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'} @@ -1687,10 +1695,6 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/runtime@7.29.2': - resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} - engines: {node: '>=6.9.0'} - '@babel/runtime@7.29.7': resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==} engines: {node: '>=6.9.0'} @@ -3208,8 +3212,8 @@ packages: resolution: {integrity: sha512-O1SJ+BbeFVsUTF4af1MfagJZM+lPgLjI8lQ3SZNjpo8SGJReSbUl2ii03OKuGni/G0yp2GnRLpOTNSHYGtVrcg==} hasBin: true - '@immich/ui@0.79.2': - resolution: {integrity: sha512-tnpYhYHrjrFJK18QglRMzPUtHv6q5V6tW38HiAraQJBv7MCg+yaJDrdF8omM2L5F311FGlv1PZLJYvmR4e49PA==} + '@immich/ui@0.79.3': + resolution: {integrity: sha512-QO2gLcmAIVLvcv3eb6RZ5kDahmVMDZlfE2RfbpyRKNI1wTjvTLP5ibIsofY0fXxLf44cN3vcjmz16CXS8bvVWQ==} peerDependencies: '@sveltejs/kit': ^2.13.0 svelte: ^5.0.0 @@ -3357,8 +3361,8 @@ packages: '@types/node': optional: true - '@internationalized/date@3.12.1': - resolution: {integrity: sha512-6IedsVWXyq4P9Tj+TxuU8WGWM70hYLl12nbYU8jkikVpa6WXapFazPUcHUMDMoWftIDE2ILDkFFte6W2nFCkRQ==} + '@internationalized/date@3.12.2': + resolution: {integrity: sha512-FY1Y+H64NDs+HAF6omlnWxm3mEpfgaCSWtL5l551ZZfImA+kGjPFgrnJrGjH6lfmLL0g8Z/mBu1R3kufeCp6Jw==} '@ioredis/commands@1.5.1': resolution: {integrity: sha512-JH8ZL/ywcJyR9MmJ5BNqZllXNZQqQbnVZOqpPQqE1vHiFgAw4NHbvE0FOduNU8IX9babitBT46571OnPTT0Zcw==} @@ -3371,10 +3375,6 @@ packages: resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} engines: {node: '>=18.0.0'} - '@istanbuljs/schema@0.1.6': - resolution: {integrity: sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==} - 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} @@ -3795,6 +3795,7 @@ packages: class-transformer: '*' class-validator: '*' reflect-metadata: ^0.1.12 || ^0.2.0 + typescript: '*' peerDependenciesMeta: '@fastify/static': optional: true @@ -4986,8 +4987,8 @@ packages: '@swc/counter@0.1.3': resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} - '@swc/helpers@0.5.22': - resolution: {integrity: sha512-/e2Ly3Docn9kYByap6TV4oquJ3wQuz3c+kC74riqtkwU9CwTMeuj6t2rW+bRr4pyOx/CYQM4wr0RgaKQwGEz0A==} + '@swc/helpers@0.5.23': + resolution: {integrity: sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==} '@swc/types@0.1.26': resolution: {integrity: sha512-lyMwd7WGgG79RS7EERZV3T8wMdmPq3xwyg+1nmAM64kIhx5yl+juO2PYIHb7vTiPgPCj8LYjsNV2T5wiQHUEaw==} @@ -5683,15 +5684,6 @@ packages: peerDependencies: valibot: ^1.4.0 - '@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/coverage-v8@4.1.7': resolution: {integrity: sha512-qsYPeXc5Q9dFLd1i8Ap+Bx8sQgcp+rFVQo4R0dDsWNBzl26ldVF1qOO+RL24K7FDrR6pA+50XedRLSoSG24bVQ==} peerDependencies: @@ -6042,9 +6034,6 @@ packages: ast-metadata-inferer@0.8.1: resolution: {integrity: sha512-ht3Dm6Zr7SXv6t1Ra6gFo0+kLDglHGrEbYihTkcycrbHw7WCcuhBzPlJYHEsIpycaUwzsJHje+vUcxXUX4ztTA==} - ast-v8-to-istanbul@0.3.12: - resolution: {integrity: sha512-BRRC8VRZY2R4Z4lFIL35MwNXmwVqBityvOIwETtsCSwvjl0IdgFsy9NhdaA6j74nUdtJJlIypeRhpDam19Wq3g==} - ast-v8-to-istanbul@1.0.0: resolution: {integrity: sha512-1fSfIwuDICFA4LKkCzRPO7F0hzFf0B7+Xqrl27ynQaa+Rh0e1Es0v6kWHPott3lU10AyAr7oKHa65OppjLn3Rg==} @@ -6940,6 +6929,9 @@ packages: csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + custom-media-element@1.4.6: + resolution: {integrity: sha512-/HRYqJOa1ob5ik4q7FIJVYxTJCFs/FL3+cQPAJjUf2uiqrDEzbTgB315gQ2rG8oK3w094W9m5tcB8S5Qah+caA==} + cytoscape-cose-bilkent@4.1.0: resolution: {integrity: sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==} peerDependencies: @@ -7300,9 +7292,9 @@ packages: resolution: {integrity: sha512-XJgGhoR/CLpqshm4d3L7rzH6t8NgDFUIIpztYlLHIApeJjMZKYJMz2zxPsYxnejq5h3ELYSw/RBsi3t5h7gNTA==} engines: {node: '>= 8.0'} - dockerode@4.0.12: - resolution: {integrity: sha512-/bCZd6KlGcjZO8Buqmi/vXuqEGVEZ0PNjx/biBNqJD3MhK9DmdiAuKxqfNhflgDESDIiBz3qF+0e55+CpnrUcw==} - engines: {node: '>= 8.0'} + dockerode@5.0.0: + resolution: {integrity: sha512-C52mvJ+7lcyhWNfrzVfFsbTrBfy/ezE9FGEYLpu17FUeBcCkxERk9nN7uDl/478ynDiQ4U+5DbQC2vENHkVEtQ==} + engines: {node: '>= 14.17'} docusaurus-lunr-search@3.6.0: resolution: {integrity: sha512-CCEAnj5e67sUZmIb2hOl4xb4nDN07fb0fvRDDmdWlYpUvyS1CSKbw4lsGInLyUFEEEBzxQmT6zaVQdF/8Zretg==} @@ -8264,6 +8256,12 @@ packages: history@4.10.1: resolution: {integrity: sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==} + hls-video-element@1.5.11: + resolution: {integrity: sha512-tJJ65/52CDxj8XFyIve6zT9nVVdUIc6mqvKR25X0ycPKHk07rpjp4xxVteeCefDUBSf/tFLhlICFmn3KWj37xA==} + + hls.js@1.6.16: + resolution: {integrity: sha512-VSIRpLfRwlAAdGL4wiTucx2ScRipo0ed1FBatWkyt832jC4CReKstga6yIhYVwGu9LOBjuX9wzmRMeQdBJtzEA==} + hogan.js@3.0.2: resolution: {integrity: sha512-RqGs4wavGYJWE07t35JQccByczmNUXQT0E12ZYV1VKYu5UiAU9lsos/yBAcf840+zrUQQxgVduCR5/B8nNtibg==} hasBin: true @@ -8706,10 +8704,6 @@ packages: 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.2.0: resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} engines: {node: '>=8'} @@ -9160,9 +9154,6 @@ packages: magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} - magicast@0.3.5: - resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==} - magicast@0.5.3: resolution: {integrity: sha512-pVKE4UdSQ7DvHzivsCIFx2BJn1mHG6KsyrFcaxFx6tONdneEuThrDx0Cj3AMg58KyN4pzYT+LHOotxDQDjNvkw==} @@ -9268,6 +9259,9 @@ packages: media-chrome@4.19.0: resolution: {integrity: sha512-HWhDTwts+BSbdPkkB1VsJXp5kvL0IxY7xFT5tBwliM2+89kTPVTnHnev+9it2f9PweANjT/C8/C/S0PW9oyZbA==} + media-tracks@0.3.5: + resolution: {integrity: sha512-l54rkKXlLBt3ob3zOLWHcnjvwUmX5bNEZ70igyapOZZC9imzqBmq1oz8p2roiV04KhjblFIi2hetLPF1oYVLRA==} + media-typer@0.3.0: resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} engines: {node: '>= 0.6'} @@ -10684,11 +10678,12 @@ packages: peerDependencies: prettier: ^3.0.0 - prettier-plugin-svelte@3.5.2: - resolution: {integrity: sha512-ItFouLvzSFE3ulNl4DKoWM3BGcbDCNVpIyy/Y3F2gC3aNiGLxtFUdffVqO5Z5hhYG+DFT5KULWaxmeFFpdbvaQ==} + prettier-plugin-svelte@4.1.0: + resolution: {integrity: sha512-YZkhA2Q9oOerFFG9tq+2f98WYT7Z2JgrybJrAyrB78jpsH9i/DdgplXemehuFPgsldetFNCcR/yCcYlDjPy94Q==} + engines: {node: '>=20'} peerDependencies: prettier: ^3.0.0 - svelte: ^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0 + svelte: ^5.0.0 prettier@3.8.3: resolution: {integrity: sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==} @@ -11243,6 +11238,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.8.1: + resolution: {integrity: sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==} + engines: {node: '>=10'} + hasBin: true + send@0.19.2: resolution: {integrity: sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==} engines: {node: '>= 0.8.0'} @@ -11856,12 +11856,8 @@ packages: engines: {node: '>=10'} hasBin: true - test-exclude@7.0.2: - resolution: {integrity: sha512-u9E6A+ZDYdp7a4WnarkXPZOx8Ilz46+kby6p1yZ8zsGTz9gYa6FIS7lj2oezzNKmtdyyJNNmmXDppga5GB7kSw==} - engines: {node: '>=18'} - - testcontainers@11.14.0: - resolution: {integrity: sha512-r9pniwv/iwzyHaI7gwAvAm4Y+IvjJg3vBWdjrUCaDMc2AXIr4jKbq7jJO18Mw2ybs73pZy1Aj7p/4RVBGMRWjg==} + testcontainers@12.0.1: + resolution: {integrity: sha512-EMjjfMNJf3HlL7V3elkxqKUO1r3CtqNBTdmKGwwma/lOtUGfoWvFJ0WQ/KQf1DHEMnRjLWzW4cXbv/Tndsbcbw==} text-decoder@1.2.7: resolution: {integrity: sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==} @@ -11951,8 +11947,8 @@ packages: resolution: {integrity: sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==} hasBin: true - tmp@0.2.5: - resolution: {integrity: sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==} + tmp@0.2.7: + resolution: {integrity: sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==} engines: {node: '>=14.14'} to-regex-range@5.0.1: @@ -12293,11 +12289,6 @@ packages: resolution: {integrity: sha512-6S5mCapmzcxetOD/2UEjL0GF5e4+gB07Dh8qs63xylw5ay4XuyW6iQs70FOJo/puf10LCkvhp4jYMQSDUBYEFg==} engines: {node: '>=10.0.0'} - uuid@10.0.0: - resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} - deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). - hasBin: true - uuid@14.0.0: resolution: {integrity: sha512-Qo+uWgilfSmAhXCMav1uYFynlQO7fMFiMVZsQqZRMIXp0O7rR7qjkj+cPvBHLgBqi960QCoo/PH2/6ZtVqKvrg==} hasBin: true @@ -13000,11 +12991,6 @@ snapshots: '@alloc/quick-lru@5.2.0': {} - '@ampproject/remapping@2.3.0': - dependencies: - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.31 - '@angular-devkit/core@19.2.24(chokidar@4.0.3)': dependencies: ajv: 8.18.0 @@ -13789,8 +13775,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/runtime@7.29.2': {} - '@babel/runtime@7.29.7': {} '@babel/template@7.28.6': @@ -14234,7 +14218,7 @@ snapshots: '@babel/preset-env': 7.29.5(@babel/core@7.29.0) '@babel/preset-react': 7.28.5(@babel/core@7.29.0) '@babel/preset-typescript': 7.28.5(@babel/core@7.29.0) - '@babel/runtime': 7.29.2 + '@babel/runtime': 7.29.7 '@babel/traverse': 7.29.0 '@docusaurus/logger': 3.10.1 '@docusaurus/utils': 3.10.1(clean-css@5.3.3)(cssnano@6.1.2(postcss@8.5.15))(html-minifier-terser@7.2.0)(postcss@8.5.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) @@ -14268,7 +14252,7 @@ snapshots: '@babel/preset-env': 7.29.5(@babel/core@7.29.0) '@babel/preset-react': 7.28.5(@babel/core@7.29.0) '@babel/preset-typescript': 7.28.5(@babel/core@7.29.0) - '@babel/runtime': 7.29.2 + '@babel/runtime': 7.29.7 '@babel/traverse': 7.29.0 '@docusaurus/logger': 3.10.1 '@docusaurus/utils': 3.10.1(postcss@8.5.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) @@ -14374,7 +14358,7 @@ snapshots: react-router: 5.3.4(react@19.2.6) react-router-config: 5.1.1(react-router@5.3.4(react@19.2.6))(react@19.2.6) react-router-dom: 5.3.4(react@19.2.6) - semver: 7.8.0 + semver: 7.8.1 serve-handler: 6.1.7 tinypool: 1.1.1 tslib: 2.8.1 @@ -15885,12 +15869,12 @@ snapshots: pg-connection-string: 2.13.0 postgres: 3.4.9 - '@immich/ui@0.79.2(@sveltejs/kit@2.60.1(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.55.8(@typescript-eslint/types@8.59.4))(vite@8.0.13(@types/node@24.12.4)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(tsx@4.22.3)(yaml@2.9.0)))(svelte@5.55.8(@typescript-eslint/types@8.59.4))(typescript@6.0.3)(vite@8.0.13(@types/node@24.12.4)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(tsx@4.22.3)(yaml@2.9.0)))(svelte@5.55.8(@typescript-eslint/types@8.59.4))': + '@immich/ui@0.79.3(@sveltejs/kit@2.60.1(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.55.8(@typescript-eslint/types@8.59.4))(vite@8.0.13(@types/node@24.12.4)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(tsx@4.22.3)(yaml@2.9.0)))(svelte@5.55.8(@typescript-eslint/types@8.59.4))(typescript@6.0.3)(vite@8.0.13(@types/node@24.12.4)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(tsx@4.22.3)(yaml@2.9.0)))(svelte@5.55.8(@typescript-eslint/types@8.59.4))': dependencies: - '@internationalized/date': 3.12.1 + '@internationalized/date': 3.12.2 '@mdi/js': 7.4.47 '@sveltejs/kit': 2.60.1(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.55.8(@typescript-eslint/types@8.59.4))(vite@8.0.13(@types/node@24.12.4)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(tsx@4.22.3)(yaml@2.9.0)))(svelte@5.55.8(@typescript-eslint/types@8.59.4))(typescript@6.0.3)(vite@8.0.13(@types/node@24.12.4)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(tsx@4.22.3)(yaml@2.9.0)) - bits-ui: 2.18.1(@internationalized/date@3.12.1)(@sveltejs/kit@2.60.1(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.55.8(@typescript-eslint/types@8.59.4))(vite@8.0.13(@types/node@24.12.4)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(tsx@4.22.3)(yaml@2.9.0)))(svelte@5.55.8(@typescript-eslint/types@8.59.4))(typescript@6.0.3)(vite@8.0.13(@types/node@24.12.4)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(tsx@4.22.3)(yaml@2.9.0)))(svelte@5.55.8(@typescript-eslint/types@8.59.4)) + bits-ui: 2.18.1(@internationalized/date@3.12.2)(@sveltejs/kit@2.60.1(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.55.8(@typescript-eslint/types@8.59.4))(vite@8.0.13(@types/node@24.12.4)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(tsx@4.22.3)(yaml@2.9.0)))(svelte@5.55.8(@typescript-eslint/types@8.59.4))(typescript@6.0.3)(vite@8.0.13(@types/node@24.12.4)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(tsx@4.22.3)(yaml@2.9.0)))(svelte@5.55.8(@typescript-eslint/types@8.59.4)) luxon: 3.7.2 simple-icons: 16.20.0 svelte: 5.55.8(@typescript-eslint/types@8.59.4) @@ -16039,9 +16023,9 @@ snapshots: optionalDependencies: '@types/node': 24.12.4 - '@internationalized/date@3.12.1': + '@internationalized/date@3.12.2': dependencies: - '@swc/helpers': 0.5.22 + '@swc/helpers': 0.5.23 '@ioredis/commands@1.5.1': {} @@ -16058,8 +16042,6 @@ snapshots: dependencies: minipass: 7.1.3 - '@istanbuljs/schema@0.1.6': {} - '@jest/schemas@29.6.3': dependencies: '@sinclair/typebox': 0.27.10 @@ -16300,7 +16282,7 @@ snapshots: nopt: 5.0.0 npmlog: 5.0.1 rimraf: 3.0.2 - semver: 7.8.0 + semver: 7.8.1 tar: 6.2.1 transitivePeerDependencies: - encoding @@ -16444,7 +16426,7 @@ snapshots: bullmq: 5.76.10 tslib: 2.8.1 - '@nestjs/cli@11.0.21(@swc/core@1.15.33(@swc/helpers@0.5.22))(@types/node@24.12.4)(esbuild@0.28.0)(lightningcss@1.32.0)(prettier@3.8.3)': + '@nestjs/cli@11.0.21(@swc/core@1.15.33(@swc/helpers@0.5.23))(@types/node@24.12.4)(esbuild@0.28.0)(lightningcss@1.32.0)(prettier@3.8.3)': dependencies: '@angular-devkit/core': 19.2.24(chokidar@4.0.3) '@angular-devkit/schematics': 19.2.24(chokidar@4.0.3) @@ -16455,17 +16437,17 @@ snapshots: chokidar: 4.0.3 cli-table3: 0.6.5 commander: 4.1.1 - fork-ts-checker-webpack-plugin: 9.1.0(typescript@5.9.3)(webpack@5.106.0(@swc/core@1.15.33(@swc/helpers@0.5.22))(esbuild@0.28.0)(lightningcss@1.32.0)) + fork-ts-checker-webpack-plugin: 9.1.0(typescript@5.9.3)(webpack@5.106.0(@swc/core@1.15.33(@swc/helpers@0.5.23))(esbuild@0.28.0)(lightningcss@1.32.0)) glob: 13.0.6 node-emoji: 1.11.0 ora: 5.4.1 tsconfig-paths: 4.2.0 tsconfig-paths-webpack-plugin: 4.2.0 typescript: 5.9.3 - webpack: 5.106.0(@swc/core@1.15.33(@swc/helpers@0.5.22))(esbuild@0.28.0)(lightningcss@1.32.0) + webpack: 5.106.0(@swc/core@1.15.33(@swc/helpers@0.5.23))(esbuild@0.28.0)(lightningcss@1.32.0) webpack-node-externals: 3.0.0 optionalDependencies: - '@swc/core': 1.15.33(@swc/helpers@0.5.22) + '@swc/core': 1.15.33(@swc/helpers@0.5.23) transitivePeerDependencies: - '@minify-html/node' - '@swc/css' @@ -16570,7 +16552,7 @@ snapshots: transitivePeerDependencies: - chokidar - '@nestjs/swagger@11.4.3(@nestjs/common@11.1.21(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.21)(reflect-metadata@0.2.2)': + '@nestjs/swagger@11.4.3(@nestjs/common@11.1.21(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.21)(reflect-metadata@0.2.2)(typescript@6.0.3)': dependencies: '@microsoft/tsdoc': 0.16.0 '@nestjs/common': 11.1.21(reflect-metadata@0.2.2)(rxjs@7.8.2) @@ -16581,6 +16563,7 @@ snapshots: path-to-regexp: 8.4.2 reflect-metadata: 0.2.2 swagger-ui-dist: 5.32.6 + typescript: 6.0.3 '@nestjs/testing@11.1.21(@nestjs/common@11.1.21(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.21)(@nestjs/platform-express@11.1.21)': dependencies: @@ -17653,7 +17636,7 @@ snapshots: '@swc/core-win32-x64-msvc@1.15.33': optional: true - '@swc/core@1.15.33(@swc/helpers@0.5.22)': + '@swc/core@1.15.33(@swc/helpers@0.5.23)': dependencies: '@swc/counter': 0.1.3 '@swc/types': 0.1.26 @@ -17670,11 +17653,11 @@ snapshots: '@swc/core-win32-arm64-msvc': 1.15.33 '@swc/core-win32-ia32-msvc': 1.15.33 '@swc/core-win32-x64-msvc': 1.15.33 - '@swc/helpers': 0.5.22 + '@swc/helpers': 0.5.23 '@swc/counter@0.1.3': {} - '@swc/helpers@0.5.22': + '@swc/helpers@0.5.23': dependencies: tslib: 2.8.1 @@ -17757,7 +17740,7 @@ snapshots: '@testing-library/dom@10.4.1': dependencies: '@babel/code-frame': 7.29.0 - '@babel/runtime': 7.29.2 + '@babel/runtime': 7.29.7 '@types/aria-query': 5.0.4 aria-query: 5.3.0 dom-accessibility-api: 0.5.16 @@ -17800,7 +17783,7 @@ snapshots: '@tokenizer/token@0.3.0': {} - '@trivago/prettier-plugin-sort-imports@6.0.2(prettier-plugin-svelte@3.5.2(prettier@3.8.3)(svelte@5.55.8(@typescript-eslint/types@8.59.4)))(prettier@3.8.3)(svelte@5.55.8(@typescript-eslint/types@8.59.4))': + '@trivago/prettier-plugin-sort-imports@6.0.2(prettier-plugin-svelte@4.1.0(prettier@3.8.3)(svelte@5.55.8(@typescript-eslint/types@8.59.4)))(prettier@3.8.3)(svelte@5.55.8(@typescript-eslint/types@8.59.4))': dependencies: '@babel/generator': 7.29.1 '@babel/parser': 7.29.3 @@ -17812,7 +17795,7 @@ snapshots: parse-imports-exports: 0.2.4 prettier: 3.8.3 optionalDependencies: - prettier-plugin-svelte: 3.5.2(prettier@3.8.3)(svelte@5.55.8(@typescript-eslint/types@8.59.4)) + prettier-plugin-svelte: 4.1.0(prettier@3.8.3)(svelte@5.55.8(@typescript-eslint/types@8.59.4)) svelte: 5.55.8(@typescript-eslint/types@8.59.4) transitivePeerDependencies: - supports-color @@ -18461,7 +18444,7 @@ snapshots: '@typescript-eslint/visitor-keys': 8.59.4 debug: 4.4.3 minimatch: 10.2.5 - semver: 7.8.0 + semver: 7.8.1 tinyglobby: 0.2.16 ts-api-utils: 2.5.0(typescript@6.0.3) typescript: 6.0.3 @@ -18495,24 +18478,19 @@ snapshots: dependencies: valibot: 1.4.0(typescript@6.0.3) - '@vitest/coverage-v8@3.2.4(vitest@3.2.4(@types/debug@4.1.13)(@types/node@24.12.4)(happy-dom@20.9.0)(jiti@2.7.0)(jsdom@26.1.0(canvas@3.2.3))(lightningcss@1.32.0)(sass@1.99.0)(terser@5.47.1)(tsx@4.22.3)(yaml@2.9.0))': + '@vitest/coverage-v8@4.1.7(vitest@3.2.4(@types/debug@4.1.13)(@types/node@24.12.4)(happy-dom@20.9.0)(jiti@2.7.0)(jsdom@26.1.0(canvas@3.2.3))(lightningcss@1.32.0)(sass@1.99.0)(terser@5.47.1)(tsx@4.22.3)(yaml@2.9.0))': dependencies: - '@ampproject/remapping': 2.3.0 '@bcoe/v8-coverage': 1.0.2 - ast-v8-to-istanbul: 0.3.12 - debug: 4.4.3 + '@vitest/utils': 4.1.7 + ast-v8-to-istanbul: 1.0.0 istanbul-lib-coverage: 3.2.2 istanbul-lib-report: 3.0.1 - istanbul-lib-source-maps: 5.0.6 istanbul-reports: 3.2.0 - magic-string: 0.30.21 - magicast: 0.3.5 - std-env: 3.10.0 - test-exclude: 7.0.2 - tinyrainbow: 2.0.0 + magicast: 0.5.3 + obug: 2.1.1 + std-env: 4.1.0 + tinyrainbow: 3.1.0 vitest: 3.2.4(@types/debug@4.1.13)(@types/node@24.12.4)(happy-dom@20.9.0)(jiti@2.7.0)(jsdom@26.1.0(canvas@3.2.3))(lightningcss@1.32.0)(sass@1.99.0)(terser@5.47.1)(tsx@4.22.3)(yaml@2.9.0) - transitivePeerDependencies: - - supports-color '@vitest/coverage-v8@4.1.7(vitest@4.1.7)': dependencies: @@ -18926,12 +18904,6 @@ snapshots: dependencies: '@mdn/browser-compat-data': 5.7.6 - ast-v8-to-istanbul@0.3.12: - dependencies: - '@jridgewell/trace-mapping': 0.3.31 - estree-walker: 3.0.3 - js-tokens: 10.0.0 - ast-v8-to-istanbul@1.0.0: dependencies: '@jridgewell/trace-mapping': 0.3.31 @@ -19079,11 +19051,11 @@ snapshots: binary-extensions@2.3.0: {} - bits-ui@2.18.1(@internationalized/date@3.12.1)(@sveltejs/kit@2.60.1(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.55.8(@typescript-eslint/types@8.59.4))(vite@8.0.13(@types/node@24.12.4)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(tsx@4.22.3)(yaml@2.9.0)))(svelte@5.55.8(@typescript-eslint/types@8.59.4))(typescript@6.0.3)(vite@8.0.13(@types/node@24.12.4)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(tsx@4.22.3)(yaml@2.9.0)))(svelte@5.55.8(@typescript-eslint/types@8.59.4)): + bits-ui@2.18.1(@internationalized/date@3.12.2)(@sveltejs/kit@2.60.1(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.55.8(@typescript-eslint/types@8.59.4))(vite@8.0.13(@types/node@24.12.4)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(tsx@4.22.3)(yaml@2.9.0)))(svelte@5.55.8(@typescript-eslint/types@8.59.4))(typescript@6.0.3)(vite@8.0.13(@types/node@24.12.4)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(tsx@4.22.3)(yaml@2.9.0)))(svelte@5.55.8(@typescript-eslint/types@8.59.4)): dependencies: '@floating-ui/core': 1.7.5 '@floating-ui/dom': 1.7.6 - '@internationalized/date': 3.12.1 + '@internationalized/date': 3.12.2 esm-env: 1.2.2 runed: 0.35.1(@sveltejs/kit@2.60.1(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.55.8(@typescript-eslint/types@8.59.4))(vite@8.0.13(@types/node@24.12.4)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(tsx@4.22.3)(yaml@2.9.0)))(svelte@5.55.8(@typescript-eslint/types@8.59.4))(typescript@6.0.3)(vite@8.0.13(@types/node@24.12.4)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(tsx@4.22.3)(yaml@2.9.0)))(svelte@5.55.8(@typescript-eslint/types@8.59.4)) svelte: 5.55.8(@typescript-eslint/types@8.59.4) @@ -19561,7 +19533,7 @@ snapshots: dot-prop: 10.1.0 env-paths: 3.0.0 json-schema-typed: 8.0.2 - semver: 7.8.0 + semver: 7.8.1 uint8array-extras: 1.5.0 config-chain@1.1.13: @@ -19733,7 +19705,7 @@ snapshots: postcss-modules-scope: 3.2.1(postcss@8.5.15) postcss-modules-values: 4.0.0(postcss@8.5.15) postcss-value-parser: 4.2.0 - semver: 7.8.0 + semver: 7.8.1 optionalDependencies: webpack: 5.107.0(postcss@8.5.15) @@ -19856,6 +19828,8 @@ snapshots: csstype@3.2.3: {} + custom-media-element@1.4.6: {} + cytoscape-cose-bilkent@4.1.0(cytoscape@3.33.4): dependencies: cose-base: 1.0.3 @@ -20201,7 +20175,7 @@ snapshots: transitivePeerDependencies: - supports-color - dockerode@4.0.12: + dockerode@5.0.0: dependencies: '@balena/dockerignore': 1.0.2 '@grpc/grpc-js': 1.14.3 @@ -20209,7 +20183,6 @@ snapshots: docker-modem: 5.0.7 protobufjs: 7.6.0 tar-fs: 2.1.4 - uuid: 10.0.0 transitivePeerDependencies: - supports-color @@ -20601,7 +20574,7 @@ snapshots: find-up: 5.0.0 globals: 15.15.0 lodash.memoize: 4.1.2 - semver: 7.8.0 + semver: 7.8.1 eslint-plugin-prettier@5.5.5(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@10.4.0(jiti@2.7.0)))(eslint@10.4.0(jiti@2.7.0))(prettier@3.8.3): dependencies: @@ -20624,7 +20597,7 @@ snapshots: postcss: 8.5.15 postcss-load-config: 3.1.4(postcss@8.5.15) postcss-safe-parser: 7.0.1(postcss@8.5.15) - semver: 7.8.0 + semver: 7.8.1 svelte-eslint-parser: 1.6.1(svelte@5.55.8(@typescript-eslint/types@8.59.4)) optionalDependencies: svelte: 5.55.8(@typescript-eslint/types@8.59.4) @@ -20648,7 +20621,7 @@ snapshots: pluralize: 8.0.0 regexp-tree: 0.1.27 regjsparser: 0.13.1 - semver: 7.8.0 + semver: 7.8.1 strip-indent: 4.1.1 eslint-scope@5.1.1: @@ -21090,7 +21063,7 @@ snapshots: cross-spawn: 7.0.6 signal-exit: 4.1.0 - fork-ts-checker-webpack-plugin@9.1.0(typescript@5.9.3)(webpack@5.106.0(@swc/core@1.15.33(@swc/helpers@0.5.22))(esbuild@0.28.0)(lightningcss@1.32.0)): + fork-ts-checker-webpack-plugin@9.1.0(typescript@5.9.3)(webpack@5.106.0(@swc/core@1.15.33(@swc/helpers@0.5.23))(esbuild@0.28.0)(lightningcss@1.32.0)): dependencies: '@babel/code-frame': 7.29.0 chalk: 4.1.2 @@ -21102,10 +21075,10 @@ snapshots: minimatch: 3.1.5 node-abort-controller: 3.1.1 schema-utils: 3.3.0 - semver: 7.8.0 + semver: 7.8.1 tapable: 2.3.3 typescript: 5.9.3 - webpack: 5.106.0(@swc/core@1.15.33(@swc/helpers@0.5.22))(esbuild@0.28.0)(lightningcss@1.32.0) + webpack: 5.106.0(@swc/core@1.15.33(@swc/helpers@0.5.23))(esbuild@0.28.0)(lightningcss@1.32.0) form-data-encoder@2.1.4: {} @@ -21538,13 +21511,21 @@ snapshots: history@4.10.1: dependencies: - '@babel/runtime': 7.29.2 + '@babel/runtime': 7.29.7 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 + hls-video-element@1.5.11: + dependencies: + custom-media-element: 1.4.6 + hls.js: 1.6.16 + media-tracks: 0.3.5 + + hls.js@1.6.16: {} + hogan.js@3.0.2: dependencies: mkdirp: 0.3.0 @@ -21969,14 +21950,6 @@ snapshots: make-dir: 4.0.0 supports-color: 7.2.0 - istanbul-lib-source-maps@5.0.6: - dependencies: - '@jridgewell/trace-mapping': 0.3.31 - debug: 4.4.3 - istanbul-lib-coverage: 3.2.2 - transitivePeerDependencies: - - supports-color - istanbul-reports@3.2.0: dependencies: html-escaper: 2.0.2 @@ -22126,7 +22099,7 @@ snapshots: lodash.isstring: 4.0.1 lodash.once: 4.1.1 ms: 2.1.3 - semver: 7.8.0 + semver: 7.8.1 just-compare@2.3.0: {} @@ -22394,12 +22367,6 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 - magicast@0.3.5: - dependencies: - '@babel/parser': 7.29.3 - '@babel/types': 7.29.0 - source-map-js: 1.2.1 - magicast@0.5.3: dependencies: '@babel/parser': 7.29.3 @@ -22412,7 +22379,7 @@ snapshots: make-dir@4.0.0: dependencies: - semver: 7.8.0 + semver: 7.8.1 maplibre-gl@5.24.0: dependencies: @@ -22648,6 +22615,8 @@ snapshots: transitivePeerDependencies: - react + media-tracks@0.3.5: {} + media-typer@0.3.0: {} media-typer@1.1.0: {} @@ -23229,14 +23198,14 @@ snapshots: '@opentelemetry/host-metrics': 0.38.3(@opentelemetry/api@1.9.1) tslib: 2.8.1 - nestjs-zod@5.4.0(@nestjs/common@11.1.21(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/swagger@11.4.3(@nestjs/common@11.1.21(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.21)(reflect-metadata@0.2.2))(rxjs@7.8.2)(zod@4.3.6): + nestjs-zod@5.4.0(@nestjs/common@11.1.21(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/swagger@11.4.3(@nestjs/common@11.1.21(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.21)(reflect-metadata@0.2.2)(typescript@6.0.3))(rxjs@7.8.2)(zod@4.3.6): dependencies: '@nestjs/common': 11.1.21(reflect-metadata@0.2.2)(rxjs@7.8.2) deepmerge: 4.3.1 rxjs: 7.8.2 zod: 4.3.6 optionalDependencies: - '@nestjs/swagger': 11.4.3(@nestjs/common@11.1.21(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.21)(reflect-metadata@0.2.2) + '@nestjs/swagger': 11.4.3(@nestjs/common@11.1.21(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.21)(reflect-metadata@0.2.2)(typescript@6.0.3) next-tick@1.1.0: {} @@ -23247,7 +23216,7 @@ snapshots: node-abi@3.92.0: dependencies: - semver: 7.8.0 + semver: 7.8.1 optional: true node-abort-controller@3.1.1: {} @@ -23288,7 +23257,7 @@ snapshots: graceful-fs: 4.2.11 nopt: 9.0.0 proc-log: 6.1.0 - semver: 7.8.0 + semver: 7.8.1 tar: 7.5.15 tinyglobby: 0.2.16 undici: 6.25.0 @@ -23526,7 +23495,7 @@ snapshots: got: 12.6.1 registry-auth-token: 5.1.1 registry-url: 6.0.1 - semver: 7.8.0 + semver: 7.8.1 package-manager-detector@1.6.0: {} @@ -23914,7 +23883,7 @@ snapshots: cosmiconfig: 8.3.6(typescript@6.0.3) jiti: 1.21.7 postcss: 8.5.15 - semver: 7.8.0 + semver: 7.8.1 webpack: 5.107.0(postcss@8.5.15) transitivePeerDependencies: - typescript @@ -24268,7 +24237,7 @@ snapshots: dependencies: prettier: 3.8.3 - prettier-plugin-svelte@3.5.2(prettier@3.8.3)(svelte@5.55.8(@typescript-eslint/types@8.59.4)): + prettier-plugin-svelte@4.1.0(prettier@3.8.3)(svelte@5.55.8(@typescript-eslint/types@8.59.4)): dependencies: prettier: 3.8.3 svelte: 5.55.8(@typescript-eslint/types@8.59.4) @@ -24478,19 +24447,19 @@ snapshots: react-loadable-ssr-addon-v5-slorber@1.0.3(@docusaurus/react-loadable@6.0.0(react@19.2.6))(webpack@5.107.0(postcss@8.5.15)): dependencies: - '@babel/runtime': 7.29.2 + '@babel/runtime': 7.29.7 react-loadable: '@docusaurus/react-loadable@6.0.0(react@19.2.6)' webpack: 5.107.0(postcss@8.5.15) react-router-config@5.1.1(react-router@5.3.4(react@19.2.6))(react@19.2.6): dependencies: - '@babel/runtime': 7.29.2 + '@babel/runtime': 7.29.7 react: 19.2.6 react-router: 5.3.4(react@19.2.6) react-router-dom@5.3.4(react@19.2.6): dependencies: - '@babel/runtime': 7.29.2 + '@babel/runtime': 7.29.7 history: 4.10.1 loose-envify: 1.4.0 prop-types: 15.8.1 @@ -24501,7 +24470,7 @@ snapshots: react-router@5.3.4(react@19.2.6): dependencies: - '@babel/runtime': 7.29.2 + '@babel/runtime': 7.29.7 history: 4.10.1 hoist-non-react-statics: 3.3.2 loose-envify: 1.4.0 @@ -24969,12 +24938,14 @@ snapshots: semver-diff@4.0.0: dependencies: - semver: 7.8.0 + semver: 7.8.1 semver@6.3.1: {} semver@7.8.0: {} + semver@7.8.1: {} + send@0.19.2: dependencies: debug: 2.6.9 @@ -25089,7 +25060,7 @@ snapshots: detect-libc: 2.1.2 node-addon-api: 8.7.0 node-gyp: 12.3.0 - semver: 7.8.0 + semver: 7.8.1 optionalDependencies: '@img/sharp-darwin-arm64': 0.34.5 '@img/sharp-darwin-x64': 0.34.5 @@ -25509,7 +25480,7 @@ snapshots: postcss: 8.5.15 postcss-scss: 4.0.9(postcss@8.5.15) postcss-selector-parser: 7.1.1 - semver: 7.8.0 + semver: 7.8.1 optionalDependencies: svelte: 5.55.8(@typescript-eslint/types@8.59.4) @@ -25767,15 +25738,15 @@ snapshots: - bare-abort-controller - react-native-b4a - terser-webpack-plugin@5.6.0(@swc/core@1.15.33(@swc/helpers@0.5.22))(esbuild@0.28.0)(lightningcss@1.32.0)(webpack@5.106.0(@swc/core@1.15.33(@swc/helpers@0.5.22))(esbuild@0.28.0)(lightningcss@1.32.0)): + terser-webpack-plugin@5.6.0(@swc/core@1.15.33(@swc/helpers@0.5.23))(esbuild@0.28.0)(lightningcss@1.32.0)(webpack@5.106.0(@swc/core@1.15.33(@swc/helpers@0.5.23))(esbuild@0.28.0)(lightningcss@1.32.0)): dependencies: '@jridgewell/trace-mapping': 0.3.31 jest-worker: 27.5.1 schema-utils: 4.3.3 terser: 5.47.1 - webpack: 5.106.0(@swc/core@1.15.33(@swc/helpers@0.5.22))(esbuild@0.28.0)(lightningcss@1.32.0) + webpack: 5.106.0(@swc/core@1.15.33(@swc/helpers@0.5.23))(esbuild@0.28.0)(lightningcss@1.32.0) optionalDependencies: - '@swc/core': 1.15.33(@swc/helpers@0.5.22) + '@swc/core': 1.15.33(@swc/helpers@0.5.23) esbuild: 0.28.0 lightningcss: 1.32.0 @@ -25809,13 +25780,7 @@ snapshots: commander: 2.20.3 source-map-support: 0.5.21 - test-exclude@7.0.2: - dependencies: - '@istanbuljs/schema': 0.1.6 - glob: 10.5.0 - minimatch: 10.2.5 - - testcontainers@11.14.0: + testcontainers@12.0.1: dependencies: '@balena/dockerignore': 1.0.2 '@types/dockerode': 4.0.1 @@ -25824,13 +25789,13 @@ snapshots: byline: 5.0.0 debug: 4.4.3 docker-compose: 1.4.2 - dockerode: 4.0.12 + dockerode: 5.0.0 get-port: 7.2.0 proper-lockfile: 4.1.2 properties-reader: 3.0.1 ssh-remote-port-forward: 1.0.4 tar-fs: 3.1.2 - tmp: 0.2.5 + tmp: 0.2.7 undici: 7.25.0 transitivePeerDependencies: - bare-abort-controller @@ -25911,7 +25876,7 @@ snapshots: tldts-core: 6.1.86 optional: true - tmp@0.2.5: {} + tmp@0.2.7: {} to-regex-range@5.0.1: dependencies: @@ -26182,10 +26147,10 @@ snapshots: unpipe@1.0.0: {} - unplugin-swc@1.5.9(@swc/core@1.15.33(@swc/helpers@0.5.22))(rollup@4.60.4): + unplugin-swc@1.5.9(@swc/core@1.15.33(@swc/helpers@0.5.23))(rollup@4.60.4): dependencies: '@rollup/pluginutils': 5.3.0(rollup@4.60.4) - '@swc/core': 1.15.33(@swc/helpers@0.5.22) + '@swc/core': 1.15.33(@swc/helpers@0.5.23) load-tsconfig: 0.2.5 unplugin: 2.3.11 transitivePeerDependencies: @@ -26217,7 +26182,7 @@ snapshots: is-yarn-global: 0.4.1 latest-version: 7.0.0 pupa: 3.3.0 - semver: 7.8.0 + semver: 7.8.1 semver-diff: 4.0.0 xdg-basedir: 5.1.0 @@ -26261,8 +26226,6 @@ snapshots: - encoding - supports-color - uuid@10.0.0: {} - uuid@14.0.0: {} uuid@8.3.2: {} @@ -26584,7 +26547,7 @@ snapshots: webpack-virtual-modules@0.6.2: {} - webpack@5.106.0(@swc/core@1.15.33(@swc/helpers@0.5.22))(esbuild@0.28.0)(lightningcss@1.32.0): + webpack@5.106.0(@swc/core@1.15.33(@swc/helpers@0.5.23))(esbuild@0.28.0)(lightningcss@1.32.0): dependencies: '@types/eslint-scope': 3.7.7 '@types/estree': 1.0.9 @@ -26608,7 +26571,7 @@ snapshots: neo-async: 2.6.2 schema-utils: 4.3.3 tapable: 2.3.3 - terser-webpack-plugin: 5.6.0(@swc/core@1.15.33(@swc/helpers@0.5.22))(esbuild@0.28.0)(lightningcss@1.32.0)(webpack@5.106.0(@swc/core@1.15.33(@swc/helpers@0.5.22))(esbuild@0.28.0)(lightningcss@1.32.0)) + terser-webpack-plugin: 5.6.0(@swc/core@1.15.33(@swc/helpers@0.5.23))(esbuild@0.28.0)(lightningcss@1.32.0)(webpack@5.106.0(@swc/core@1.15.33(@swc/helpers@0.5.23))(esbuild@0.28.0)(lightningcss@1.32.0)) watchpack: 2.5.1 webpack-sources: 3.4.1 transitivePeerDependencies: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index f53cb0d406..a848deca82 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -60,6 +60,9 @@ packageExtensions: dependencies: node-addon-api: '*' node-gyp: '*' + '@nestjs/swagger': + peerDependencies: + typescript: '*' dedupePeerDependents: false preferWorkspacePackages: true injectWorkspacePackages: true diff --git a/server/Dockerfile b/server/Dockerfile index 6078a97504..16335febda 100644 --- a/server/Dockerfile +++ b/server/Dockerfile @@ -1,4 +1,4 @@ -FROM ghcr.io/immich-app/base-server-dev:202605051129@sha256:d07d8fcdb7e9f3ac22a811e87761ebf341ed0bb91956b89097540c2ed3fb9ca3 AS builder +FROM ghcr.io/immich-app/base-server-dev:202606021219@sha256:63fa91aa011f6f2921dd32fe6d1be8d637e9bd7f3e3dd0c8e446afb31b282af4 AS builder ENV COREPACK_ENABLE_DOWNLOAD_PROMPT=0 \ CI=1 \ COREPACK_HOME=/tmp \ @@ -13,14 +13,15 @@ FROM builder AS server WORKDIR /usr/src/app COPY ./server ./server/ +COPY ./packages/sdk ./packages/sdk/ COPY ./packages/plugin-sdk ./packages/plugin-sdk/ 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 --filter @immich/plugin-sdk --frozen-lockfile build && \ - SHARP_FORCE_GLOBAL_LIBVIPS=true pnpm --filter immich --frozen-lockfile --prod --no-optional deploy /output/server-pruned + SHARP_IGNORE_GLOBAL_LIBVIPS=true pnpm --filter @immich/sdk --filter @immich/plugin-sdk --filter immich --frozen-lockfile build && \ + SHARP_FORCE_GLOBAL_LIBVIPS=true pnpm --filter immich --frozen-lockfile --prod --no-optional deploy /output/server-pruned FROM builder AS web @@ -55,7 +56,7 @@ FROM builder AS plugins ARG TARGETPLATFORM -COPY --from=ghcr.io/jdx/mise:2026.5.11@sha256:2ba959e4827f845fe0c4cfb4814089e790dc513040ef74f9e14925f446412a51 /usr/local/bin/mise /usr/local/bin/mise +COPY --from=ghcr.io/jdx/mise:2026.5.18@sha256:5bb3311994fa78cef307ca3077cdb18f9551da0886371fc26ea91ab56220ffc5 /usr/local/bin/mise /usr/local/bin/mise WORKDIR /app COPY ./mise.toml ./mise.toml @@ -66,6 +67,7 @@ ENV MISE_DISABLE_TOOLS=flutter RUN --mount=type=cache,id=mise-tools-${TARGETPLATFORM},target=/buildcache/mise \ mise install +COPY ./packages/sdk ./packages/sdk/ COPY ./packages/plugin-core ./packages/plugin-core/ COPY ./packages/plugin-sdk ./packages/plugin-sdk/ @@ -78,7 +80,7 @@ RUN --mount=type=cache,id=pnpm-packages,target=/buildcache/pnpm-store \ --mount=type=cache,id=mise-tools-${TARGETPLATFORM},target=/buildcache/mise \ mise //:plugins -FROM ghcr.io/immich-app/base-server-prod:202605051129@sha256:50f7ffe4ed31e360c90c4905bd5f6658f2a121297544e3fe9368e338b3f76bcd +FROM ghcr.io/immich-app/base-server-prod:202606021219@sha256:6ef9ef5859492149af770a6c884b5e2ddbaeef99f8885ea5f2d9f73625a3d9ec WORKDIR /usr/src/app ENV NODE_ENV=production \ diff --git a/server/Dockerfile.dev b/server/Dockerfile.dev index 7979e88dd3..06196ae143 100644 --- a/server/Dockerfile.dev +++ b/server/Dockerfile.dev @@ -1,8 +1,8 @@ # dev build -FROM ghcr.io/immich-app/base-server-dev:202605051129@sha256:d07d8fcdb7e9f3ac22a811e87761ebf341ed0bb91956b89097540c2ed3fb9ca3 AS dev +FROM ghcr.io/immich-app/base-server-dev:202606021219@sha256:63fa91aa011f6f2921dd32fe6d1be8d637e9bd7f3e3dd0c8e446afb31b282af4 AS dev -COPY --from=ghcr.io/jdx/mise:2026.5.11@sha256:2ba959e4827f845fe0c4cfb4814089e790dc513040ef74f9e14925f446412a51 /usr/local/bin/mise /usr/local/bin/mise +COPY --from=ghcr.io/jdx/mise:2026.5.18@sha256:5bb3311994fa78cef307ca3077cdb18f9551da0886371fc26ea91ab56220ffc5 /usr/local/bin/mise /usr/local/bin/mise RUN echo "devdir=/buildcache/node-gyp" >> /usr/local/etc/npmrc && \ echo "store-dir=/buildcache/pnpm-store" >> /usr/local/etc/npmrc && \ diff --git a/server/mise.toml b/server/mise.toml index f7e4f92a26..1462af0c52 100644 --- a/server/mise.toml +++ b/server/mise.toml @@ -68,6 +68,7 @@ run = [ [tasks.ci-medium] run = [ { task = ":install" }, + { task = "//:plugins" }, { task = "//packages/plugin-core:build" }, { task = ":test-medium --run" }, ] diff --git a/server/package.json b/server/package.json index 119a1ea603..2aca79a0dc 100644 --- a/server/package.json +++ b/server/package.json @@ -106,7 +106,7 @@ "reflect-metadata": "^0.2.0", "rxjs": "^7.8.1", "sanitize-filename": "^1.6.3", - "semver": "^7.6.2", + "semver": "^7.8.1", "sharp": "^0.34.5", "sirv": "^3.0.0", "socket.io": "^4.8.1", @@ -147,7 +147,7 @@ "@types/supertest": "^7.0.0", "@types/ua-parser-js": "^0.7.36", "@types/validator": "^13.15.2", - "@vitest/coverage-v8": "^3.0.0", + "@vitest/coverage-v8": "^4.0.0", "eslint": "^10.0.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.1.3", @@ -160,7 +160,7 @@ "sql-formatter": "^15.0.0", "supertest": "^7.1.0", "tailwindcss": "^3.4.0", - "testcontainers": "^11.0.0", + "testcontainers": "^12.0.0", "typescript": "^6.0.0", "typescript-eslint": "^8.28.0", "unplugin-swc": "^1.4.5", diff --git a/server/src/config.ts b/server/src/config.ts index 999e1e45bc..730663d046 100644 --- a/server/src/config.ts +++ b/server/src/config.ts @@ -1,4 +1,5 @@ import { CronExpression } from '@nestjs/schedule'; +import { ReleaseChannel } from 'src/dtos/system-config.dto'; import { AudioCodec, Colorspace, @@ -45,6 +46,9 @@ export type SystemConfig = { accel: TranscodeHardwareAcceleration; accelDecode: boolean; tonemap: ToneMapping; + realtime: { + enabled: boolean; + }; }; job: Record; logging: { @@ -135,6 +139,7 @@ export type SystemConfig = { }; newVersionCheck: { enabled: boolean; + channel: ReleaseChannel; }; nightlyTasks: { startTime: string; @@ -224,6 +229,9 @@ export const defaults = Object.freeze({ tonemap: ToneMapping.Hable, accel: TranscodeHardwareAcceleration.Disabled, accelDecode: true, + realtime: { + enabled: false, + }, }, job: { [QueueName.BackgroundTask]: { concurrency: 5 }, @@ -344,6 +352,7 @@ export const defaults = Object.freeze({ }, newVersionCheck: { enabled: true, + channel: ReleaseChannel.Stable, }, nightlyTasks: { startTime: '00:00', diff --git a/server/src/constants.ts b/server/src/constants.ts index 9f8cdbefdb..16b7731dce 100644 --- a/server/src/constants.ts +++ b/server/src/constants.ts @@ -1,7 +1,15 @@ import { readFileSync } from 'node:fs'; import { dirname, join } from 'node:path'; import { SemVer } from 'semver'; -import { ApiTag, AudioCodec, DatabaseExtension, ExifOrientation, VectorIndex } from 'src/enum'; +import { + ApiTag, + AudioCodec, + DatabaseExtension, + ExifOrientation, + TranscodeHardwareAcceleration, + VectorIndex, + VideoCodec, +} from 'src/enum'; export const IMMICH_SERVER_START = 'Immich Server is listening'; @@ -202,3 +210,32 @@ export const AUDIO_ENCODER: Record = { [AudioCodec.Opus]: 'libopus', [AudioCodec.PcmS16le]: 'pcm_s16le', }; + +export const SUPPORTED_HWA_CODECS: Record = { + [TranscodeHardwareAcceleration.Nvenc]: [VideoCodec.H264, VideoCodec.Hevc, VideoCodec.Av1], + [TranscodeHardwareAcceleration.Qsv]: [VideoCodec.H264, VideoCodec.Hevc, VideoCodec.Vp9, VideoCodec.Av1], + [TranscodeHardwareAcceleration.Vaapi]: [VideoCodec.H264, VideoCodec.Hevc, VideoCodec.Vp9, VideoCodec.Av1], + [TranscodeHardwareAcceleration.Rkmpp]: [VideoCodec.H264, VideoCodec.Hevc], + [TranscodeHardwareAcceleration.Disabled]: [VideoCodec.H264, VideoCodec.Hevc, VideoCodec.Vp9, VideoCodec.Av1], +}; + +export const HLS_BACKPRESSURE_PAUSE_SEGMENTS = 30; +export const HLS_BACKPRESSURE_RESUME_SEGMENTS = 15; +export const HLS_CLEANUP_INTERVAL_MS = 60 * 1000; +export const HLS_INACTIVITY_TIMEOUT_MS = 5 * 60 * 1000; +export const HLS_LEASE_DURATION_MS = 30 * 60 * 1000; +export const HLS_PLAYLIST_CONTENT_TYPE = 'application/vnd.apple.mpegurl'; +export const HLS_SEGMENT_DURATION = 2; +export const HLS_SEGMENT_FILENAME_REGEX = /^seg_(\d+)\.m4s$/; +export const HLS_VARIANTS = [ + { resolution: 480, codec: VideoCodec.Av1, bitrate: 1_000_000, codecString: 'av01.0.04M.08' }, + { resolution: 480, codec: VideoCodec.Hevc, bitrate: 1_200_000, codecString: 'hvc1.1.6.L90.B0' }, + { resolution: 480, codec: VideoCodec.H264, bitrate: 2_500_000, codecString: 'avc1.64001e' }, + { resolution: 720, codec: VideoCodec.Av1, bitrate: 2_000_000, codecString: 'av01.0.08M.08' }, + { resolution: 720, codec: VideoCodec.Hevc, bitrate: 2_500_000, codecString: 'hvc1.1.6.L93.B0' }, + { resolution: 720, codec: VideoCodec.H264, bitrate: 5_000_000, codecString: 'avc1.64001f' }, + { resolution: 1080, codec: VideoCodec.Av1, bitrate: 4_000_000, codecString: 'av01.0.09M.08' }, + { resolution: 1080, codec: VideoCodec.Hevc, bitrate: 4_500_000, codecString: 'hvc1.1.6.L120.B0' }, + { resolution: 1080, codec: VideoCodec.H264, bitrate: 8_000_000, codecString: 'avc1.640028' }, +]; +export const HLS_VERSION = 7; diff --git a/server/src/controllers/index.ts b/server/src/controllers/index.ts index dc3754ce24..336ea1cf91 100644 --- a/server/src/controllers/index.ts +++ b/server/src/controllers/index.ts @@ -35,6 +35,7 @@ import { TimelineController } from 'src/controllers/timeline.controller'; import { TrashController } from 'src/controllers/trash.controller'; import { UserAdminController } from 'src/controllers/user-admin.controller'; import { UserController } from 'src/controllers/user.controller'; +import { VideoStreamController } from 'src/controllers/video-stream.controller'; import { ViewController } from 'src/controllers/view.controller'; import { WorkflowController } from 'src/controllers/workflow.controller'; @@ -76,6 +77,7 @@ export const controllers = [ TrashController, UserAdminController, UserController, + VideoStreamController, ViewController, WorkflowController, ]; diff --git a/server/src/controllers/video-stream.controller.ts b/server/src/controllers/video-stream.controller.ts new file mode 100644 index 0000000000..8707584361 --- /dev/null +++ b/server/src/controllers/video-stream.controller.ts @@ -0,0 +1,79 @@ +import { Controller, Delete, Get, Header, HttpCode, HttpStatus, Next, Param, Res } from '@nestjs/common'; +import { ApiProduces, ApiTags } from '@nestjs/swagger'; +import { NextFunction, Response } from 'express'; +import { HLS_PLAYLIST_CONTENT_TYPE } from 'src/constants'; +import { Endpoint, HistoryBuilder } from 'src/decorators'; +import { AuthDto } from 'src/dtos/auth.dto'; +import { HlsSegmentParamDto, HlsSessionParamDto, HlsVariantParamDto } from 'src/dtos/streaming.dto'; +import { ApiTag, Permission, RouteKey } from 'src/enum'; +import { Auth, Authenticated, FileResponse } from 'src/middleware/auth.guard'; +import { LoggingRepository } from 'src/repositories/logging.repository'; +import { HlsService } from 'src/services/hls.service'; +import { sendFile } from 'src/utils/file'; +import { UUIDParamDto } from 'src/validation'; + +@ApiTags(ApiTag.Assets) +@Controller(RouteKey.Asset) +export class VideoStreamController { + constructor( + private logger: LoggingRepository, + private service: HlsService, + ) {} + + @Get(':id/video/stream/main.m3u8') + @Authenticated({ permission: Permission.AssetView, sharedLink: true }) + @Header('Cache-Control', 'no-cache') + @Header('Content-Type', HLS_PLAYLIST_CONTENT_TYPE) + @ApiProduces(HLS_PLAYLIST_CONTENT_TYPE) + @Endpoint({ + summary: 'Get HLS main playlist', + description: 'Returns an HLS main playlist with all available variants for the asset.', + history: new HistoryBuilder().added('v3').alpha('v3'), + }) + getMainPlaylist(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto) { + return this.service.getMainPlaylist(auth, id); + } + + @Get(':id/video/stream/:sessionId/:variantIndex/playlist.m3u8') + @Authenticated({ permission: Permission.AssetView, sharedLink: true }) + @Header('Cache-Control', 'no-cache') + @Header('Content-Type', HLS_PLAYLIST_CONTENT_TYPE) + @ApiProduces(HLS_PLAYLIST_CONTENT_TYPE) + @Endpoint({ + summary: 'Get HLS media playlist', + description: 'Returns an HLS media playlist for one variant of the streaming session.', + history: new HistoryBuilder().added('v3').alpha('v3'), + }) + getMediaPlaylist(@Auth() auth: AuthDto, @Param() { id, sessionId }: HlsVariantParamDto) { + return this.service.getMediaPlaylist(auth, id, sessionId); + } + + @Get(':id/video/stream/:sessionId/:variantIndex/:filename') + @FileResponse() + @Authenticated({ permission: Permission.AssetView, sharedLink: true }) + @Endpoint({ + summary: 'Get HLS segment or init file', + description: 'Streams an HLS init segment (init.mp4) or media segment (seg_N.m4s).', + history: new HistoryBuilder().added('v3').alpha('v3'), + }) + async getSegment( + @Auth() auth: AuthDto, + @Param() { id, sessionId, variantIndex, filename }: HlsSegmentParamDto, + @Res() res: Response, + @Next() next: NextFunction, + ) { + await sendFile(res, next, () => this.service.getSegment(auth, id, sessionId, variantIndex, filename), this.logger); + } + + @Delete(':id/video/stream/:sessionId') + @HttpCode(HttpStatus.NO_CONTENT) + @Authenticated({ permission: Permission.AssetView, sharedLink: true }) + @Endpoint({ + summary: 'End HLS streaming session', + description: 'Releases server resources for the streaming session.', + history: new HistoryBuilder().added('v3').alpha('v3'), + }) + async endSession(@Auth() auth: AuthDto, @Param() { id, sessionId }: HlsSessionParamDto) { + await this.service.endSession(auth, id, sessionId); + } +} diff --git a/server/src/controllers/workflow.controller.spec.ts b/server/src/controllers/workflow.controller.spec.ts index 7bc164e285..140fb00e95 100644 --- a/server/src/controllers/workflow.controller.spec.ts +++ b/server/src/controllers/workflow.controller.spec.ts @@ -1,5 +1,5 @@ +import { WorkflowTrigger } from '@immich/plugin-sdk'; import { WorkflowController } from 'src/controllers/workflow.controller'; -import { WorkflowTrigger } from 'src/enum'; import { LoggingRepository } from 'src/repositories/logging.repository'; import { WorkflowService } from 'src/services/workflow.service'; import request from 'supertest'; diff --git a/server/src/cores/storage.core.ts b/server/src/cores/storage.core.ts index d40518762d..8802145020 100644 --- a/server/src/cores/storage.core.ts +++ b/server/src/cores/storage.core.ts @@ -35,6 +35,10 @@ export interface MoveRequest { export type ThumbnailPathEntity = { id: string; ownerId: string }; +export type HlsSessionFolder = { ownerId: string; sessionId: string }; + +export type HlsVariantFolder = { ownerId: string; sessionId: string; variantIndex: number }; + export type ImagePathOptions = { fileType: AssetFileType; format: ImageFormat | RawExtractedFormat; isEdited: boolean }; let instance: StorageCore | null; @@ -125,6 +129,14 @@ export class StorageCore { return StorageCore.getNestedPath(StorageFolder.EncodedVideo, asset.ownerId, `${asset.id}.mp4`); } + static getHlsSessionFolder({ ownerId, sessionId }: HlsSessionFolder) { + return StorageCore.getNestedPath(StorageFolder.EncodedVideo, ownerId, sessionId); + } + + static getHlsVariantFolder({ ownerId, sessionId, variantIndex }: HlsVariantFolder) { + return join(StorageCore.getHlsSessionFolder({ ownerId, sessionId }), variantIndex.toString()); + } + static getAndroidMotionPath(asset: ThumbnailPathEntity, uuid: string) { return StorageCore.getNestedPath(StorageFolder.EncodedVideo, asset.ownerId, `${uuid}-MP.mp4`); } diff --git a/server/src/decorators.ts b/server/src/decorators.ts index c8cf1f9221..513ae36c9f 100644 --- a/server/src/decorators.ts +++ b/server/src/decorators.ts @@ -265,3 +265,13 @@ export class HistoryBuilder { return this; } } + +// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type +export const extraModels: Function[] = []; + +export const ExtraModel = (): ClassDecorator => { + // eslint-disable-next-line unicorn/consistent-function-scoping, @typescript-eslint/no-unsafe-function-type + return (object: Function) => { + extraModels.push(object); + }; +}; diff --git a/server/src/dtos/album.dto.ts b/server/src/dtos/album.dto.ts index 095e399b96..100550659d 100644 --- a/server/src/dtos/album.dto.ts +++ b/server/src/dtos/album.dto.ts @@ -65,6 +65,8 @@ const UpdateAlbumSchema = z const GetAlbumsSchema = z .object({ + id: z.uuidv4().optional().describe('Album ID'), + name: z.string().optional().describe('Album name (exact match)'), isOwned: stringToBool .optional() .describe('Filter by ownership: true = only owned, false = only shared-with-me, undefined = no filter'), diff --git a/server/src/dtos/plugin-manifest.dto.ts b/server/src/dtos/plugin-manifest.dto.ts index c8f043fde1..b175c6e1bb 100644 --- a/server/src/dtos/plugin-manifest.dto.ts +++ b/server/src/dtos/plugin-manifest.dto.ts @@ -38,6 +38,7 @@ const PluginManifestTemplateSchema = z description: z.string().min(1).describe('Template description'), trigger: WorkflowTriggerSchema.describe('Workflow trigger'), steps: z.array(PluginManifestTemplateStepSchema).describe('Workflow steps'), + uiHints: z.array(z.string()).optional().default([]).describe('Ui hints, for example "smart-album"'), }) .meta({ id: 'PluginManifestTemplateDto' }); diff --git a/server/src/dtos/plugin.dto.ts b/server/src/dtos/plugin.dto.ts index 074321bb44..8ee695d61c 100644 --- a/server/src/dtos/plugin.dto.ts +++ b/server/src/dtos/plugin.dto.ts @@ -1,6 +1,7 @@ +import { WorkflowTrigger } from '@immich/plugin-sdk'; import { createZodDto } from 'nestjs-zod'; import { JsonSchemaDto } from 'src/dtos/json-schema.dto'; -import { WorkflowTrigger, WorkflowTriggerSchema, WorkflowType, WorkflowTypeSchema } from 'src/enum'; +import { WorkflowTriggerSchema, WorkflowType, WorkflowTypeSchema } from 'src/enum'; import { asPluginKey } from 'src/utils/workflow'; import z from 'zod'; @@ -58,6 +59,7 @@ const PluginTemplateResponseSchema = z description: z.string().describe('Template description'), trigger: WorkflowTriggerSchema.describe('Workflow trigger'), steps: z.array(PluginTemplateStepResponseSchema).describe('Workflow steps'), + uiHints: z.array(z.string()).describe('Ui hints, for example "smart-album"'), }) .meta({ id: 'PluginTemplateResponseDto' }); @@ -91,6 +93,7 @@ export type PluginTemplate = { config?: Record | null; enabled?: boolean; }>; + uiHints: string[]; }; export const mapTemplate = (plugin: { name: string }, template: PluginTemplate): PluginTemplateResponseDto => { @@ -104,6 +107,7 @@ export const mapTemplate = (plugin: { name: string }, template: PluginTemplate): config: step.config ?? null, enabled: step.enabled, })), + uiHints: template.uiHints ?? [], }; }; diff --git a/server/src/dtos/server.dto.ts b/server/src/dtos/server.dto.ts index 57a58e1dd7..03d45fab1c 100644 --- a/server/src/dtos/server.dto.ts +++ b/server/src/dtos/server.dto.ts @@ -1,5 +1,6 @@ import { createZodDto } from 'nestjs-zod'; import type { SemVer } from 'semver'; +import { ExtraModel, HistoryBuilder } from 'src/decorators'; import { isoDatetimeToDate } from 'src/validation'; import z from 'zod'; @@ -58,9 +59,15 @@ const ServerStorageResponseSchema = z const ServerVersionResponseSchema = z .object({ - major: z.int().describe('Major version number'), - minor: z.int().describe('Minor version number'), - patch: z.int().describe('Patch version number'), + major: z.int().min(0).describe('Major version number'), + minor: z.int().min(0).describe('Minor version number'), + patch: z.int().min(0).describe('Patch version number'), + prerelease: z + .int() + .min(0) + .nullable() + .meta(HistoryBuilder.v3().getExtensions()) + .describe('Pre-release version number'), }) .meta({ id: 'ServerVersionResponseDto' }); @@ -117,6 +124,7 @@ const ServerConfigSchema = z mapDarkStyleUrl: z.string().describe('Map dark style URL'), mapLightStyleUrl: z.string().describe('Map light style URL'), maintenanceMode: z.boolean().describe('Whether maintenance mode is active'), + minFaces: z.int().describe('People min faces server default'), }) .meta({ id: 'ServerConfigDto' }); @@ -137,9 +145,30 @@ const ServerFeaturesSchema = z search: z.boolean().describe('Whether search is enabled'), email: z.boolean().describe('Whether email notifications are enabled'), ocr: z.boolean().describe('Whether OCR is enabled'), + realtimeTranscoding: z.boolean().describe('Whether real-time transcoding is enabled'), }) .meta({ id: 'ServerFeaturesDto' }); +export enum ReleaseType { + Major = 'major', + Premajor = 'premajor', + Minor = 'minor', + Preminor = 'preminor', + Patch = 'patch', + Prepatch = 'prepatch', + Prerelease = 'prerelease', +} + +const ReleaseTypeSchema = z.enum(ReleaseType).meta({ id: 'ReleaseType' }).describe('Release type'); + +const ReleaseEventV1Schema = z.object({ + isAvailable: z.boolean().describe('Whether a new version is available'), + checkedAt: z.string().describe('When the server last checked for a latest version. As an ISO timestamp'), + serverVersion: ServerVersionResponseSchema, + releaseVersion: ServerVersionResponseSchema, + type: ReleaseTypeSchema.nullable(), +}); + export class ServerPingResponse extends createZodDto(ServerPingResponseSchema) {} export class ServerAboutResponseDto extends createZodDto(ServerAboutResponseSchema) {} export class ServerApkLinksDto extends createZodDto(ServerApkLinksSchema) {} @@ -147,7 +176,12 @@ export class ServerStorageResponseDto extends createZodDto(ServerStorageResponse export class ServerVersionResponseDto extends createZodDto(ServerVersionResponseSchema) { static fromSemVer(value: SemVer): z.infer { - return { major: value.major, minor: value.minor, patch: value.patch }; + return { + major: value.major, + minor: value.minor, + patch: value.patch, + prerelease: (value.prerelease[1] as number) ?? null, + }; } } @@ -158,10 +192,5 @@ export class ServerMediaTypesResponseDto extends createZodDto(ServerMediaTypesRe export class ServerConfigDto extends createZodDto(ServerConfigSchema) {} export class ServerFeaturesDto extends createZodDto(ServerFeaturesSchema) {} -export interface ReleaseNotification { - isAvailable: boolean; - /** ISO8601 */ - checkedAt: string; - serverVersion: ServerVersionResponseDto; - releaseVersion: ServerVersionResponseDto; -} +@ExtraModel() +export class ReleaseEventV1 extends createZodDto(ReleaseEventV1Schema) {} diff --git a/server/src/dtos/streaming.dto.ts b/server/src/dtos/streaming.dto.ts new file mode 100644 index 0000000000..5270e45fc2 --- /dev/null +++ b/server/src/dtos/streaming.dto.ts @@ -0,0 +1,26 @@ +import { createZodDto } from 'nestjs-zod'; +import z from 'zod'; + +const HlsSessionParamSchema = z.object({ + id: z.uuidv4(), + sessionId: z.uuidv4(), +}); + +export class HlsSessionParamDto extends createZodDto(HlsSessionParamSchema) {} + +const HlsVariantParamSchema = z.object({ + id: z.uuidv4(), + sessionId: z.uuidv4(), + variantIndex: z.coerce.number().int().min(0), +}); + +export class HlsVariantParamDto extends createZodDto(HlsVariantParamSchema) {} + +const HlsSegmentParamSchema = z.object({ + id: z.uuidv4(), + sessionId: z.uuidv4(), + variantIndex: z.coerce.number().int().min(0), + filename: z.string().regex(/^(init\.mp4|seg_\d+\.m4s)$/, { error: 'Invalid HLS segment filename' }), +}); + +export class HlsSegmentParamDto extends createZodDto(HlsSegmentParamSchema) {} diff --git a/server/src/dtos/sync.dto.ts b/server/src/dtos/sync.dto.ts index 35ef874dfa..698939e627 100644 --- a/server/src/dtos/sync.dto.ts +++ b/server/src/dtos/sync.dto.ts @@ -1,5 +1,5 @@ -/* eslint-disable @typescript-eslint/no-unsafe-function-type */ import { createZodDto } from 'nestjs-zod'; +import { ExtraModel } from 'src/decorators'; import { AssetEditActionSchema } from 'src/dtos/editing.dto'; import { AlbumUserRole, @@ -17,15 +17,6 @@ import { import { isoDatetimeToDate } from 'src/validation'; import z from 'zod'; -export const extraSyncModels: Function[] = []; - -const ExtraModel = (): ClassDecorator => { - // eslint-disable-next-line unicorn/consistent-function-scoping - return (object: Function) => { - extraSyncModels.push(object); - }; -}; - const SyncUserV1Schema = z .object({ id: z.string().describe('User ID'), diff --git a/server/src/dtos/system-config.dto.ts b/server/src/dtos/system-config.dto.ts index 94c1aa36b0..3b31705918 100644 --- a/server/src/dtos/system-config.dto.ts +++ b/server/src/dtos/system-config.dto.ts @@ -79,6 +79,11 @@ const SystemConfigFFmpegSchema = z accel: TranscodeHardwareAccelerationSchema, accelDecode: configBool.describe('Accelerated decode'), tonemap: ToneMappingSchema, + realtime: z + .object({ + enabled: configBool.describe('Enable real-time HLS transcoding (alpha)'), + }) + .meta({ id: 'SystemConfigFFmpegRealtimeDto' }), }) .meta({ id: 'SystemConfigFFmpegDto' }); @@ -151,8 +156,15 @@ const SystemConfigMapSchema = z }) .meta({ id: 'SystemConfigMapDto' }); +export enum ReleaseChannel { + Stable = 'stable', + ReleaseCandidate = 'releaseCandidate', +} + +const ReleaseChannelSchema = z.enum(ReleaseChannel).describe('Release channel').meta({ id: 'ReleaseChannel' }); + const SystemConfigNewVersionCheckSchema = z - .object({ enabled: configBool.describe('Enabled') }) + .object({ enabled: configBool.describe('Enabled'), channel: ReleaseChannelSchema }) .meta({ id: 'SystemConfigNewVersionCheckDto' }); const SystemConfigNightlyTasksSchema = z diff --git a/server/src/dtos/user-preferences.dto.ts b/server/src/dtos/user-preferences.dto.ts index 7a7c1d2558..b8894e4d51 100644 --- a/server/src/dtos/user-preferences.dto.ts +++ b/server/src/dtos/user-preferences.dto.ts @@ -45,6 +45,7 @@ const PeopleUpdateSchema = z .object({ enabled: z.boolean().optional().describe('Whether people are enabled'), sidebarWeb: z.boolean().optional().describe('Whether people appear in web sidebar'), + minimumFaces: z.int().min(1).optional().describe('People face threshold'), }) .optional() .meta({ id: 'PeopleUpdate' }); @@ -138,6 +139,7 @@ const PeopleResponseSchema = z .object({ enabled: z.boolean().describe('Whether people are enabled'), sidebarWeb: z.boolean().describe('Whether people appear in web sidebar'), + minimumFaces: z.int().min(1).optional().describe('People face threshold'), }) .meta({ id: 'PeopleResponse' }); diff --git a/server/src/dtos/workflow.dto.ts b/server/src/dtos/workflow.dto.ts index 8a5960470d..1a2c2ac9f9 100644 --- a/server/src/dtos/workflow.dto.ts +++ b/server/src/dtos/workflow.dto.ts @@ -1,6 +1,6 @@ -import type { WorkflowStepConfig } from '@immich/plugin-sdk'; +import type { WorkflowStepConfig, WorkflowTrigger } from '@immich/plugin-sdk'; import { createZodDto } from 'nestjs-zod'; -import { WorkflowTrigger, WorkflowTriggerSchema, WorkflowTypeSchema } from 'src/enum'; +import { WorkflowTriggerSchema, WorkflowTypeSchema } from 'src/enum'; import z from 'zod'; const WorkflowTriggerResponseSchema = z diff --git a/server/src/enum.ts b/server/src/enum.ts index bc52e65f83..9dee1db313 100644 --- a/server/src/enum.ts +++ b/server/src/enum.ts @@ -1,3 +1,4 @@ +import { WorkflowTrigger } from '@immich/plugin-sdk'; import z from 'zod'; export enum AuthType { @@ -452,11 +453,7 @@ export enum VideoCodec { export const VideoCodecSchema = z.enum(VideoCodec).describe('Target video codec').meta({ id: 'VideoCodec' }); -export enum VideoSegmentCodec { - Av1 = 'av1', - Hevc = 'hevc', - H264 = 'h264', -} +export type VideoSegmentCodec = VideoCodec.Av1 | VideoCodec.Hevc | VideoCodec.H264; export enum AudioCodec { Mp3 = 'mp3', @@ -826,6 +823,8 @@ export enum JobName { LibrarySyncFiles = 'LibrarySyncFiles', LibraryScanQueueAll = 'LibraryScanQueueAll', + HlsSessionCleanup = 'HlsSessionCleanup', + MemoryCleanup = 'MemoryCleanup', MemoryGenerate = 'MemoryGenerate', @@ -866,7 +865,7 @@ export enum JobName { Ocr = 'Ocr', // Workflow - WorkflowAssetCreate = 'WorkflowAssetCreate', + WorkflowAssetTrigger = 'WorkflowAssetTrigger', } export const JobNameSchema = z.enum(JobName).describe('Job name').meta({ id: 'JobName' }); @@ -919,6 +918,7 @@ export enum DatabaseLock { MaintenanceOperation = 621, MemoryCreation = 777, VersionCheck = 800, + HlsSessionCleanup = 850, } export enum MaintenanceAction { @@ -1164,11 +1164,6 @@ export enum PluginContext { export const PluginContextSchema = z.enum(PluginContext).describe('Plugin context').meta({ id: 'PluginContextType' }); -export enum WorkflowTrigger { - AssetCreate = 'AssetCreate', - PersonRecognized = 'PersonRecognized', -} - export const WorkflowTriggerSchema = z .enum(WorkflowTrigger) .describe('Plugin trigger type') diff --git a/server/src/queries/person.repository.sql b/server/src/queries/person.repository.sql index 318c151cca..a2f3f64442 100644 --- a/server/src/queries/person.repository.sql +++ b/server/src/queries/person.repository.sql @@ -42,7 +42,18 @@ group by having ( "person"."name" != $3 - or count("asset_face"."assetId") >= $4 + or count("asset_face"."assetId") >= COALESCE( + ( + SELECT + value -> 'people' ->> 'minimumFaces' + FROM + user_metadata + WHERE + "userId" = $4 + AND key = 'preferences' + ), + '3' + )::int ) order by "person"."isHidden" asc, diff --git a/server/src/queries/video.stream.repository.sql b/server/src/queries/video.stream.repository.sql index c77882d77d..714e138ce8 100644 --- a/server/src/queries/video.stream.repository.sql +++ b/server/src/queries/video.stream.repository.sql @@ -7,6 +7,7 @@ from "video_stream_session" where "id" = $1 + and "expiresAt" > $2 -- VideoStreamRepository.getVariant select @@ -27,11 +28,13 @@ where -- VideoStreamRepository.getExpiredSessions select - "id" + "video_stream_session"."id", + "asset"."ownerId" from "video_stream_session" + inner join "asset" on "asset"."id" = "video_stream_session"."assetId" where - "expiresAt" <= $1 + "video_stream_session"."expiresAt" <= $1 -- VideoStreamRepository.extendSession update "video_stream_session" @@ -44,3 +47,253 @@ where delete from "video_stream_session" where "id" = $1 + +-- VideoStreamRepository.getForMainPlaylist +select + ( + select + to_json(obj) + from + ( + select + "asset_video"."index", + "asset_video"."codecName", + "asset_video"."profile", + "asset_video"."level", + "asset_video"."bitrate", + "asset_exif"."exifImageWidth" as "width", + "asset_exif"."exifImageHeight" as "height", + "asset_video"."pixelFormat", + "asset_video"."frameCount", + "asset_exif"."fps" as "frameRate", + "asset_video"."timeBase", + case + when "asset_exif"."orientation" = '6' then -90 + when "asset_exif"."orientation" = '8' then 90 + when "asset_exif"."orientation" = '3' then 180 + else 0 + end as "rotation", + "asset_video"."colorPrimaries", + "asset_video"."colorMatrix", + "asset_video"."colorTransfer", + "asset_video"."dvProfile", + "asset_video"."dvLevel", + "asset_video"."dvBlSignalCompatibilityId" + from + ( + select + 1 + ) as "dummy" + where + "asset_video"."assetId" is not null + ) as obj + ) as "videoStream", + ( + select + to_json(obj) + from + ( + select + "asset_keyframe"."pts" as "keyframePts", + "asset_keyframe"."accDuration" as "keyframeAccDuration", + "asset_keyframe"."ownDuration" as "keyframeOwnDuration", + "asset_keyframe"."totalDuration", + "asset_keyframe"."packetCount", + "asset_keyframe"."outputFrames" + from + ( + select + 1 + ) as "dummy" + where + "asset_keyframe"."assetId" is not null + ) as obj + ) as "packets" +from + "asset" + inner join "asset_exif" on "asset"."id" = "asset_exif"."assetId" + inner join "asset_video" on "asset"."id" = "asset_video"."assetId" + inner join "asset_keyframe" on "asset"."id" = "asset_keyframe"."assetId" +where + "asset"."id" = $1 + +-- VideoStreamRepository.getForMediaPlaylist +select + ( + select + to_json(obj) + from + ( + select + "asset_video"."index", + "asset_video"."codecName", + "asset_video"."profile", + "asset_video"."level", + "asset_video"."bitrate", + "asset_exif"."exifImageWidth" as "width", + "asset_exif"."exifImageHeight" as "height", + "asset_video"."pixelFormat", + "asset_video"."frameCount", + "asset_exif"."fps" as "frameRate", + "asset_video"."timeBase", + case + when "asset_exif"."orientation" = '6' then -90 + when "asset_exif"."orientation" = '8' then 90 + when "asset_exif"."orientation" = '3' then 180 + else 0 + end as "rotation", + "asset_video"."colorPrimaries", + "asset_video"."colorMatrix", + "asset_video"."colorTransfer", + "asset_video"."dvProfile", + "asset_video"."dvLevel", + "asset_video"."dvBlSignalCompatibilityId" + from + ( + select + 1 + ) as "dummy" + where + "asset_video"."assetId" is not null + ) as obj + ) as "videoStream", + ( + select + to_json(obj) + from + ( + select + "asset_keyframe"."pts" as "keyframePts", + "asset_keyframe"."accDuration" as "keyframeAccDuration", + "asset_keyframe"."ownDuration" as "keyframeOwnDuration", + "asset_keyframe"."totalDuration", + "asset_keyframe"."packetCount", + "asset_keyframe"."outputFrames" + from + ( + select + 1 + ) as "dummy" + where + "asset_keyframe"."assetId" is not null + ) as obj + ) as "packets" +from + "asset" + inner join "asset_exif" on "asset"."id" = "asset_exif"."assetId" + inner join "video_stream_session" on "asset"."id" = "video_stream_session"."assetId" + inner join "asset_video" on "asset"."id" = "asset_video"."assetId" + inner join "asset_keyframe" on "asset"."id" = "asset_keyframe"."assetId" +where + "asset"."id" = $1 + and "video_stream_session"."id" = $2 + and "video_stream_session"."expiresAt" > $3 + +-- VideoStreamRepository.getForTranscoding +select + "asset"."originalPath", + ( + select + to_json(obj) + from + ( + select + "asset_audio"."index", + "asset_audio"."codecName", + "asset_audio"."profile", + "asset_audio"."bitrate" + from + ( + select + 1 + ) as "dummy" + where + "asset_audio"."assetId" is not null + ) as obj + ) as "audioStream", + ( + select + to_json(obj) + from + ( + select + "asset_video"."index", + "asset_video"."codecName", + "asset_video"."profile", + "asset_video"."level", + "asset_video"."bitrate", + "asset_exif"."exifImageWidth" as "width", + "asset_exif"."exifImageHeight" as "height", + "asset_video"."pixelFormat", + "asset_video"."frameCount", + "asset_exif"."fps" as "frameRate", + "asset_video"."timeBase", + case + when "asset_exif"."orientation" = '6' then -90 + when "asset_exif"."orientation" = '8' then 90 + when "asset_exif"."orientation" = '3' then 180 + else 0 + end as "rotation", + "asset_video"."colorPrimaries", + "asset_video"."colorMatrix", + "asset_video"."colorTransfer", + "asset_video"."dvProfile", + "asset_video"."dvLevel", + "asset_video"."dvBlSignalCompatibilityId" + from + ( + select + 1 + ) as "dummy" + where + "asset_video"."assetId" is not null + ) as obj + ) as "videoStream", + ( + select + to_json(obj) + from + ( + select + "asset_video"."formatName", + "asset_video"."formatLongName", + "asset"."duration", + "asset_video"."bitrate" + from + ( + select + 1 + ) as "dummy" + where + "asset_video"."assetId" is not null + ) as obj + ) as "format", + ( + select + to_json(obj) + from + ( + select + "asset_keyframe"."pts" as "keyframePts", + "asset_keyframe"."accDuration" as "keyframeAccDuration", + "asset_keyframe"."ownDuration" as "keyframeOwnDuration", + "asset_keyframe"."totalDuration", + "asset_keyframe"."packetCount", + "asset_keyframe"."outputFrames" + from + ( + select + 1 + ) as "dummy" + where + "asset_keyframe"."assetId" is not null + ) as obj + ) as "packets" +from + "asset" + inner join "asset_exif" on "asset"."id" = "asset_exif"."assetId" + left join "asset_audio" on "asset"."id" = "asset_audio"."assetId" + inner join "asset_video" on "asset"."id" = "asset_video"."assetId" + inner join "asset_keyframe" on "asset"."id" = "asset_keyframe"."assetId" +where + "asset"."id" = $1 diff --git a/server/src/repositories/album.repository.ts b/server/src/repositories/album.repository.ts index a712151355..724788fa74 100644 --- a/server/src/repositories/album.repository.ts +++ b/server/src/repositories/album.repository.ts @@ -209,11 +209,16 @@ export class AlbumRepository { } @GenerateSql({ params: [DummyValue.UUID, { isOwned: true, isShared: true }] }) - getAll(ownerId: string, options: { isOwned?: boolean; isShared?: boolean } = {}): Promise { + getAll( + ownerId: string, + options: { id?: string; isOwned?: boolean; isShared?: boolean; name?: string } = {}, + ): Promise { return this.buildAlbumBaseQuery(ownerId, options) .selectAll('album') .select(withAlbumUsers(ownerId)) .select(withSharedLink) + .$if(!!options.id, (qb) => qb.where('album.id', '=', options.id!)) + .$if(!!options.name, (qb) => qb.where('album.albumName', '=', options.name!)) .orderBy('album.createdAt', 'desc') .execute(); } diff --git a/server/src/repositories/event.repository.ts b/server/src/repositories/event.repository.ts index fa92a6b0b7..b4d968599b 100644 --- a/server/src/repositories/event.repository.ts +++ b/server/src/repositories/event.repository.ts @@ -92,6 +92,14 @@ type EventMap = { AuthChangePassword: [{ userId: string; currentSessionId?: string; invalidateSessions?: boolean }]; + // hls streaming events + HlsSegmentRequest: [{ sessionId: string; assetId: string; variantIndex: number; segmentIndex: number }]; + HlsSegmentResult: [{ sessionId: string; variantIndex: number; segmentIndex: number; error?: string }]; + HlsHeartbeat: [{ sessionId: string; variantIndex?: number; segmentIndex?: number }]; + HlsSessionRequest: [{ sessionId: string; assetId: string; ownerId: string }]; + HlsSessionResult: [{ sessionId: string; error?: string }]; + HlsSessionEnd: [{ sessionId: string }]; + // websocket events WebsocketConnect: [{ userId: string }]; }; diff --git a/server/src/repositories/media.repository.ts b/server/src/repositories/media.repository.ts index fa08ba8701..c2ec95636a 100644 --- a/server/src/repositories/media.repository.ts +++ b/server/src/repositories/media.repository.ts @@ -490,18 +490,43 @@ export class MediaRepository { return this.parseInt(b.bit_rate) - this.parseInt(a.bit_rate); } + /* Ported from https://code.ffmpeg.org/FFmpeg/FFmpeg/src/commit/5c44245878e235ae64fe87fb9877644856d33d1d/fftools/ffmpeg_filter.c + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright (c) FFmpeg authors and contributors — https://ffmpeg.org/ + * Modifications: TS port operating on probe-derived packet metadata rather than decoded AVFrames. */ private cfrOutputFrames(packets: { pts: number; duration: number }[], slotsPerTick: number) { - // Packets may be out of PTS order due to B-frames packets.sort((a, b) => a.pts - b.pts); const firstPts = packets[0].pts; let outputFrames = 0; let nextPts = 0; + const history = [0, 0, 0]; for (const pkt of packets) { - const delta = (pkt.pts - firstPts) * slotsPerTick - nextPts + pkt.duration * slotsPerTick; - const nb = delta < -1.1 ? 0 : delta > 1.1 ? Math.round(delta) : 1; + const syncIpts = (pkt.pts - firstPts) * slotsPerTick; + const duration = pkt.duration * slotsPerTick; + let delta0 = syncIpts - nextPts; + const delta = delta0 + duration; + + if (delta0 < 0 && delta > 0) { + delta0 = 0; + } + + let nb = 1; + let nbPrev = 0; + if (delta < -1.1) { + nb = 0; + } else if (delta > 1.1) { + nb = Math.round(delta); + if (delta0 > 1.1) { + nbPrev = Math.round(delta0 - 0.6); + } + } outputFrames += nb; nextPts += nb; + history[2] = history[1]; + history[1] = history[0]; + history[0] = nbPrev; } - return outputFrames; + const median = history.sort((a, b) => a - b)[1]; + return outputFrames + median; } } diff --git a/server/src/repositories/person.repository.ts b/server/src/repositories/person.repository.ts index 2a9f822e94..0db03a18c7 100644 --- a/server/src/repositories/person.repository.ts +++ b/server/src/repositories/person.repository.ts @@ -4,7 +4,7 @@ import { jsonObjectFrom } from 'kysely/helpers/postgres'; import { InjectKysely } from 'nestjs-kysely'; import { AssetFace } from 'src/database'; import { Chunked, ChunkedArray, DummyValue, GenerateSql } from 'src/decorators'; -import { AssetFileType, AssetVisibility, SourceType } from 'src/enum'; +import { AssetFileType, AssetVisibility, SourceType, UserMetadataKey } from 'src/enum'; import { DB } from 'src/schema'; import { AssetFaceTable } from 'src/schema/tables/asset-face.table'; import { FaceSearchTable } from 'src/schema/tables/face-search.table'; @@ -13,7 +13,6 @@ import { dummy, removeUndefinedKeys, withFilePath } from 'src/utils/database'; import { paginationHelper, PaginationOptions } from 'src/utils/pagination'; export interface PersonSearchOptions { - minimumFaceCount: number; withHidden: boolean; closestFaceAssetId?: string; } @@ -168,7 +167,17 @@ export class PersonRepository { .having((eb) => eb.or([ eb('person.name', '!=', ''), - eb((innerEb) => innerEb.fn.count('asset_face.assetId'), '>=', options?.minimumFaceCount || 1), + eb( + (innerEb) => innerEb.fn.count('asset_face.assetId'), + '>=', + sql`COALESCE( + (SELECT value -> 'people' ->> 'minimumFaces' + FROM user_metadata + WHERE "userId" = ${userId} + AND key = ${sql.lit(UserMetadataKey.Preferences)}), + '3' + )::int `, + ), ]), ) .groupBy('person.id') diff --git a/server/src/repositories/process.repository.ts b/server/src/repositories/process.repository.ts index 9d8cac1f40..928531408f 100644 --- a/server/src/repositories/process.repository.ts +++ b/server/src/repositories/process.repository.ts @@ -1,12 +1,10 @@ import { Injectable } from '@nestjs/common'; -import { ChildProcessWithoutNullStreams, fork, spawn, SpawnOptionsWithoutStdio } from 'node:child_process'; +import { fork, spawn, SpawnOptionsWithoutStdio } from 'node:child_process'; import { Duplex } from 'node:stream'; @Injectable() export class ProcessRepository { - spawn(command: string, args?: readonly string[], options?: SpawnOptionsWithoutStdio): ChildProcessWithoutNullStreams { - return spawn(command, args, options); - } + spawn = spawn; spawnDuplexStream(command: string, args?: readonly string[], options?: SpawnOptionsWithoutStdio): Duplex { let stdinClosed = false; diff --git a/server/src/repositories/server-info.repository.ts b/server/src/repositories/server-info.repository.ts index 85d26d6cfa..c8fb896281 100644 --- a/server/src/repositories/server-info.repository.ts +++ b/server/src/repositories/server-info.repository.ts @@ -4,6 +4,7 @@ import { exec as execCallback } from 'node:child_process'; import { readFile } from 'node:fs/promises'; import { promisify } from 'node:util'; import sharp from 'sharp'; +import { ReleaseChannel } from 'src/dtos/system-config.dto'; import { ConfigRepository } from 'src/repositories/config.repository'; import { LoggingRepository } from 'src/repositories/logging.repository'; @@ -64,10 +65,12 @@ export class ServerInfoRepository { this.logger.setContext(ServerInfoRepository.name); } - async getLatestRelease(): Promise { + async getLatestRelease(channel: ReleaseChannel): Promise { try { const { versionCheck } = this.configRepository.getEnv(); - const response = await fetch(versionCheck.url); + const url = new URL(versionCheck.url); + url.searchParams.append('channel', channel); + const response = await fetch(url); if (!response.ok) { throw new Error(`Version check request failed with status ${response.status}: ${await response.text()}`); diff --git a/server/src/repositories/storage.repository.ts b/server/src/repositories/storage.repository.ts index 1d3971fd28..9604372fbe 100644 --- a/server/src/repositories/storage.repository.ts +++ b/server/src/repositories/storage.repository.ts @@ -10,6 +10,7 @@ import { existsSync, mkdirSync, ReadOptionsWithBuffer, + watch, } from 'node:fs'; import fs from 'node:fs/promises'; import path from 'node:path'; @@ -277,6 +278,8 @@ export class StorageRepository { return () => watcher.close(); } + watchDir = watch; // Native fs.watch without chokidar overhead + private asGlob(pathToCrawl: string): string { const escapedPath = escapePath(pathToCrawl).replaceAll('"', '["]').replaceAll("'", "[']").replaceAll('`', '[`]'); const extensions = `*{${mimeTypes.getSupportedFileExtensions().join(',')}}`; diff --git a/server/src/repositories/video-stream.repository.ts b/server/src/repositories/video-stream.repository.ts index e23ee4ca4c..43c5ef80f0 100644 --- a/server/src/repositories/video-stream.repository.ts +++ b/server/src/repositories/video-stream.repository.ts @@ -8,6 +8,7 @@ import { VideoStreamSessionTable, VideoStreamVariantTable, } from 'src/schema/tables/video-stream.table'; +import { withAudioStream, withVideoFormat, withVideoPackets, withVideoStream } from 'src/utils/database'; @Injectable() export class VideoStreamRepository { @@ -27,7 +28,12 @@ export class VideoStreamRepository { @GenerateSql({ params: [DummyValue.UUID] }) getSession(id: string) { - return this.db.selectFrom('video_stream_session').selectAll().where('id', '=', id).executeTakeFirst(); + return this.db + .selectFrom('video_stream_session') + .selectAll() + .where('id', '=', id) + .where('expiresAt', '>', new Date()) + .executeTakeFirst(); } @GenerateSql({ params: [DummyValue.UUID] }) @@ -47,7 +53,12 @@ export class VideoStreamRepository { @GenerateSql() getExpiredSessions() { - return this.db.selectFrom('video_stream_session').select(['id']).where('expiresAt', '<=', new Date()).execute(); + return this.db + .selectFrom('video_stream_session') + .innerJoin('asset', 'asset.id', 'video_stream_session.assetId') + .select(['video_stream_session.id', 'asset.ownerId']) + .where('video_stream_session.expiresAt', '<=', new Date()) + .execute(); } @GenerateSql({ params: [DummyValue.UUID, DummyValue.DATE] }) @@ -59,4 +70,50 @@ export class VideoStreamRepository { async deleteSession(id: string) { await this.db.deleteFrom('video_stream_session').where('id', '=', id).execute(); } + + @GenerateSql({ params: [DummyValue.UUID] }) + async getForMainPlaylist(id: string) { + return this.db + .selectFrom('asset') + .innerJoin('asset_exif', 'asset.id', 'asset_exif.assetId') + .where('asset.id', '=', id) + .innerJoin('asset_video', 'asset.id', 'asset_video.assetId') + .innerJoin('asset_keyframe', 'asset.id', 'asset_keyframe.assetId') + .select((eb) => withVideoStream(eb).$notNull().as('videoStream')) + .select((eb) => withVideoPackets(eb).$notNull().as('packets')) + .executeTakeFirst(); + } + + @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID] }) + async getForMediaPlaylist(id: string, sessionId: string) { + return this.db + .selectFrom('asset') + .innerJoin('asset_exif', 'asset.id', 'asset_exif.assetId') + .innerJoin('video_stream_session', 'asset.id', 'video_stream_session.assetId') + .where('asset.id', '=', id) + .where('video_stream_session.id', '=', sessionId) + .where('video_stream_session.expiresAt', '>', new Date()) + .innerJoin('asset_video', 'asset.id', 'asset_video.assetId') + .innerJoin('asset_keyframe', 'asset.id', 'asset_keyframe.assetId') + .select((eb) => withVideoStream(eb).$notNull().as('videoStream')) + .select((eb) => withVideoPackets(eb).$notNull().as('packets')) + .executeTakeFirst(); + } + + @GenerateSql({ params: [DummyValue.UUID] }) + async getForTranscoding(id: string) { + return this.db + .selectFrom('asset') + .innerJoin('asset_exif', 'asset.id', 'asset_exif.assetId') + .where('asset.id', '=', id) + .leftJoin('asset_audio', 'asset.id', 'asset_audio.assetId') + .innerJoin('asset_video', 'asset.id', 'asset_video.assetId') + .innerJoin('asset_keyframe', 'asset.id', 'asset_keyframe.assetId') + .select('asset.originalPath') + .select((eb) => withAudioStream(eb).as('audioStream')) + .select((eb) => withVideoStream(eb).$notNull().as('videoStream')) + .select((eb) => withVideoFormat(eb).$notNull().as('format')) + .select((eb) => withVideoPackets(eb).$notNull().as('packets')) + .executeTakeFirst(); + } } diff --git a/server/src/repositories/websocket.repository.ts b/server/src/repositories/websocket.repository.ts index b4a0fcc00a..d79e1563e3 100644 --- a/server/src/repositories/websocket.repository.ts +++ b/server/src/repositories/websocket.repository.ts @@ -10,13 +10,22 @@ import { Server, Socket } from 'socket.io'; import { AssetResponseDto } from 'src/dtos/asset-response.dto'; import { AuthDto } from 'src/dtos/auth.dto'; import { NotificationDto } from 'src/dtos/notification.dto'; -import { ReleaseNotification, ServerVersionResponseDto } from 'src/dtos/server.dto'; +import { ReleaseEventV1, ServerVersionResponseDto } from 'src/dtos/server.dto'; import { SyncAssetEditV1, SyncAssetExifV1, SyncAssetV2 } from 'src/dtos/sync.dto'; import { AppRestartEvent, ArgsOf, EventRepository } from 'src/repositories/event.repository'; import { LoggingRepository } from 'src/repositories/logging.repository'; import { handlePromiseError } from 'src/utils/misc'; -export const serverEvents = ['ConfigUpdate', 'AppRestart'] as const; +export const serverEvents = [ + 'ConfigUpdate', + 'AppRestart', + 'HlsSegmentRequest', + 'HlsSegmentResult', + 'HlsHeartbeat', + 'HlsSessionRequest', + 'HlsSessionResult', + 'HlsSessionEnd', +] as const; export type ServerEvents = (typeof serverEvents)[number]; export interface ClientEventMap { @@ -31,7 +40,7 @@ export interface ClientEventMap { on_person_thumbnail: [string]; on_server_version: [ServerVersionResponseDto]; on_config_update: []; - on_new_release: [ReleaseNotification]; + on_new_release: [ReleaseEventV1]; on_notification: [NotificationDto]; on_session_delete: [string]; diff --git a/server/src/repositories/workflow.repository.ts b/server/src/repositories/workflow.repository.ts index 69ecb83ae9..9ceef72a50 100644 --- a/server/src/repositories/workflow.repository.ts +++ b/server/src/repositories/workflow.repository.ts @@ -45,10 +45,10 @@ export class WorkflowRepository { } @GenerateSql({ params: [DummyValue.UUID] }) - search(dto: WorkflowSearchDto & { ownerId?: string }) { + search(dto: WorkflowSearchDto & { userId?: string }) { return this.queryBuilder() .$if(!!dto.id, (qb) => qb.where('id', '=', dto.id!)) - .$if(!!dto.ownerId, (qb) => qb.where('ownerId', '=', dto.ownerId!)) + .$if(!!dto.userId, (qb) => qb.where('ownerId', '=', dto.userId!)) .$if(!!dto.trigger, (qb) => qb.where('trigger', '=', dto.trigger!)) .$if(dto.enabled !== undefined, (qb) => qb.where('enabled', '=', dto.enabled!)) .orderBy('createdAt', 'desc') @@ -103,6 +103,10 @@ export class WorkflowRepository { }); } + async updateStep(id: string, dto: Updateable) { + await this.db.updateTable('workflow_step').where('workflow_step.id', '=', id).set(dto).execute(); + } + private async replaceAndReturn(tx: Kysely, workflowId: string, steps?: WorkflowStepUpsert[]) { if (steps) { await tx.deleteFrom('workflow_step').where('workflowId', '=', workflowId).execute(); diff --git a/server/src/schema/enums.ts b/server/src/schema/enums.ts index 73f8133441..ecf559c39d 100644 --- a/server/src/schema/enums.ts +++ b/server/src/schema/enums.ts @@ -1,12 +1,5 @@ import { registerEnum } from '@immich/sql-tools'; -import { - AlbumUserRole, - AssetStatus, - AssetVisibility, - ChecksumAlgorithm, - SourceType, - VideoSegmentCodec, -} from 'src/enum'; +import { AlbumUserRole, AssetStatus, AssetVisibility, ChecksumAlgorithm, SourceType, VideoCodec } from 'src/enum'; export const album_user_role_enum = registerEnum({ name: 'album_user_role_enum', @@ -35,5 +28,5 @@ export const asset_checksum_algorithm_enum = registerEnum({ export const video_stream_variant_codec_enum = registerEnum({ name: 'video_stream_variant_codec_enum', - values: Object.values(VideoSegmentCodec), + values: [VideoCodec.Av1, VideoCodec.Hevc, VideoCodec.H264], }); diff --git a/server/src/schema/migrations/1780435471692-DeleteMismatchedAssetFaces.ts b/server/src/schema/migrations/1780435471692-DeleteMismatchedAssetFaces.ts new file mode 100644 index 0000000000..d0387c4038 --- /dev/null +++ b/server/src/schema/migrations/1780435471692-DeleteMismatchedAssetFaces.ts @@ -0,0 +1,16 @@ +import { Kysely, sql } from 'kysely'; + +export async function up(db: Kysely): Promise { + // Delete unauthorized cross-owner asset faces + await sql` + DELETE FROM asset_face + USING person, asset + WHERE asset_face."personId" = person.id + AND asset_face."assetId" = asset.id + AND person."ownerId" != asset."ownerId" + `.execute(db); +} + +export async function down(): Promise { + // Not implemented: the deleted rows were unauthorized cross-owner entries +} diff --git a/server/src/schema/tables/workflow.table.ts b/server/src/schema/tables/workflow.table.ts index 8ac89d4b65..944fffd9d5 100644 --- a/server/src/schema/tables/workflow.table.ts +++ b/server/src/schema/tables/workflow.table.ts @@ -1,3 +1,4 @@ +import { WorkflowTrigger } from '@immich/plugin-sdk'; import { Column, CreateDateColumn, @@ -9,7 +10,6 @@ import { UpdateDateColumn, } from '@immich/sql-tools'; import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators'; -import { WorkflowTrigger } from 'src/enum'; import { UserTable } from 'src/schema/tables/user.table'; @Table('workflow') diff --git a/server/src/services/album.service.ts b/server/src/services/album.service.ts index 723288e5b5..31c4ff2e38 100644 --- a/server/src/services/album.service.ts +++ b/server/src/services/album.service.ts @@ -37,15 +37,12 @@ export class AlbumService extends BaseService { }; } - async getAll( - { user: { id: ownerId } }: AuthDto, - { assetId, isOwned, isShared }: GetAlbumsDto, - ): Promise { + async getAll({ user: { id: ownerId } }: AuthDto, { assetId, ...rest }: GetAlbumsDto): Promise { await this.albumRepository.updateThumbnails(); const albums = assetId ? await this.albumRepository.getByAssetId(ownerId, assetId) - : await this.albumRepository.getAll(ownerId, { isOwned, isShared }); + : await this.albumRepository.getAll(ownerId, rest); if (albums.length === 0) { return []; diff --git a/server/src/services/hls.service.spec.ts b/server/src/services/hls.service.spec.ts new file mode 100644 index 0000000000..ccbba48107 --- /dev/null +++ b/server/src/services/hls.service.spec.ts @@ -0,0 +1,327 @@ +import { BadRequestException, NotFoundException } from '@nestjs/common'; +import { TranscodeHardwareAcceleration } from 'src/enum'; +import { HlsService } from 'src/services/hls.service'; +import { eiffelTower, train, waterfall } from 'test/fixtures/media.stub'; +import { factory } from 'test/small.factory'; +import { newTestService, ServiceMocks } from 'test/utils'; + +// EXTINF values come from FFmpeg's playlist to enforce an exact match +const eiffelExpectedMediaPlaylist = `#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-TARGETDURATION:2 +#EXT-X-MEDIA-SEQUENCE:0 +#EXT-X-PLAYLIST-TYPE:VOD +#EXT-X-MAP:URI="init.mp4" +#EXTINF:2.007222, +seg_0.m4s +#EXTINF:2.007222, +seg_1.m4s +#EXTINF:2.007222, +seg_2.m4s +#EXTINF:2.007222, +seg_3.m4s +#EXTINF:2.007222, +seg_4.m4s +#EXTINF:2.007222, +seg_5.m4s +#EXTINF:2.007222, +seg_6.m4s +#EXTINF:2.007222, +seg_7.m4s +#EXTINF:2.007222, +seg_8.m4s +#EXTINF:2.007222, +seg_9.m4s +#EXTINF:2.007222, +seg_10.m4s +#EXTINF:0.281011, +seg_11.m4s +#EXT-X-ENDLIST +`; + +const waterfallExpectedMediaPlaylist = `#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-TARGETDURATION:2 +#EXT-X-MEDIA-SEQUENCE:0 +#EXT-X-PLAYLIST-TYPE:VOD +#EXT-X-MAP:URI="init.mp4" +#EXTINF:2.011405, +seg_0.m4s +#EXTINF:2.011405, +seg_1.m4s +#EXTINF:2.011405, +seg_2.m4s +#EXTINF:2.011405, +seg_3.m4s +#EXTINF:2.011405, +seg_4.m4s +#EXTINF:0.301711, +seg_5.m4s +#EXT-X-ENDLIST +`; + +const trainExpectedMediaPlaylist = `#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-TARGETDURATION:2 +#EXT-X-MEDIA-SEQUENCE:0 +#EXT-X-PLAYLIST-TYPE:VOD +#EXT-X-MAP:URI="init.mp4" +#EXTINF:2.000000, +seg_0.m4s +#EXTINF:2.000000, +seg_1.m4s +#EXTINF:2.000000, +seg_2.m4s +#EXTINF:2.000000, +seg_3.m4s +#EXTINF:2.000000, +seg_4.m4s +#EXTINF:2.000000, +seg_5.m4s +#EXTINF:2.000000, +seg_6.m4s +#EXTINF:2.000000, +seg_7.m4s +#EXTINF:2.000000, +seg_8.m4s +#EXTINF:2.000000, +seg_9.m4s +#EXTINF:1.733333, +seg_10.m4s +#EXT-X-ENDLIST +`; + +const sessionId = '00000000-0000-0000-0000-000000000000'; + +const eiffelExpectedMasterDisabled = `#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-STREAM-INF:BANDWIDTH=1000000,RESOLUTION=480x852,CODECS="av01.0.04M.08,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=24.910 +${sessionId}/0/playlist.m3u8 +#EXT-X-STREAM-INF:BANDWIDTH=1200000,RESOLUTION=480x852,CODECS="hvc1.1.6.L90.B0,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=24.910 +${sessionId}/1/playlist.m3u8 +#EXT-X-STREAM-INF:BANDWIDTH=2500000,RESOLUTION=480x852,CODECS="avc1.64001e,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=24.910 +${sessionId}/2/playlist.m3u8 +#EXT-X-STREAM-INF:BANDWIDTH=2000000,RESOLUTION=720x1280,CODECS="av01.0.08M.08,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=24.910 +${sessionId}/3/playlist.m3u8 +#EXT-X-STREAM-INF:BANDWIDTH=2500000,RESOLUTION=720x1280,CODECS="hvc1.1.6.L93.B0,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=24.910 +${sessionId}/4/playlist.m3u8 +#EXT-X-STREAM-INF:BANDWIDTH=5000000,RESOLUTION=720x1280,CODECS="avc1.64001f,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=24.910 +${sessionId}/5/playlist.m3u8 +#EXT-X-STREAM-INF:BANDWIDTH=4000000,RESOLUTION=1080x1920,CODECS="av01.0.09M.08,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=24.910 +${sessionId}/6/playlist.m3u8 +#EXT-X-STREAM-INF:BANDWIDTH=4500000,RESOLUTION=1080x1920,CODECS="hvc1.1.6.L120.B0,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=24.910 +${sessionId}/7/playlist.m3u8 +#EXT-X-STREAM-INF:BANDWIDTH=8000000,RESOLUTION=1080x1920,CODECS="avc1.640028,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=24.910 +${sessionId}/8/playlist.m3u8 +`; + +const eiffelExpectedMasterRkmpp = `#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-STREAM-INF:BANDWIDTH=1200000,RESOLUTION=480x852,CODECS="hvc1.1.6.L90.B0,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=24.910 +${sessionId}/1/playlist.m3u8 +#EXT-X-STREAM-INF:BANDWIDTH=2500000,RESOLUTION=480x852,CODECS="avc1.64001e,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=24.910 +${sessionId}/2/playlist.m3u8 +#EXT-X-STREAM-INF:BANDWIDTH=2500000,RESOLUTION=720x1280,CODECS="hvc1.1.6.L93.B0,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=24.910 +${sessionId}/4/playlist.m3u8 +#EXT-X-STREAM-INF:BANDWIDTH=5000000,RESOLUTION=720x1280,CODECS="avc1.64001f,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=24.910 +${sessionId}/5/playlist.m3u8 +#EXT-X-STREAM-INF:BANDWIDTH=4500000,RESOLUTION=1080x1920,CODECS="hvc1.1.6.L120.B0,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=24.910 +${sessionId}/7/playlist.m3u8 +#EXT-X-STREAM-INF:BANDWIDTH=8000000,RESOLUTION=1080x1920,CODECS="avc1.640028,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=24.910 +${sessionId}/8/playlist.m3u8 +`; + +const waterfallExpectedMasterDisabled = `#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-STREAM-INF:BANDWIDTH=1000000,RESOLUTION=480x852,CODECS="av01.0.04M.08,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=29.830 +${sessionId}/0/playlist.m3u8 +#EXT-X-STREAM-INF:BANDWIDTH=1200000,RESOLUTION=480x852,CODECS="hvc1.1.6.L90.B0,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=29.830 +${sessionId}/1/playlist.m3u8 +#EXT-X-STREAM-INF:BANDWIDTH=2500000,RESOLUTION=480x852,CODECS="avc1.64001e,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=29.830 +${sessionId}/2/playlist.m3u8 +#EXT-X-STREAM-INF:BANDWIDTH=2000000,RESOLUTION=720x1280,CODECS="av01.0.08M.08,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=29.830 +${sessionId}/3/playlist.m3u8 +#EXT-X-STREAM-INF:BANDWIDTH=2500000,RESOLUTION=720x1280,CODECS="hvc1.1.6.L93.B0,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=29.830 +${sessionId}/4/playlist.m3u8 +#EXT-X-STREAM-INF:BANDWIDTH=5000000,RESOLUTION=720x1280,CODECS="avc1.64001f,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=29.830 +${sessionId}/5/playlist.m3u8 +#EXT-X-STREAM-INF:BANDWIDTH=4000000,RESOLUTION=1080x1920,CODECS="av01.0.09M.08,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=29.830 +${sessionId}/6/playlist.m3u8 +#EXT-X-STREAM-INF:BANDWIDTH=4500000,RESOLUTION=1080x1920,CODECS="hvc1.1.6.L120.B0,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=29.830 +${sessionId}/7/playlist.m3u8 +#EXT-X-STREAM-INF:BANDWIDTH=8000000,RESOLUTION=1080x1920,CODECS="avc1.640028,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=29.830 +${sessionId}/8/playlist.m3u8 +`; + +describe(HlsService.name, () => { + let sut: HlsService; + let mocks: ServiceMocks; + + beforeEach(() => { + ({ sut, mocks } = newTestService(HlsService)); + }); + + describe('getMainPlaylist', () => { + const auth = factory.auth(); + const assetId = 'asset-1'; + + const setup = (asset: typeof eiffelTower | typeof waterfall, accel: TranscodeHardwareAcceleration) => { + mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set([assetId])); + mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { realtime: { enabled: true }, accel } }); + mocks.videoStream.getForMainPlaylist.mockResolvedValue(asset); + mocks.crypto.randomUUID.mockReturnValue(sessionId); + mocks.websocket.serverSend.mockImplementation((event, ...rest) => { + if (event === 'HlsSessionRequest') { + const { sessionId: id } = rest[0] as { sessionId: string }; + queueMicrotask(() => sut.onSessionResult({ sessionId: id })); + } + }); + }; + + it('returns main playlist for eiffel-tower (1080p portrait, no acceleration)', async () => { + setup(eiffelTower, TranscodeHardwareAcceleration.Disabled); + await expect(sut.getMainPlaylist(auth, assetId)).resolves.toBe(eiffelExpectedMasterDisabled); + }); + + it('returns main playlist for eiffel-tower with RKMPP (no AV1 variants)', async () => { + setup(eiffelTower, TranscodeHardwareAcceleration.Rkmpp); + await expect(sut.getMainPlaylist(auth, assetId)).resolves.toBe(eiffelExpectedMasterRkmpp); + }); + + it('returns main playlist for waterfall (4K landscape) with no acceleration', async () => { + setup(waterfall, TranscodeHardwareAcceleration.Disabled); + await expect(sut.getMainPlaylist(auth, assetId)).resolves.toBe(waterfallExpectedMasterDisabled); + }); + + it('throws BadRequestException when realtime transcoding is disabled', async () => { + mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set([assetId])); + mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { realtime: { enabled: false } } }); + await expect(sut.getMainPlaylist(auth, assetId)).rejects.toBeInstanceOf(BadRequestException); + }); + + it('throws NotFoundException when asset is not yet ready for streaming', async () => { + mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set([assetId])); + mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { realtime: { enabled: true } } }); + await expect(sut.getMainPlaylist(auth, assetId)).rejects.toBeInstanceOf(NotFoundException); + }); + }); + + describe('getMediaPlaylist', () => { + const auth = factory.auth(); + const assetId = 'asset-1'; + const fixtures = [ + { data: eiffelTower, playlist: eiffelExpectedMediaPlaylist }, + { data: waterfall, playlist: waterfallExpectedMediaPlaylist }, + { data: train, playlist: trainExpectedMediaPlaylist }, + ]; + + it.each(fixtures)('matches FFmpeg for $data.originalPath', async ({ data, playlist }) => { + mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set([assetId])); + mocks.videoStream.getForMediaPlaylist.mockResolvedValue(data); + await expect(sut.getMediaPlaylist(auth, assetId, sessionId)).resolves.toBe(playlist); + }); + + it('throws NotFoundException when the session/asset cannot be loaded', async () => { + mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set([assetId])); + await expect(sut.getMediaPlaylist(auth, assetId, sessionId)).rejects.toBeInstanceOf(NotFoundException); + }); + }); + + describe('getSegment', () => { + const auth = factory.auth(); + const assetId = 'asset-1'; + const variantIndex = 0; + + beforeEach(() => { + mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set([assetId])); + mocks.videoStream.getSession.mockResolvedValue({ id: sessionId, assetId } as never); + mocks.storage.checkFileExists.mockResolvedValue(true); + }); + + it('emits HlsHeartbeat with segmentIndex 0 for the first init.mp4 request', async () => { + await sut.getSegment(auth, assetId, sessionId, variantIndex, 'init.mp4'); + expect(mocks.websocket.serverSend).toHaveBeenCalledWith('HlsHeartbeat', { + sessionId, + variantIndex, + segmentIndex: 0, + }); + }); + + it('emits HlsHeartbeat with the parsed segment number for seg_K.m4s', async () => { + await sut.getSegment(auth, assetId, sessionId, variantIndex, 'seg_5.m4s'); + expect(mocks.websocket.serverSend).toHaveBeenCalledWith('HlsHeartbeat', { + sessionId, + variantIndex, + segmentIndex: 5, + }); + }); + + it('returns lastRequested + 1 for init.mp4 after a segment has been served', async () => { + await sut.getSegment(auth, assetId, sessionId, variantIndex, 'seg_5.m4s'); + mocks.websocket.serverSend.mockClear(); + await sut.getSegment(auth, assetId, sessionId, variantIndex, 'init.mp4'); + expect(mocks.websocket.serverSend).toHaveBeenCalledWith('HlsHeartbeat', { + sessionId, + variantIndex, + segmentIndex: 6, + }); + }); + + it('updates lastRequested on a backward-seek segment request', async () => { + await sut.getSegment(auth, assetId, sessionId, variantIndex, 'seg_5.m4s'); + await sut.getSegment(auth, assetId, sessionId, variantIndex, 'seg_3.m4s'); + mocks.websocket.serverSend.mockClear(); + await sut.getSegment(auth, assetId, sessionId, variantIndex, 'init.mp4'); + expect(mocks.websocket.serverSend).toHaveBeenCalledWith('HlsHeartbeat', { + sessionId, + variantIndex, + segmentIndex: 4, + }); + }); + + it('tracks segment state per session independently', async () => { + await sut.getSegment(auth, assetId, 'session-a', variantIndex, 'seg_5.m4s'); + await sut.getSegment(auth, assetId, 'session-b', variantIndex, 'seg_2.m4s'); + mocks.websocket.serverSend.mockClear(); + await sut.getSegment(auth, assetId, 'session-a', variantIndex, 'init.mp4'); + await sut.getSegment(auth, assetId, 'session-b', variantIndex, 'init.mp4'); + expect(mocks.websocket.serverSend).toHaveBeenCalledWith('HlsHeartbeat', { + sessionId: 'session-a', + variantIndex, + segmentIndex: 6, + }); + expect(mocks.websocket.serverSend).toHaveBeenCalledWith('HlsHeartbeat', { + sessionId: 'session-b', + variantIndex, + segmentIndex: 3, + }); + }); + + it('rejects pending waiters for the previous variant on variant change', async () => { + mocks.storage.checkFileExists.mockResolvedValueOnce(false); + + const pending = sut.getSegment(auth, assetId, sessionId, 0, 'seg_1.m4s'); + await new Promise((resolve) => setImmediate(resolve)); + await sut.getSegment(auth, assetId, sessionId, 1, 'seg_1.m4s'); + + await expect(pending).rejects.toThrow('Variant changed'); + }); + + it('throws NotFoundException when the session does not exist', async () => { + mocks.videoStream.getSession.mockReset(); + await expect(sut.getSegment(auth, assetId, sessionId, variantIndex, 'init.mp4')).rejects.toBeInstanceOf( + NotFoundException, + ); + }); + }); + + describe('endSession', () => { + it('emits HlsSessionEnd', async () => { + const auth = factory.auth(); + const assetId = 'asset-1'; + mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set([assetId])); + await sut.endSession(auth, assetId, sessionId); + expect(mocks.websocket.serverSend).toHaveBeenCalledWith('HlsSessionEnd', { sessionId }); + }); + }); +}); diff --git a/server/src/services/hls.service.ts b/server/src/services/hls.service.ts new file mode 100644 index 0000000000..fba8b8e060 --- /dev/null +++ b/server/src/services/hls.service.ts @@ -0,0 +1,198 @@ +import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common'; +import { constants } from 'node:fs'; +import { join } from 'node:path'; +import { + HLS_SEGMENT_DURATION, + HLS_SEGMENT_FILENAME_REGEX, + HLS_VARIANTS, + HLS_VERSION, + SUPPORTED_HWA_CODECS, +} from 'src/constants'; +import { StorageCore } from 'src/cores/storage.core'; +import { OnEvent } from 'src/decorators'; +import { AuthDto } from 'src/dtos/auth.dto'; +import { SystemConfigFFmpegDto } from 'src/dtos/system-config.dto'; +import { CacheControl, ImmichWorker, Permission } from 'src/enum'; +import { ArgOf } from 'src/repositories/event.repository'; +import { BaseService } from 'src/services/base.service'; +import { VideoPacketInfo, VideoStreamInfo } from 'src/types'; +import { PendingEvents } from 'src/utils/event'; +import { ImmichFileResponse } from 'src/utils/file'; +import { getOutputSize } from 'src/utils/media'; + +type AssetWithStreamInfo = { videoStream: VideoStreamInfo & { timeBase: number }; packets: VideoPacketInfo }; +type ApiSession = { lastRequestedSegment: number | null; lastVariantIndex: number | null }; + +@Injectable() +export class HlsService extends BaseService { + private pendingSegments = new PendingEvents<'HlsSegmentResult'>({ timeoutMs: 15_000 }); + private pendingSessions = new PendingEvents<'HlsSessionResult'>({ timeoutMs: 5000 }); + private sessions = new Map(); + + @OnEvent({ name: 'HlsSessionResult', server: true, workers: [ImmichWorker.Api] }) + onSessionResult(event: ArgOf<'HlsSessionResult'>) { + this.pendingSessions.complete(event.sessionId, event); + if (event.error) { + this.sessions.delete(event.sessionId); + this.pendingSegments.rejectByPrefix(`${event.sessionId}:`, event.error); + } + } + + @OnEvent({ name: 'HlsSessionEnd', server: true, workers: [ImmichWorker.Api] }) + onSessionEnd({ sessionId }: ArgOf<'HlsSessionEnd'>) { + this.sessions.delete(sessionId); + this.pendingSegments.rejectByPrefix(`${sessionId}:`, 'Session ended'); + } + + @OnEvent({ name: 'HlsSegmentResult', server: true, workers: [ImmichWorker.Api] }) + onSegmentResult(event: ArgOf<'HlsSegmentResult'>) { + this.pendingSegments.complete(this.getSegmentKey(event), event); + } + + async getMainPlaylist(auth: AuthDto, assetId: string) { + await this.requireAccess({ auth, permission: Permission.AssetView, ids: [assetId] }); + const { ffmpeg } = await this.getConfig({ withCache: true }); + if (!ffmpeg.realtime.enabled) { + throw new BadRequestException('Real-time transcoding is not enabled'); + } + + const asset = await this.videoStreamRepository.getForMainPlaylist(assetId); + if (!asset) { + throw new NotFoundException('Asset is not yet ready for streaming'); + } + + // Sharing the sessionId allows only one microservices worker to successfully insert to the session table. + // The microservices worker that creates a session owns the transcoding lifecycle for it. + const sessionId = this.cryptoRepository.randomUUID(); + this.websocketRepository.serverSend('HlsSessionRequest', { sessionId, assetId, ownerId: auth.user.id }); + await this.pendingSessions.wait(sessionId); + this.trackSession(sessionId); + + return this.generateMainPlaylist(sessionId, ffmpeg, asset); + } + + async getMediaPlaylist(auth: AuthDto, assetId: string, sessionId: string) { + await this.requireAccess({ auth, permission: Permission.AssetView, ids: [assetId] }); + + const asset = await this.videoStreamRepository.getForMediaPlaylist(assetId, sessionId); + if (!asset) { + throw new NotFoundException('Asset not found or not yet ready for streaming'); + } + + return this.generateMediaPlaylist(asset); + } + + async getSegment(auth: AuthDto, assetId: string, sessionId: string, variantIndex: number, filename: string) { + await this.requireAccess({ auth, permission: Permission.AssetView, ids: [assetId] }); + + const session = await this.videoStreamRepository.getSession(sessionId); + if (!session) { + throw new NotFoundException('Session not found'); + } + + const variantDir = StorageCore.getHlsVariantFolder({ ownerId: auth.user.id, sessionId, variantIndex }); + const path = join(variantDir, filename); + const response = new ImmichFileResponse({ + path, + contentType: 'video/mp4', + cacheControl: CacheControl.PrivateWithCache, + }); + + const apiSession = this.trackSession(sessionId, variantIndex); + const segmentIndex = this.getSegmentIndex(apiSession, filename); + this.websocketRepository.serverSend('HlsHeartbeat', { sessionId, variantIndex, segmentIndex }); + + if (await this.storageRepository.checkFileExists(path, constants.R_OK)) { + return response; + } + + this.websocketRepository.serverSend('HlsSegmentRequest', { sessionId, assetId, variantIndex, segmentIndex }); + await this.pendingSegments.wait(this.getSegmentKey({ sessionId, variantIndex, segmentIndex })); + + return response; + } + + async endSession(auth: AuthDto, assetId: string, sessionId: string): Promise { + await this.requireAccess({ auth, permission: Permission.AssetView, ids: [assetId] }); + + this.websocketRepository.serverSend('HlsSessionEnd', { sessionId }); + } + + private generateMainPlaylist(sessionId: string, ffmpeg: SystemConfigFFmpegDto, asset: AssetWithStreamInfo) { + const fps = ((asset.packets.packetCount * asset.videoStream.timeBase) / asset.packets.totalDuration).toFixed(3); + const sourceResolution = Math.min(asset.videoStream.height, asset.videoStream.width); + const targetResolution = Math.max(sourceResolution, HLS_VARIANTS[0].resolution); + const lines = ['#EXTM3U', `#EXT-X-VERSION:${HLS_VERSION}`]; + for (let i = 0; i < HLS_VARIANTS.length; i++) { + const { resolution, bitrate, codec, codecString } = HLS_VARIANTS[i]; + if (resolution > targetResolution || !SUPPORTED_HWA_CODECS[ffmpeg.accel].includes(codec)) { + continue; + } + const { width, height } = getOutputSize(asset.videoStream, resolution); + lines.push( + `#EXT-X-STREAM-INF:BANDWIDTH=${bitrate},RESOLUTION=${width}x${height},CODECS="${codecString},mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=${fps}`, + `${sessionId}/${i}/playlist.m3u8`, + ); + } + lines.push(''); + + if (lines.length === 3) { + throw new NotFoundException('No supported variants for this video'); + } + + return lines.join('\n'); + } + + private generateMediaPlaylist({ videoStream, packets }: AssetWithStreamInfo) { + const fps = (packets.packetCount * videoStream.timeBase) / packets.totalDuration; + const framesPerSegment = Math.ceil(HLS_SEGMENT_DURATION * fps); + const fullSegmentDuration = framesPerSegment / fps; + const segmentCount = Math.ceil(packets.outputFrames / framesPerSegment); + const lastSegmentFrames = packets.outputFrames - framesPerSegment * (segmentCount - 1); + const lastSegmentDuration = lastSegmentFrames / fps; + + const lines = [ + '#EXTM3U', + `#EXT-X-VERSION:${HLS_VERSION}`, + `#EXT-X-TARGETDURATION:${HLS_SEGMENT_DURATION}`, + '#EXT-X-MEDIA-SEQUENCE:0', + '#EXT-X-PLAYLIST-TYPE:VOD', + '#EXT-X-MAP:URI="init.mp4"', + ]; + + for (let i = 0; i < segmentCount - 1; i++) { + lines.push(`#EXTINF:${fullSegmentDuration.toFixed(6)},`, `seg_${i}.m4s`); + } + lines.push(`#EXTINF:${lastSegmentDuration.toFixed(6)},`, `seg_${segmentCount - 1}.m4s`, '#EXT-X-ENDLIST', ''); + + return lines.join('\n'); + } + + private getSegmentKey({ sessionId, variantIndex, segmentIndex }: ArgOf<'HlsSegmentResult'>) { + return `${sessionId}:${variantIndex}:${segmentIndex}`; + } + + private getSegmentIndex(session: ApiSession, filename: string) { + if (filename.endsWith('.mp4')) { + return (session.lastRequestedSegment ?? -1) + 1; + } + const segmentIndex = Number.parseInt(HLS_SEGMENT_FILENAME_REGEX.exec(filename)![1]); + session.lastRequestedSegment = segmentIndex; + return segmentIndex; + } + + private trackSession(id: string, variantIndex: number | null = null) { + const session = this.sessions.get(id); + if (!session) { + const newSession = { lastRequestedSegment: null, lastVariantIndex: variantIndex }; + this.sessions.set(id, newSession); + return newSession; + } + + if (session.lastVariantIndex !== null && session.lastVariantIndex !== variantIndex) { + this.pendingSegments.rejectByPrefix(`${id}:${session.lastVariantIndex}:`, 'Variant changed'); + } + session.lastVariantIndex = variantIndex; + return session; + } +} diff --git a/server/src/services/index.ts b/server/src/services/index.ts index b733483aa8..3c23e723bc 100644 --- a/server/src/services/index.ts +++ b/server/src/services/index.ts @@ -11,6 +11,7 @@ import { DatabaseBackupService } from 'src/services/database-backup.service'; import { DatabaseService } from 'src/services/database.service'; import { DownloadService } from 'src/services/download.service'; import { DuplicateService } from 'src/services/duplicate.service'; +import { HlsService } from 'src/services/hls.service'; import { JobService } from 'src/services/job.service'; import { LibraryService } from 'src/services/library.service'; import { MaintenanceService } from 'src/services/maintenance.service'; @@ -39,6 +40,7 @@ import { SystemMetadataService } from 'src/services/system-metadata.service'; import { TagService } from 'src/services/tag.service'; import { TelemetryService } from 'src/services/telemetry.service'; import { TimelineService } from 'src/services/timeline.service'; +import { TranscodingService } from 'src/services/transcoding.service'; import { TrashService } from 'src/services/trash.service'; import { UserAdminService } from 'src/services/user-admin.service'; import { UserService } from 'src/services/user.service'; @@ -61,6 +63,7 @@ export const services = [ DatabaseService, DownloadService, DuplicateService, + HlsService, JobService, LibraryService, MaintenanceService, @@ -89,6 +92,7 @@ export const services = [ TagService, TelemetryService, TimelineService, + TranscodingService, TrashService, UserAdminService, UserService, diff --git a/server/src/services/person.service.spec.ts b/server/src/services/person.service.spec.ts index 8b303d04f6..e6a11786af 100644 --- a/server/src/services/person.service.spec.ts +++ b/server/src/services/person.service.spec.ts @@ -57,7 +57,6 @@ describe(PersonService.name, () => { ], }); expect(mocks.person.getAllForUser).toHaveBeenCalledWith({ skip: 0, take: 10 }, auth.user.id, { - minimumFaceCount: 3, withHidden: true, }); }); @@ -84,7 +83,6 @@ describe(PersonService.name, () => { ], }); expect(mocks.person.getAllForUser).toHaveBeenCalledWith({ skip: 0, take: 10 }, auth.user.id, { - minimumFaceCount: 3, withHidden: false, }); }); @@ -454,6 +452,30 @@ describe(PersonService.name, () => { expect(mocks.person.update).not.toHaveBeenCalled(); expect(mocks.job.queueAll).not.toHaveBeenCalled(); }); + + it('should reject creating a face on an asset the user does not own', async () => { + const auth = AuthFactory.create(); + const asset = AssetFactory.create(); + const person = PersonFactory.create({ faceAssetId: null }); + + mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set()); + mocks.access.person.checkOwnerAccess.mockResolvedValue(new Set([person.id])); + + await expect( + sut.createFace(auth, { + assetId: asset.id, + personId: person.id, + imageHeight: 500, + imageWidth: 400, + x: 10, + y: 20, + width: 100, + height: 110, + }), + ).rejects.toBeInstanceOf(BadRequestException); + + expect(mocks.person.createAssetFace).not.toHaveBeenCalled(); + }); }); describe('createNewFeaturePhoto', () => { diff --git a/server/src/services/person.service.ts b/server/src/services/person.service.ts index fde5313f4d..bb17590795 100644 --- a/server/src/services/person.service.ts +++ b/server/src/services/person.service.ts @@ -63,9 +63,7 @@ export class PersonService extends BaseService { } closestFaceAssetId = person.faceAssetId; } - const { machineLearning } = await this.getConfig({ withCache: false }); const { items, hasNextPage } = await this.personRepository.getAllForUser(pagination, auth.user.id, { - minimumFaceCount: machineLearning.facialRecognition.minFaces, withHidden, closestFaceAssetId, }); @@ -627,7 +625,7 @@ export class PersonService extends BaseService { // TODO return a asset face response async createFace(auth: AuthDto, dto: AssetFaceCreateDto): Promise { await Promise.all([ - this.requireAccess({ auth, permission: Permission.AssetRead, ids: [dto.assetId] }), + this.requireAccess({ auth, permission: Permission.AssetUpdate, ids: [dto.assetId] }), this.requireAccess({ auth, permission: Permission.PersonRead, ids: [dto.personId] }), ]); diff --git a/server/src/services/queue.service.spec.ts b/server/src/services/queue.service.spec.ts index d4c425e8bd..48c61c0951 100644 --- a/server/src/services/queue.service.spec.ts +++ b/server/src/services/queue.service.spec.ts @@ -41,6 +41,7 @@ describe(QueueService.name, () => { { name: JobName.PersonCleanup }, { name: JobName.MemoryCleanup }, { name: JobName.SessionCleanup }, + { name: JobName.HlsSessionCleanup }, { name: JobName.AuditTableCleanup }, { name: JobName.MemoryGenerate }, { name: JobName.UserSyncUsage }, diff --git a/server/src/services/queue.service.ts b/server/src/services/queue.service.ts index ba6f4c5f3b..d11c9180b2 100644 --- a/server/src/services/queue.service.ts +++ b/server/src/services/queue.service.ts @@ -269,6 +269,7 @@ export class QueueService extends BaseService { { name: JobName.PersonCleanup }, { name: JobName.MemoryCleanup }, { name: JobName.SessionCleanup }, + { name: JobName.HlsSessionCleanup }, { name: JobName.AuditTableCleanup }, ); } diff --git a/server/src/services/server.service.spec.ts b/server/src/services/server.service.spec.ts index 6e1187a900..e1575a496a 100644 --- a/server/src/services/server.service.spec.ts +++ b/server/src/services/server.service.spec.ts @@ -148,6 +148,7 @@ describe(ServerService.name, () => { configFile: false, trash: true, email: false, + realtimeTranscoding: false, }); expect(mocks.systemMetadata.get).toHaveBeenCalled(); }); @@ -167,6 +168,7 @@ describe(ServerService.name, () => { mapDarkStyleUrl: 'https://tiles.immich.cloud/v1/style/dark.json', mapLightStyleUrl: 'https://tiles.immich.cloud/v1/style/light.json', maintenanceMode: false, + minFaces: 3, }); expect(mocks.systemMetadata.get).toHaveBeenCalled(); }); diff --git a/server/src/services/server.service.ts b/server/src/services/server.service.ts index ff9e90f7e0..3b66b677a5 100644 --- a/server/src/services/server.service.ts +++ b/server/src/services/server.service.ts @@ -86,7 +86,7 @@ export class ServerService extends BaseService { } async getFeatures(): Promise { - const { reverseGeocoding, metadata, map, machineLearning, trash, oauth, passwordLogin, notifications } = + const { reverseGeocoding, metadata, map, machineLearning, trash, oauth, passwordLogin, notifications, ffmpeg } = await this.getConfig({ withCache: false }); const { configFile } = this.configRepository.getEnv(); @@ -106,6 +106,7 @@ export class ServerService extends BaseService { passwordLogin: passwordLogin.enabled, configFile: !!configFile, email: notifications.smtp.enabled, + realtimeTranscoding: ffmpeg.realtime.enabled, }; } @@ -127,6 +128,7 @@ export class ServerService extends BaseService { mapDarkStyleUrl: config.map.darkStyle, mapLightStyleUrl: config.map.lightStyle, maintenanceMode: false, + minFaces: config.machineLearning.facialRecognition.minFaces, }; } diff --git a/server/src/services/system-config.service.spec.ts b/server/src/services/system-config.service.spec.ts index c9a8492b5d..2d0850ac58 100644 --- a/server/src/services/system-config.service.spec.ts +++ b/server/src/services/system-config.service.spec.ts @@ -1,5 +1,6 @@ import { BadRequestException } from '@nestjs/common'; import { defaults, SystemConfig } from 'src/config'; +import { ReleaseChannel } from 'src/dtos/system-config.dto'; import { AudioCodec, Colorspace, @@ -72,6 +73,9 @@ const updatedConfig = Object.freeze({ accel: TranscodeHardwareAcceleration.Disabled, accelDecode: true, tonemap: ToneMapping.Hable, + realtime: { + enabled: false, + }, }, logging: { enabled: true, @@ -184,6 +188,7 @@ const updatedConfig = Object.freeze({ }, newVersionCheck: { enabled: true, + channel: ReleaseChannel.Stable, }, trash: { enabled: true, diff --git a/server/src/services/transcoding.service.spec.ts b/server/src/services/transcoding.service.spec.ts new file mode 100644 index 0000000000..349cba6a7d --- /dev/null +++ b/server/src/services/transcoding.service.spec.ts @@ -0,0 +1,539 @@ +import { + HLS_BACKPRESSURE_PAUSE_SEGMENTS, + HLS_BACKPRESSURE_RESUME_SEGMENTS, + HLS_CLEANUP_INTERVAL_MS, + HLS_INACTIVITY_TIMEOUT_MS, + HLS_LEASE_DURATION_MS, +} from 'src/constants'; +import { TranscodingService } from 'src/services/transcoding.service'; +import { VIDEO_STREAM_SESSION_PK_CONSTRAINT } from 'src/utils/database'; +import { eiffelTower, train, waterfall } from 'test/fixtures/media.stub'; +import { mockSpawn, newTestService, ServiceMocks } from 'test/utils'; +import { vi } from 'vitest'; + +describe(TranscodingService.name, () => { + let sut: TranscodingService; + let mocks: ServiceMocks; + + const sessionId = 'session-1'; + const assetId = 'asset-1'; + const ownerId = 'user-1'; + + const completeSegment = (index: number) => { + const listener = vi.mocked(mocks.storage.watchDir).mock.lastCall?.[1]; + expect(listener).toBeDefined(); + listener!('rename', `seg_${index}.m4s`); + }; + + const completeSegmentsThrough = (start: number, end: number) => { + for (let i = start; i <= end; i++) { + completeSegment(i); + } + }; + + beforeEach(() => { + ({ sut, mocks } = newTestService(TranscodingService)); + mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { realtime: { enabled: true } } }); + mocks.videoStream.getForTranscoding.mockResolvedValue(eiffelTower); + }); + + describe('onSessionRequest', () => { + it('creates the session row and emits HlsSessionResult on success', async () => { + await sut.onSessionRequest({ sessionId, assetId, ownerId }); + + expect(mocks.videoStream.createSession).toHaveBeenCalledWith({ + id: sessionId, + assetId, + expiresAt: expect.any(Date), + }); + expect(mocks.websocket.serverSend).toHaveBeenCalledWith('HlsSessionResult', { sessionId }); + }); + + it('treats a primary-key conflict as a no-op for replay tolerance', async () => { + mocks.videoStream.createSession.mockRejectedValue({ constraint_name: VIDEO_STREAM_SESSION_PK_CONSTRAINT }); + + await sut.onSessionRequest({ sessionId, assetId, ownerId }); + + expect(mocks.websocket.serverSend).not.toHaveBeenCalled(); + }); + + it('emits HlsSessionResult with an error on other DB failures', async () => { + mocks.videoStream.createSession.mockRejectedValue(new Error('database is down')); + + await sut.onSessionRequest({ sessionId, assetId, ownerId }); + + expect(mocks.websocket.serverSend).toHaveBeenCalledWith('HlsSessionResult', { + sessionId, + error: 'Failed to create HLS session', + }); + }); + }); + + describe('onSessionEnd', () => { + it('removes the session, kills the transcode, and deletes the dir + DB row', async () => { + await sut.onSessionRequest({ sessionId, assetId, ownerId }); + const process = mockSpawn(0, '', ''); + mocks.process.spawn.mockReturnValue(process); + await sut.onSegmentRequest({ sessionId, assetId, variantIndex: 0, segmentIndex: 0 }); + + await sut.onSessionEnd({ sessionId }); + + expect(process.kill).toHaveBeenCalled(); + expect(mocks.storage.unlinkDir).toHaveBeenCalled(); + expect(mocks.videoStream.deleteSession).toHaveBeenCalledWith(sessionId); + }); + + it('is a no-op when the session is unknown', async () => { + await sut.onSessionEnd({ sessionId: 'never-created' }); + + expect(mocks.videoStream.deleteSession).not.toHaveBeenCalled(); + expect(mocks.storage.unlinkDir).not.toHaveBeenCalled(); + }); + }); + + describe('onHeartbeat', () => { + it('extends the DB lease when remaining time falls below half', async () => { + vi.useFakeTimers(); + try { + await sut.onSessionRequest({ sessionId, assetId, ownerId }); + vi.setSystemTime(Date.now() + HLS_LEASE_DURATION_MS / 2 + 1); + + await sut.onHeartbeat({ sessionId }); + + expect(mocks.videoStream.extendSession).toHaveBeenCalledWith(sessionId, expect.any(Date)); + } finally { + vi.useRealTimers(); + } + }); + + it('does not extend the lease while it is still fresh', async () => { + await sut.onSessionRequest({ sessionId, assetId, ownerId }); + + await sut.onHeartbeat({ sessionId }); + + expect(mocks.videoStream.extendSession).not.toHaveBeenCalled(); + }); + + it('is a no-op when the session is unknown', async () => { + await sut.onHeartbeat({ sessionId: 'never-created' }); + + expect(mocks.videoStream.extendSession).not.toHaveBeenCalled(); + }); + }); + + describe('onSegmentRequest', () => { + beforeEach(async () => { + await sut.onSessionRequest({ sessionId, assetId, ownerId }); + mocks.websocket.serverSend.mockClear(); + }); + + it('spawns FFmpeg on the first request', async () => { + mocks.process.spawn.mockReturnValue(mockSpawn(0, '', '')); + + await sut.onSegmentRequest({ sessionId, assetId, variantIndex: 0, segmentIndex: 0 }); + + expect(mocks.process.spawn).toHaveBeenCalledTimes(1); + expect(mocks.process.spawn).toHaveBeenCalledWith('ffmpeg', expect.any(Array), expect.any(Object)); + }); + + it('kills and respawns when the variant changes', async () => { + const first = mockSpawn(0, '', ''); + const second = mockSpawn(0, '', ''); + mocks.process.spawn.mockReturnValueOnce(first).mockReturnValueOnce(second); + + await sut.onSegmentRequest({ sessionId, assetId, variantIndex: 0, segmentIndex: 0 }); + await sut.onSegmentRequest({ sessionId, assetId, variantIndex: 1, segmentIndex: 0 }); + + expect(first.kill).toHaveBeenCalled(); + expect(mocks.process.spawn).toHaveBeenCalledTimes(2); + }); + + it('kills and respawns when seeking before the start segment', async () => { + const first = mockSpawn(0, '', ''); + const second = mockSpawn(0, '', ''); + mocks.process.spawn.mockReturnValueOnce(first).mockReturnValueOnce(second); + + await sut.onSegmentRequest({ sessionId, assetId, variantIndex: 0, segmentIndex: 5 }); + await sut.onSegmentRequest({ sessionId, assetId, variantIndex: 0, segmentIndex: 2 }); + + expect(first.kill).toHaveBeenCalled(); + expect(mocks.process.spawn).toHaveBeenCalledTimes(2); + }); + + it('kills and respawns when the requested segment is too far ahead', async () => { + const first = mockSpawn(0, '', ''); + const second = mockSpawn(0, '', ''); + mocks.process.spawn.mockReturnValueOnce(first).mockReturnValueOnce(second); + + await sut.onSegmentRequest({ sessionId, assetId, variantIndex: 0, segmentIndex: 0 }); + await sut.onSegmentRequest({ sessionId, assetId, variantIndex: 0, segmentIndex: 5 }); + + expect(first.kill).toHaveBeenCalled(); + expect(mocks.process.spawn).toHaveBeenCalledTimes(2); + }); + + it('does not spawn when the session is unknown', async () => { + await sut.onSegmentRequest({ sessionId: 'never-created', assetId, variantIndex: 0, segmentIndex: 0 }); + + expect(mocks.process.spawn).not.toHaveBeenCalled(); + }); + + it('accepts segments from a restart after the previous ffmpeg exited on its own', async () => { + const first = mockSpawn(0, '', ''); + const second = mockSpawn(0, '', ''); + mocks.process.spawn.mockReturnValueOnce(first).mockReturnValueOnce(second); + + await sut.onSegmentRequest({ sessionId, assetId, variantIndex: 0, segmentIndex: 10 }); + completeSegment(10); + + const onCalls = vi.mocked(first.on).mock.calls as unknown as [string, (code: number) => void][]; + const exitHandler = onCalls.find(([event]) => event === 'exit')?.[1]; + exitHandler?.(0); + + mocks.websocket.serverSend.mockClear(); + await sut.onSegmentRequest({ sessionId, assetId, variantIndex: 0, segmentIndex: 2 }); + completeSegment(2); + + expect(mocks.websocket.serverSend).toHaveBeenCalledWith('HlsSegmentResult', { + sessionId, + variantIndex: 0, + segmentIndex: 2, + }); + }); + }); + + describe('backpressure', () => { + let proc: ReturnType; + + beforeEach(async () => { + proc = mockSpawn(0, '', ''); + mocks.process.spawn.mockReturnValue(proc); + + await sut.onSessionRequest({ sessionId, assetId, ownerId }); + await sut.onSegmentRequest({ sessionId, assetId, variantIndex: 0, segmentIndex: 0 }); + }); + + it('pauses the transcode once the lead exceeds HLS_BACKPRESSURE_PAUSE_SEGMENTS', async () => { + completeSegmentsThrough(0, HLS_BACKPRESSURE_PAUSE_SEGMENTS + 1); + + await sut.onHeartbeat({ sessionId, segmentIndex: 0 }); + + expect(proc.kill).toHaveBeenCalledWith('SIGSTOP'); + }); + + it('does not pause when the lead equals the pause threshold', async () => { + completeSegmentsThrough(0, HLS_BACKPRESSURE_PAUSE_SEGMENTS); + + await sut.onHeartbeat({ sessionId, segmentIndex: 0 }); + + expect(proc.kill).not.toHaveBeenCalled(); + }); + + it('resumes once the lead drops below HLS_BACKPRESSURE_RESUME_SEGMENTS', async () => { + completeSegmentsThrough(0, HLS_BACKPRESSURE_PAUSE_SEGMENTS + 1); + await sut.onHeartbeat({ sessionId, segmentIndex: 0 }); + expect(proc.kill).toHaveBeenCalledWith('SIGSTOP'); + vi.mocked(proc.kill).mockClear(); + + const requested = HLS_BACKPRESSURE_PAUSE_SEGMENTS + 1 - (HLS_BACKPRESSURE_RESUME_SEGMENTS - 1); + await sut.onHeartbeat({ sessionId, segmentIndex: requested }); + + expect(proc.kill).toHaveBeenCalledWith('SIGCONT'); + }); + + it('stays paused while the lead is in the dead-band', async () => { + completeSegmentsThrough(0, HLS_BACKPRESSURE_PAUSE_SEGMENTS + 1); + await sut.onHeartbeat({ sessionId, segmentIndex: 0 }); + vi.mocked(proc.kill).mockClear(); + + const requested = HLS_BACKPRESSURE_PAUSE_SEGMENTS + 1 - HLS_BACKPRESSURE_RESUME_SEGMENTS; + await sut.onHeartbeat({ sessionId, segmentIndex: requested }); + + expect(proc.kill).not.toHaveBeenCalled(); + }); + + it('is a no-op when no segment has completed yet', async () => { + await sut.onHeartbeat({ sessionId, segmentIndex: 0 }); + + expect(proc.kill).not.toHaveBeenCalled(); + }); + + it('is a no-op when the heartbeat omits segmentIndex', async () => { + completeSegmentsThrough(0, HLS_BACKPRESSURE_PAUSE_SEGMENTS + 1); + + await sut.onHeartbeat({ sessionId }); + + expect(proc.kill).not.toHaveBeenCalled(); + }); + + it('resumes the paused transcode when the client requests the next in-range segment', async () => { + completeSegmentsThrough(0, HLS_BACKPRESSURE_PAUSE_SEGMENTS + 1); + await sut.onHeartbeat({ sessionId, segmentIndex: 0 }); + expect(proc.kill).toHaveBeenCalledWith('SIGSTOP'); + vi.mocked(proc.kill).mockClear(); + + await sut.onSegmentRequest({ sessionId, assetId, variantIndex: 0, segmentIndex: 1 }); + + expect(proc.kill).toHaveBeenCalledWith('SIGCONT'); + expect(mocks.process.spawn).toHaveBeenCalledTimes(1); + }); + + it('does not re-pause a freshly spawned transcode after a seek-driven restart', async () => { + const newProc = mockSpawn(0, '', ''); + mocks.process.spawn.mockReturnValueOnce(newProc); + + completeSegmentsThrough(0, HLS_BACKPRESSURE_PAUSE_SEGMENTS + 1); + await sut.onHeartbeat({ sessionId, segmentIndex: 0 }); + expect(proc.kill).toHaveBeenCalledWith('SIGSTOP'); + + await sut.onSegmentRequest({ sessionId, assetId, variantIndex: 1, segmentIndex: 0 }); + vi.mocked(newProc.kill).mockClear(); + + await sut.onHeartbeat({ sessionId, segmentIndex: 0 }); + + expect(newProc.kill).not.toHaveBeenCalled(); + }); + + it('ignores stale segment events from the prior transcode after a backward seek', async () => { + const newProc = mockSpawn(0, '', ''); + mocks.process.spawn.mockReturnValueOnce(newProc); + + const completedAhead = HLS_BACKPRESSURE_PAUSE_SEGMENTS + 5; + completeSegmentsThrough(1, completedAhead); // seg_0 was emitted in beforeEach + + await sut.onSegmentRequest({ sessionId, assetId, variantIndex: 1, segmentIndex: 0 }); + + vi.mocked(newProc.kill).mockClear(); + mocks.websocket.serverSend.mockClear(); + completeSegment(completedAhead + 1); + + expect(mocks.websocket.serverSend).not.toHaveBeenCalledWith( + 'HlsSegmentResult', + expect.objectContaining({ segmentIndex: completedAhead + 1 }), + ); + expect(newProc.kill).not.toHaveBeenCalled(); + + completeSegment(0); + expect(mocks.websocket.serverSend).toHaveBeenCalledWith( + 'HlsSegmentResult', + expect.objectContaining({ segmentIndex: 0 }), + ); + }); + }); + + describe('inactivity sweeper', () => { + it('reaps a session whose last activity exceeds the inactivity timeout', async () => { + vi.useFakeTimers(); + try { + await sut.onSessionRequest({ sessionId, assetId, ownerId }); + mocks.websocket.serverSend.mockClear(); + await vi.advanceTimersByTimeAsync(HLS_INACTIVITY_TIMEOUT_MS + HLS_CLEANUP_INTERVAL_MS); + + expect(mocks.websocket.serverSend).toHaveBeenCalledWith('HlsSessionEnd', { sessionId }); + expect(mocks.videoStream.deleteSession).toHaveBeenCalledWith(sessionId); + } finally { + vi.useRealTimers(); + } + }); + }); + + describe('onShutdown', () => { + it('ends every active session', async () => { + await sut.onSessionRequest({ sessionId: 'session-a', assetId, ownerId }); + await sut.onSessionRequest({ sessionId: 'session-b', assetId, ownerId }); + + await sut.onShutdown(); + + expect(mocks.videoStream.deleteSession).toHaveBeenCalledWith('session-a'); + expect(mocks.videoStream.deleteSession).toHaveBeenCalledWith('session-b'); + }); + }); + + describe('onHlsSessionCleanup', () => { + it('reaps DB-expired sessions under a database lock', async () => { + mocks.database.withLock.mockImplementation(async (_, fn) => fn()); + mocks.videoStream.getExpiredSessions.mockResolvedValue([ + { id: 'expired-1', ownerId: 'user-a' }, + { id: 'expired-2', ownerId: 'user-b' }, + ]); + + await sut.onHlsSessionCleanup(); + + expect(mocks.videoStream.deleteSession).toHaveBeenCalledWith('expired-1'); + expect(mocks.videoStream.deleteSession).toHaveBeenCalledWith('expired-2'); + expect(mocks.storage.unlinkDir).toHaveBeenCalledTimes(2); + }); + }); + + describe('FFmpeg full command', () => { + const baseCommand = [ + '-nostdin', + '-nostats', + '-i', + 'eiffel-tower.mp4', + '-map', + '0:0', + '-map_metadata', + '-1', + '-map', + '0:1', + '-g', + '50', + '-keyint_min', + '50', + '-crf', + '23', + '-copyts', + '-r', + '50130000/2012441', + '-avoid_negative_ts', + 'disabled', + '-f', + 'hls', + '-hls_time', + '2', + '-hls_list_size', + '0', + '-hls_segment_type', + 'fmp4', + '-hls_fmp4_init_filename', + 'init.mp4', + '-hls_segment_options', + 'movflags=+frag_discont', + '-hls_flags', + 'temp_file', + '-start_number', + '0', + ]; + + it.each([ + { + variantIndex: 6, + expected: [ + ...baseCommand, + '-c:v', + 'libsvtav1', + '-c:a', + 'aac', + '-preset', + '12', + '-svtav1-params', + 'hierarchical-levels=3:lookahead=0:enable-tf=0:mbr=4000k', + '-hls_segment_filename', + '/data/encoded-video/user-1/se/ss/session-1/6/seg_%d.m4s', + '/data/encoded-video/user-1/se/ss/session-1/6/playlist.m3u8', + ].sort(), + }, + { + variantIndex: 4, + expected: [ + ...baseCommand, + '-c:v', + 'hevc', + '-c:a', + 'aac', + '-tag:v', + 'hvc1', + '-preset', + 'ultrafast', + '-maxrate', + '2500k', + '-bufsize', + '5000k', + '-x265-params', + 'no-scenecut=1:no-open-gop=1', + '-vf', + 'scale=720:-2', + '-hls_segment_filename', + '/data/encoded-video/user-1/se/ss/session-1/4/seg_%d.m4s', + '/data/encoded-video/user-1/se/ss/session-1/4/playlist.m3u8', + ].sort(), + }, + { + variantIndex: 2, + expected: [ + ...baseCommand, + '-c:v', + 'h264', + '-c:a', + 'aac', + '-preset', + 'ultrafast', + '-maxrate', + '2500k', + '-bufsize', + '5000k', + '-sc_threshold:v', + '0', + '-vf', + 'scale=480:-2', + '-hls_segment_filename', + '/data/encoded-video/user-1/se/ss/session-1/2/seg_%d.m4s', + '/data/encoded-video/user-1/se/ss/session-1/2/playlist.m3u8', + ].sort(), + }, + ])('builds the expected FFmpeg command for $codec (variant $variantIndex)', async ({ variantIndex, expected }) => { + mocks.process.spawn.mockReturnValue(mockSpawn(0, '', '')); + + await sut.onSessionRequest({ sessionId, assetId, ownerId }); + await sut.onSegmentRequest({ sessionId, assetId, variantIndex, segmentIndex: 0 }); + + expect(mocks.process.spawn.mock.calls[0][1].toSorted()).toEqual(expected); + }); + }); + + describe('FFmpeg seek per segment', () => { + const eiffelSeeks = [ + 0, 1.987_15, 3.994_372_222_222_222, 6.001_594_444_444_444, 8.008_816_666_666_666, 10.016_038_888_888_888, + 12.023_261_111_111_111, 14.030_483_333_333_333, 16.037_705_555_555_554, 18.044_927_777_777_776, + 20.052_149_999_999_997, 22.059_372_222_222_223, + ]; + const waterfallSeeks = [ + 0, 1.994_642_826_321_467, 4.006_047_357_065_803, 6.017_451_887_810_139_5, 8.028_856_418_554_476, + 10.040_260_949_298_812, + ]; + const trainSeeks = [ + 0, 1.991_666_666_666_666_7, 3.991_666_666_666_666_7, 5.991_666_666_666_666, 7.991_666_666_666_666, + 9.991_666_666_666_667, 11.991_666_666_666_667, 13.991_666_666_666_667, 15.991_666_666_666_667, + 17.991_666_666_666_667, 19.991_666_666_666_667, + ]; + const cases = [ + ...eiffelSeeks.map((expected, segmentIndex) => ({ + name: `${eiffelTower.originalPath} K=${segmentIndex}`, + fixture: eiffelTower, + segmentIndex, + expected, + })), + ...waterfallSeeks.map((expected, segmentIndex) => ({ + name: `${waterfall.originalPath} K=${segmentIndex}`, + fixture: waterfall, + segmentIndex, + expected, + })), + ...trainSeeks.map((expected, segmentIndex) => ({ + name: `${train.originalPath} K=${segmentIndex}`, + fixture: train, + segmentIndex, + expected, + })), + ]; + + it.each(cases)('$name', async ({ fixture, segmentIndex, expected }) => { + mocks.videoStream.getForTranscoding.mockResolvedValue(fixture); + mocks.process.spawn.mockReturnValue(mockSpawn(0, '', '')); + + await sut.onSessionRequest({ sessionId, assetId, ownerId }); + await sut.onSegmentRequest({ sessionId, assetId, variantIndex: 0, segmentIndex }); + + const args = mocks.process.spawn.mock.calls[0][1] as string[]; + if (expected === 0) { + expect(args).toEqual(expect.arrayContaining(['-copyts', '-avoid_negative_ts', 'disabled'])); + expect(args).not.toContain('-ss'); + } else { + expect(args).toEqual( + expect.arrayContaining(['-ss', String(expected), '-copyts', '-avoid_negative_ts', 'disabled']), + ); + } + }); + }); +}); diff --git a/server/src/services/transcoding.service.ts b/server/src/services/transcoding.service.ts new file mode 100644 index 0000000000..69e2529d63 --- /dev/null +++ b/server/src/services/transcoding.service.ts @@ -0,0 +1,387 @@ +import { Injectable } from '@nestjs/common'; +import { ChildProcess } from 'node:child_process'; +import { join } from 'node:path'; +import { + HLS_BACKPRESSURE_PAUSE_SEGMENTS, + HLS_BACKPRESSURE_RESUME_SEGMENTS, + HLS_CLEANUP_INTERVAL_MS, + HLS_INACTIVITY_TIMEOUT_MS, + HLS_LEASE_DURATION_MS, + HLS_SEGMENT_DURATION, + HLS_SEGMENT_FILENAME_REGEX, + HLS_VARIANTS, +} from 'src/constants'; +import { StorageCore } from 'src/cores/storage.core'; +import { OnEvent, OnJob } from 'src/decorators'; +import { DatabaseLock, ImmichWorker, JobName, QueueName, TranscodeTarget } from 'src/enum'; +import { ArgOf } from 'src/repositories/event.repository'; +import { BaseService } from 'src/services/base.service'; +import { VideoInterfaces } from 'src/types'; +import { isVideoStreamSessionPkConstraint } from 'src/utils/database'; +import { BaseConfig } from 'src/utils/media'; + +type Session = { + assetId: string; + expiresAt: Date; + id: string; + lastActivityTime: Date; + lastClientRequestedSegment: number | null; + lastCompletedSegment: number | null; + ownerId: string; + paused: boolean; + process: ChildProcess | null; + startSegment: number | null; + variantIndex: number | null; +}; + +@Injectable() +export class TranscodingService extends BaseService { + private sessions = new Map(); + private videoInterfaces: VideoInterfaces = { dri: [], mali: false }; + private cleanupInterval: NodeJS.Timeout | null = null; + + @OnEvent({ name: 'AppBootstrap', workers: [ImmichWorker.Microservices] }) + async onBootstrap() { + const [videoInterfaces] = await Promise.all([this.storageCore.getVideoInterfaces(), this.removeExpiredSessions()]); + this.videoInterfaces = videoInterfaces; + } + + @OnEvent({ name: 'AppShutdown', workers: [ImmichWorker.Microservices] }) + onShutdown() { + if (this.cleanupInterval) { + clearInterval(this.cleanupInterval); + this.cleanupInterval = null; + } + return Promise.all([...this.sessions.values()].map(({ id }) => this.onSessionEnd({ sessionId: id }))); + } + + @OnJob({ name: JobName.HlsSessionCleanup, queue: QueueName.BackgroundTask }) + onHlsSessionCleanup() { + return this.removeExpiredSessions(); + } + + @OnEvent({ name: 'HlsSessionRequest', server: true, workers: [ImmichWorker.Microservices] }) + async onSessionRequest({ assetId, sessionId, ownerId }: ArgOf<'HlsSessionRequest'>) { + try { + const expiresAt = new Date(Date.now() + HLS_LEASE_DURATION_MS); + await this.videoStreamRepository.createSession({ id: sessionId, assetId, expiresAt }); + this.sessions.set(sessionId, { + assetId, + expiresAt, + id: sessionId, + lastActivityTime: new Date(), + lastClientRequestedSegment: null, + lastCompletedSegment: null, + ownerId, + paused: false, + process: null, + startSegment: null, + variantIndex: null, + }); + this.cleanupInterval ??= setInterval(() => void this.removeInactiveSessions(), HLS_CLEANUP_INTERVAL_MS); + this.websocketRepository.serverSend('HlsSessionResult', { sessionId }); + } catch (error) { + // If insertion failed due to a PK constraint, another worker has already created a session for this ID. + if (!isVideoStreamSessionPkConstraint(error)) { + this.logger.error(`Failed to create HLS session ${sessionId}: ${error}`); + this.websocketRepository.serverSend('HlsSessionResult', { sessionId, error: 'Failed to create HLS session' }); + } + } + } + + @OnEvent({ name: 'HlsSessionEnd', server: true, workers: [ImmichWorker.Microservices] }) + async onSessionEnd({ sessionId }: ArgOf<'HlsSessionEnd'>) { + const session = this.sessions.get(sessionId); + if (!session) { + return; + } + this.sessions.delete(sessionId); + if (this.cleanupInterval && this.sessions.size === 0) { + clearInterval(this.cleanupInterval); + this.cleanupInterval = null; + } + this.stopTranscode(session); + await this.removeSessionDir(session); + await this.videoStreamRepository.deleteSession(sessionId); + } + + @OnEvent({ name: 'HlsHeartbeat', server: true, workers: [ImmichWorker.Microservices] }) + async onHeartbeat({ sessionId, segmentIndex }: ArgOf<'HlsHeartbeat'>) { + const session = this.sessions.get(sessionId); + if (!session) { + return; + } + + session.lastActivityTime = new Date(); + + if (segmentIndex !== undefined) { + session.lastClientRequestedSegment = segmentIndex; + this.applyBackpressure(session); + } + + const remaining = session.expiresAt.getTime() - Date.now(); + if (remaining < HLS_LEASE_DURATION_MS / 2) { + session.expiresAt = new Date(Date.now() + HLS_LEASE_DURATION_MS); + await this.videoStreamRepository.extendSession(sessionId, session.expiresAt); + } + } + + @OnEvent({ name: 'HlsSegmentRequest', server: true, workers: [ImmichWorker.Microservices] }) + async onSegmentRequest({ sessionId, variantIndex, segmentIndex }: ArgOf<'HlsSegmentRequest'>) { + const session = this.sessions.get(sessionId); + if (!session) { + return; + } + + session.variantIndex ??= variantIndex; + session.startSegment ??= segmentIndex; + const curSegment = session.lastCompletedSegment === null ? session.startSegment : session.lastCompletedSegment + 1; + const needsRestart = + session.variantIndex !== variantIndex || segmentIndex < session.startSegment || segmentIndex > curSegment + 1; + if (needsRestart) { + this.stopTranscode(session); + session.variantIndex = variantIndex; + session.startSegment = segmentIndex; + } else if (session.process) { + this.resumeTranscode(session); + return; + } + + const process = await this.startTranscode(session, variantIndex, segmentIndex); + if (process) { + session.process = process; + } + } + + private applyBackpressure(session: Session) { + if (session.lastCompletedSegment === null || session.lastClientRequestedSegment === null) { + return; + } + const lead = session.lastCompletedSegment - session.lastClientRequestedSegment; + this.logger.debug(`Session ${session.id} lead is ${lead} segments`); + if (!session.paused && lead > HLS_BACKPRESSURE_PAUSE_SEGMENTS) { + this.pauseTranscode(session); + } else if (session.paused && lead < HLS_BACKPRESSURE_RESUME_SEGMENTS) { + this.resumeTranscode(session); + } + } + + private async startTranscode(session: Session, variantIndex: number, startSegment: number) { + const { ffmpeg } = await this.getConfig({ withCache: true }); + + const asset = await this.videoStreamRepository.getForTranscoding(session.assetId); + if (!asset) { + this.logger.error(`Asset ${session.assetId} not found for HLS transcoding`); + return; + } + + if (session.variantIndex !== variantIndex || session.startSegment !== startSegment) { + return; + } + + const variant = HLS_VARIANTS[variantIndex]; + if (!variant) { + this.logger.error(`Variant ${variantIndex} out of range for asset ${session.assetId}`); + await this.failSession(session, `Invalid variant index ${variantIndex}`); + return; + } + + const variantDir = StorageCore.getHlsVariantFolder({ + ownerId: session.ownerId, + sessionId: session.id, + variantIndex, + }); + this.storageRepository.mkdirSync(variantDir); + + // Encoder runs at fps = packetCount × timeBase / totalDuration with + // gop = ceil(SEGMENT_DURATION × fps). To start segment K's content at + // exactly cfr slot K × gop, seek to the midpoint between slots K×gop−1 and + // K×gop. accurate_seek's "discard < target" then keeps the source frame + // that quantizes to slot K×gop and discards the one quantizing to K×gop−1. + const fps = (asset.packets.packetCount * asset.videoStream.timeBase) / asset.packets.totalDuration; + const gop = Math.ceil(HLS_SEGMENT_DURATION * fps); + const seekSeconds = startSegment > 0 ? (startSegment * gop - 0.5) / fps : 0; + + let config; + try { + config = BaseConfig.create( + { + ...ffmpeg, + targetVideoCodec: variant.codec, + targetResolution: String(variant.resolution), + maxBitrate: `${Math.round(variant.bitrate / 1000)}k`, + gopSize: gop, + }, + this.videoInterfaces, + { strictGop: true, lowLatency: true }, + ); + } catch (error: any) { + this.logger.error( + `Failed to create transcode config for variant ${variantIndex} asset ${session.assetId}: ${error?.message ?? error}`, + ); + await this.failSession(session, `Failed to start transcode: ${error?.message ?? 'unknown error'}`); + return; + } + const args = config.getHlsCommand( + { + initFilename: 'init.mp4', + inputPath: asset.originalPath, + packetCount: asset.packets.packetCount, + playlistFilename: join(variantDir, 'playlist.m3u8'), + seekSeconds, + segmentDuration: HLS_SEGMENT_DURATION, + segmentFilename: join(variantDir, 'seg_%d.m4s'), + startSegment, + target: TranscodeTarget.All, + timeBase: asset.videoStream.timeBase, + totalDuration: asset.packets.totalDuration, + }, + asset.videoStream, + asset.audioStream ?? undefined, + ); + this.logger.log( + `Starting HLS transcode for asset ${session.assetId} variant ${variantIndex} with command: ffmpeg ${args.join(' ')}`, + ); + const process = this.processRepository.spawn('ffmpeg', args, { stdio: ['ignore', 'ignore', 'pipe'] }); + this.attachProcessHandlers(process, session, variantIndex); + return process; + } + + private failSession(session: Session, error: string) { + this.websocketRepository.serverSend('HlsSessionResult', { sessionId: session.id, error }); + return this.onSessionEnd({ sessionId: session.id }); + } + + private attachProcessHandlers(process: ChildProcess, session: Session, variantIndex: number) { + let stderr = ''; + const variantDir = StorageCore.getHlsVariantFolder({ + ownerId: session.ownerId, + sessionId: session.id, + variantIndex, + }); + + // hlsenc writes each segment as `seg_K.m4s.tmp` then renames to + // `seg_K.m4s`. The rename event fires the moment the renamed file is + // observable — the only signal we need to tell the API worker the + // segment is ready to serve. + const watcher = this.storageRepository.watchDir(variantDir, (eventType, filename) => { + if (eventType !== 'rename' || !filename || session.process !== process) { + return; + } + const match = HLS_SEGMENT_FILENAME_REGEX.exec(filename); + if (!match) { + return; + } + const segmentIndex = Number.parseInt(match[1]); + const expected = session.lastCompletedSegment === null ? session.startSegment : session.lastCompletedSegment + 1; + // Ignore stale events from old process after seek + if (expected === null || segmentIndex !== expected) { + return; + } + session.lastCompletedSegment = segmentIndex; + this.websocketRepository.serverSend('HlsSegmentResult', { + sessionId: session.id, + variantIndex, + segmentIndex, + }); + this.applyBackpressure(session); + }); + watcher.on('error', (error) => { + this.logger.error(`watcher error for ${variantDir}: ${error}`); + }); + + process.stderr!.on('data', (chunk: Buffer) => { + if (session.process !== process) { + return; + } + stderr += chunk.toString(); + }); + + process.on('exit', (code) => { + watcher.close(); + if (session.process !== process || session.variantIndex !== variantIndex) { + return; + } + session.paused = false; + session.process = null; + session.lastCompletedSegment = null; + if (code) { + this.logger.error( + `FFmpeg exited with code ${code} for variant ${variantIndex} asset ${session.assetId}\n${stderr}`, + ); + void this.failSession(session, `Transcoding process exited unexpectedly with code ${code}`).catch((error) => + this.logger.error(`Failed to end session ${session.id} after ffmpeg exit: ${error}`), + ); + } + }); + } + + private stopTranscode(session: Session) { + if (!session.process) { + return; + } + // SIGTERM makes it rename .tmp segments to .m4s even if they're still incomplete + session.process.kill('SIGKILL'); + session.process = null; + session.lastCompletedSegment = null; + session.paused = false; + this.logger.debug(`Stopped transcoding for session ${session.id}`); + } + + private pauseTranscode(session: Session) { + if (session.paused || !session.process) { + return; + } + session.process.kill('SIGSTOP'); + session.paused = true; + this.logger.debug(`Paused transcoding for session ${session.id}`); + } + + private resumeTranscode(session: Session) { + if (!session.paused || !session.process) { + return; + } + session.process.kill('SIGCONT'); + session.paused = false; + this.logger.debug(`Resumed transcoding for session ${session.id}`); + } + + private async removeSessionDir(session: { ownerId: string; id: string }) { + const dir = StorageCore.getHlsSessionFolder({ ownerId: session.ownerId, sessionId: session.id }); + try { + await this.storageRepository.unlinkDir(dir, { recursive: true, force: true }); + } catch (error) { + if ((error as NodeJS.ErrnoException)?.code !== 'ENOENT') { + throw error; + } + this.logger.warn(`Session dir ${dir} does not exist.`); + } + } + + private removeInactiveSessions() { + const cutoff = Date.now() - HLS_INACTIVITY_TIMEOUT_MS; + const inactiveSessions = [...this.sessions.values()].filter((s) => s.lastActivityTime.getTime() < cutoff); + return Promise.all( + inactiveSessions.map(async (session) => { + try { + this.websocketRepository.serverSend('HlsSessionEnd', { sessionId: session.id }); + await this.onSessionEnd({ sessionId: session.id }); + } catch (error) { + this.logger.error(`Failed to sweep inactive HLS session ${session.id}: ${error}`); + } + }), + ); + } + + private removeExpiredSessions() { + return this.databaseRepository.withLock(DatabaseLock.HlsSessionCleanup, async () => { + const expiredSessions = await this.videoStreamRepository.getExpiredSessions(); + await Promise.all( + expiredSessions.map(async (session) => { + await this.removeSessionDir(session); + await this.videoStreamRepository.deleteSession(session.id); + }), + ); + }); + } +} diff --git a/server/src/services/version.service.spec.ts b/server/src/services/version.service.spec.ts index 2fbe7292fa..d73edb9850 100644 --- a/server/src/services/version.service.spec.ts +++ b/server/src/services/version.service.spec.ts @@ -2,6 +2,7 @@ import { DateTime } from 'luxon'; import { SemVer } from 'semver'; import { defaults } from 'src/config'; import { serverVersion } from 'src/constants'; +import { ReleaseChannel } from 'src/dtos/system-config.dto'; import { CronJob, JobName, JobStatus, SystemMetadataKey } from 'src/enum'; import { VersionService } from 'src/services/version.service'; import { factory } from 'test/small.factory'; @@ -22,6 +23,17 @@ describe(VersionService.name, () => { mocks.cron.update.mockResolvedValue(); }); + beforeAll(() => { + vitest.mock(import('src/constants.js'), async () => ({ + ...(await vitest.importActual('src/constants.js')), + serverVersion: new SemVer('v3.0.0'), + })); + }); + + afterAll(() => { + vitest.unmock(import('src/constants.js')); + }); + it('should work', () => { expect(sut).toBeDefined(); }); @@ -66,9 +78,10 @@ describe(VersionService.name, () => { describe('getVersion', () => { it('should respond the server version', () => { expect(sut.getVersion()).toEqual({ - major: serverVersion.major, - minor: serverVersion.minor, - patch: serverVersion.patch, + major: 3, + minor: 0, + patch: 0, + prerelease: null, }); }); }); @@ -143,24 +156,24 @@ describe(VersionService.name, () => { describe('onConfigUpdate', () => { it('should queue a version check job when newVersionCheck is enabled', async () => { await sut.onConfigUpdate({ - oldConfig: { ...defaults, newVersionCheck: { enabled: false } }, - newConfig: { ...defaults, newVersionCheck: { enabled: true } }, + oldConfig: { ...defaults, newVersionCheck: { enabled: false, channel: ReleaseChannel.Stable } }, + newConfig: { ...defaults, newVersionCheck: { enabled: true, channel: ReleaseChannel.Stable } }, }); expect(mocks.job.queue).toHaveBeenCalledWith({ name: JobName.VersionCheck, data: {} }); }); it('should not queue a version check job when newVersionCheck is disabled', async () => { await sut.onConfigUpdate({ - oldConfig: { ...defaults, newVersionCheck: { enabled: true } }, - newConfig: { ...defaults, newVersionCheck: { enabled: false } }, + oldConfig: { ...defaults, newVersionCheck: { enabled: true, channel: ReleaseChannel.Stable } }, + newConfig: { ...defaults, newVersionCheck: { enabled: false, channel: ReleaseChannel.Stable } }, }); expect(mocks.job.queue).not.toHaveBeenCalled(); }); it('should not queue a version check job when newVersionCheck was already enabled', async () => { await sut.onConfigUpdate({ - oldConfig: { ...defaults, newVersionCheck: { enabled: true } }, - newConfig: { ...defaults, newVersionCheck: { enabled: true } }, + oldConfig: { ...defaults, newVersionCheck: { enabled: true, channel: ReleaseChannel.Stable } }, + newConfig: { ...defaults, newVersionCheck: { enabled: true, channel: ReleaseChannel.Stable } }, }); expect(mocks.job.queue).not.toHaveBeenCalled(); }); @@ -169,21 +182,36 @@ describe(VersionService.name, () => { describe('onWebsocketConnection', () => { it('should send on_server_version client event', async () => { await sut.onWebsocketConnection({ userId: '42' }); - expect(mocks.websocket.clientSend).toHaveBeenCalledWith('on_server_version', '42', expect.any(SemVer)); + expect(mocks.websocket.clientSend).toHaveBeenCalledWith('on_server_version', '42', { + major: 3, + minor: 0, + patch: 0, + prerelease: null, + }); expect(mocks.websocket.clientSend).toHaveBeenCalledTimes(1); }); it('should also send a new release notification', async () => { mocks.systemMetadata.get.mockResolvedValue({ checkedAt: '2024-01-01', releaseVersion: 'v1.42.0' }); await sut.onWebsocketConnection({ userId: '42' }); - expect(mocks.websocket.clientSend).toHaveBeenCalledWith('on_server_version', '42', expect.any(SemVer)); + expect(mocks.websocket.clientSend).toHaveBeenCalledWith('on_server_version', '42', { + major: 3, + minor: 0, + patch: 0, + prerelease: null, + }); expect(mocks.websocket.clientSend).toHaveBeenCalledWith('on_new_release', '42', expect.any(Object)); }); it('should not send a release notification when the version check is disabled', async () => { mocks.systemMetadata.get.mockResolvedValueOnce({ newVersionCheck: { enabled: false } }); await sut.onWebsocketConnection({ userId: '42' }); - expect(mocks.websocket.clientSend).toHaveBeenCalledWith('on_server_version', '42', expect.any(SemVer)); + expect(mocks.websocket.clientSend).toHaveBeenCalledWith('on_server_version', '42', { + major: 3, + minor: 0, + patch: 0, + prerelease: null, + }); expect(mocks.websocket.clientSend).not.toHaveBeenCalledWith('on_new_release', '42', expect.any(Object)); }); }); diff --git a/server/src/services/version.service.ts b/server/src/services/version.service.ts index ce6d6d7a6f..37010db5e7 100644 --- a/server/src/services/version.service.ts +++ b/server/src/services/version.service.ts @@ -3,19 +3,27 @@ import { DateTime } from 'luxon'; import semver, { SemVer } from 'semver'; import { serverVersion } from 'src/constants'; import { OnEvent, OnJob } from 'src/decorators'; -import { ReleaseNotification, ServerVersionResponseDto } from 'src/dtos/server.dto'; +import { ReleaseEventV1, ReleaseType, ServerVersionResponseDto } from 'src/dtos/server.dto'; +import { ReleaseChannel } from 'src/dtos/system-config.dto'; import { CronJob, DatabaseLock, ImmichWorker, JobName, JobStatus, QueueName, SystemMetadataKey } from 'src/enum'; import { ArgOf } from 'src/repositories/event.repository'; import { BaseService } from 'src/services/base.service'; import { VersionCheckMetadata } from 'src/types'; import { handlePromiseError } from 'src/utils/misc'; -const asNotification = ({ checkedAt, releaseVersion }: VersionCheckMetadata): ReleaseNotification => { +const asNotification = ( + channel: ReleaseChannel, + { checkedAt, releaseVersion }: VersionCheckMetadata, +): ReleaseEventV1 => { return { - isAvailable: semver.gt(releaseVersion, serverVersion), + // can't use gt because it's broken for release candidates F https://github.com/npm/node-semver/issues/483 + isAvailable: semver.intersects(`>${serverVersion}`, releaseVersion.toString(), { + includePrerelease: channel === ReleaseChannel.ReleaseCandidate, + }), checkedAt, serverVersion: ServerVersionResponseDto.fromSemVer(serverVersion), releaseVersion: ServerVersionResponseDto.fromSemVer(new SemVer(releaseVersion)), + type: semver.diff(serverVersion, releaseVersion) as ReleaseType, }; }; @@ -98,14 +106,21 @@ export class VersionService extends BaseService { } } - const { version: releaseVersion, published_at: publishedAt } = await this.serverInfoRepository.getLatestRelease(); + const { version: releaseVersion, published_at: publishedAt } = await this.serverInfoRepository.getLatestRelease( + newVersionCheck.channel, + ); const metadata: VersionCheckMetadata = { checkedAt: DateTime.utc().toISO(), releaseVersion }; await this.systemMetadataRepository.set(SystemMetadataKey.VersionCheckState, metadata); - if (semver.gt(releaseVersion, serverVersion)) { + // can't use gt because it's broken for release candidates F https://github.com/npm/node-semver/issues/483 + if ( + semver.intersects(`>${serverVersion}`, releaseVersion.toString(), { + includePrerelease: newVersionCheck.channel === ReleaseChannel.ReleaseCandidate, + }) + ) { this.logger.log(`Found ${releaseVersion}, released at ${new Date(publishedAt).toLocaleString()}`); - this.websocketRepository.clientBroadcast('on_new_release', asNotification(metadata)); + this.websocketRepository.clientBroadcast('on_new_release', asNotification(newVersionCheck.channel, metadata)); } } catch (error: Error | any) { this.logger.warn(`Unable to run version check: ${error}\n${error?.stack}`); @@ -117,7 +132,11 @@ export class VersionService extends BaseService { @OnEvent({ name: 'WebsocketConnect' }) async onWebsocketConnection({ userId }: ArgOf<'WebsocketConnect'>) { - this.websocketRepository.clientSend('on_server_version', userId, serverVersion); + this.websocketRepository.clientSend( + 'on_server_version', + userId, + ServerVersionResponseDto.fromSemVer(serverVersion), + ); const { newVersionCheck } = await this.getConfig({ withCache: true }); if (!newVersionCheck.enabled) { @@ -126,7 +145,7 @@ export class VersionService extends BaseService { const metadata = await this.systemMetadataRepository.get(SystemMetadataKey.VersionCheckState); if (metadata) { - this.websocketRepository.clientSend('on_new_release', userId, asNotification(metadata)); + this.websocketRepository.clientSend('on_new_release', userId, asNotification(newVersionCheck.channel, metadata)); } } } diff --git a/server/src/services/workflow-execution.service.ts b/server/src/services/workflow-execution.service.ts index a1ecf4526d..0a5f025fc1 100644 --- a/server/src/services/workflow-execution.service.ts +++ b/server/src/services/workflow-execution.service.ts @@ -1,10 +1,15 @@ import { CurrentPlugin } from '@extism/extism'; -import { WorkflowChanges, WorkflowEventData, WorkflowEventPayload, WorkflowResponse } from '@immich/plugin-sdk'; +import { + WorkflowChanges, + WorkflowEventData, + WorkflowEventPayload, + WorkflowResponse, + WorkflowTrigger, +} from '@immich/plugin-sdk'; import { HttpException, UnauthorizedException } from '@nestjs/common'; -import _ from 'lodash'; import { join } from 'node:path'; -import { OnEvent, OnJob } from 'src/decorators'; -import { AlbumsAddAssetsDto } from 'src/dtos/album.dto'; +import { DummyValue, OnEvent, OnJob } from 'src/decorators'; +import { AlbumsAddAssetsDto, CreateAlbumDto, GetAlbumsDto } from 'src/dtos/album.dto'; import { BulkIdsDto } from 'src/dtos/asset-ids.response.dto'; import { AuthDto } from 'src/dtos/auth.dto'; import { PluginManifestDto } from 'src/dtos/plugin-manifest.dto'; @@ -16,11 +21,11 @@ import { JobName, JobStatus, QueueName, - WorkflowTrigger, WorkflowType, } from 'src/enum'; import { ArgOf } from 'src/repositories/event.repository'; import { AlbumService } from 'src/services/album.service'; +import { AssetService } from 'src/services/asset.service'; import { BaseService } from 'src/services/base.service'; import { JobOf } from 'src/types'; @@ -32,9 +37,11 @@ const dummy = () => { type ExecuteOptions = { read: (type: T) => Promise<{ authUserId: string; data: WorkflowEventData }>; - write: (changes: WorkflowChanges) => Promise; + write: (auth: AuthDto, changes: WorkflowChanges) => Promise; }; +type AssetTrigger = { userId: string; assetId: string; trigger: WorkflowTrigger }; + export class WorkflowExecutionService extends BaseService { private jwtSecret!: string; @@ -59,21 +66,26 @@ export class WorkflowExecutionService extends BaseService { const albumService = BaseService.create(AlbumService, this); - const albumAddAssets = this.wrap<[id: string, dto: BulkIdsDto]>((authDto, args) => + const searchAlbums = this.wrap<[dto: GetAlbumsDto]>((authDto, args) => albumService.getAll(authDto, ...args)); + const createAlbum = this.wrap<[dto: CreateAlbumDto]>((authDto, args) => albumService.create(authDto, ...args)); + const addAssetsToAlbum = this.wrap<[id: string, dto: BulkIdsDto]>((authDto, args) => albumService.addAssets(authDto, ...args), ); - const addAssetsToAlbums = this.wrap<[dto: AlbumsAddAssetsDto]>((authDto, args) => albumService.addAssetsToAlbums(authDto, ...args), ); const functions = { - albumAddAssets, + searchAlbums, + createAlbum, + addAssetsToAlbum, addAssetsToAlbums, }; - const stubs = { - albumAddAssets: dummy, + const stubs: typeof functions = { + searchAlbums: dummy, + createAlbum: dummy, + addAssetsToAlbum: dummy, addAssetsToAlbums: dummy, }; @@ -247,20 +259,36 @@ export class WorkflowExecutionService extends BaseService { } @OnEvent({ name: 'AssetCreate' }) - async onAssetCreate({ asset }: ArgOf<'AssetCreate'>) { - const dto = { ownerId: asset.ownerId, trigger: WorkflowTrigger.AssetCreate }; - const items = await this.workflowRepository.search(dto); + onAssetCreate({ asset: { ownerId: userId, id: assetId } }: ArgOf<'AssetCreate'>) { + return this.onAssetTrigger({ userId, assetId, trigger: WorkflowTrigger.AssetCreate }); + } + + @OnEvent({ name: 'AssetMetadataExtracted' }) + onAssetMetadataExtracted({ userId, assetId, source }: ArgOf<'AssetMetadataExtracted'>) { + // prevent loops + // TODO loop detection in job service directly + if (source === 'sidecar-write') { + return; + } + + return this.onAssetTrigger({ userId, assetId, trigger: WorkflowTrigger.AssetMetadataExtraction }); + } + + private async onAssetTrigger({ userId, assetId, trigger }: AssetTrigger) { + const items = await this.workflowRepository.search({ userId, trigger }); await this.jobRepository.queueAll( items.map((workflow) => ({ - name: JobName.WorkflowAssetCreate, - data: { workflowId: workflow.id, assetId: asset.id }, + name: JobName.WorkflowAssetTrigger, + data: { workflowId: workflow.id, assetId, trigger }, })), ); } - @OnJob({ name: JobName.WorkflowAssetCreate, queue: QueueName.Workflow }) - handleAssetCreate({ workflowId, assetId }: JobOf) { + @OnJob({ name: JobName.WorkflowAssetTrigger, queue: QueueName.Workflow }) + handleAssetTrigger({ workflowId, assetId }: JobOf) { return this.execute(workflowId, (type) => { + const assetService = BaseService.create(AssetService, this); + switch (type) { case WorkflowType.AssetV1: { return { @@ -271,19 +299,35 @@ export class WorkflowExecutionService extends BaseService { authUserId: asset.ownerId, }; }, - write: async (changes) => { - if (changes.asset) { - await this.assetRepository.update({ - id: assetId, - ..._.omitBy( - { - isFavorite: changes.asset?.isFavorite, - visibility: changes.asset?.visibility, - }, - _.isUndefined, - ), - }); + write: async (auth, changes) => { + const asset = changes.asset; + if (!asset) { + return; } + + await assetService.update(auth, assetId, { + isFavorite: asset.isFavorite, + visibility: asset.visibility, + dateTimeOriginal: asset.exifInfo?.dateTimeOriginal ?? undefined, + // TODO allow setting to null + longitude: asset.exifInfo?.longitude ?? undefined, + // TODO allow setting to null + latitude: asset.exifInfo?.latitude ?? undefined, + // TODO allow setting to null + description: asset.exifInfo?.description ?? undefined, + rating: asset.exifInfo?.rating, + + // TODO add to update dto + // make: asset.exifInfo?.make, + // model: asset.exifInfo?.model, + // city: asset.exifInfo?.city, + // state: asset.exifInfo?.state, + // country: asset.exifInfo?.country, + // lensModel: asset.exifInfo?.lensModel, + // fNumber: asset.exifInfo?.fNumber, + // fps: asset.exifInfo?.fps, + // iso: asset.exifInfo?.iso, + }); }, } satisfies ExecuteOptions; } @@ -301,7 +345,19 @@ export class WorkflowExecutionService extends BaseService { } // TODO infer from steps - const type = 'AssetV1' as T; + let type: T | undefined; + for (const targetType of Object.values(WorkflowType)) { + const missing = workflow.steps.some((step) => !step.types.includes(targetType)); + if (!missing) { + type = targetType as unknown as T; + break; + } + } + + if (!type) { + throw new Error('Unable to infer workflow event type from steps'); + } + const handler = getHandler(type); if (!handler) { this.logger.error(`Misconfigured workflow ${workflowId}: no handler for type ${type}`); @@ -337,10 +393,25 @@ export class WorkflowExecutionService extends BaseService { payload, ); if (result?.changes) { - await write(result.changes); + await write( + { + user: { + id: readResult.authUserId, + }, + session: { + id: DummyValue.UUID, + hasElevatedPermission: true, + }, + } as AuthDto, + result.changes, + ); ({ data } = await read(type)); } + if (result?.config) { + await this.workflowRepository.updateStep(step.id, { config: result.config }); + } + const shouldContinue = result?.workflow?.continue ?? true; if (!shouldContinue) { break; diff --git a/server/src/services/workflow.service.ts b/server/src/services/workflow.service.ts index 0a62a60887..850b4ac086 100644 --- a/server/src/services/workflow.service.ts +++ b/server/src/services/workflow.service.ts @@ -1,4 +1,4 @@ -import { WorkflowStepConfig } from '@immich/plugin-sdk'; +import { WorkflowStepConfig, WorkflowTrigger } from '@immich/plugin-sdk'; import { BadRequestException, Injectable } from '@nestjs/common'; import { AuthDto } from 'src/dtos/auth.dto'; import { @@ -11,7 +11,7 @@ import { WorkflowTriggerResponseDto, WorkflowUpdateDto, } from 'src/dtos/workflow.dto'; -import { Permission, WorkflowTrigger } from 'src/enum'; +import { Permission } from 'src/enum'; import { PluginMethodSearchResponse } from 'src/repositories/plugin.repository'; import { BaseService } from 'src/services/base.service'; import { getWorkflowTriggers, isMethodCompatible, resolveMethod } from 'src/utils/workflow'; @@ -23,7 +23,7 @@ export class WorkflowService extends BaseService { } async search(auth: AuthDto, dto: WorkflowSearchDto): Promise { - const workflows = await this.workflowRepository.search({ ...dto, ownerId: auth.user.id }); + const workflows = await this.workflowRepository.search({ ...dto, userId: auth.user.id }); return workflows.map((workflow) => mapWorkflow(workflow)); } diff --git a/server/src/types.ts b/server/src/types.ts index c7dc1f5e18..4e5a383cca 100644 --- a/server/src/types.ts +++ b/server/src/types.ts @@ -1,3 +1,4 @@ +import { WorkflowTrigger } from '@immich/plugin-sdk'; import { ShallowDehydrateObject } from 'kysely'; import { SystemConfig } from 'src/config'; import { VECTOR_EXTENSIONS } from 'src/constants'; @@ -28,8 +29,6 @@ import { SystemMetadataKey, TranscodeTarget, UserMetadataKey, - VideoCodec, - WorkflowTrigger, WorkflowType, } from 'src/enum'; @@ -162,6 +161,25 @@ export interface TranscodeCommand { }; } +export interface VideoTuning { + strictGop: boolean; + lowLatency: boolean; +} + +export interface HlsCommandOptions { + initFilename: string; + inputPath: string; + packetCount: number; + playlistFilename: string; + seekSeconds?: number; + segmentDuration: number; + segmentFilename: string; + startSegment: number; + target: TranscodeTarget; + timeBase: number; + totalDuration: number; +} + export interface BitrateDistribution { max: number; target: number; @@ -177,14 +195,11 @@ export interface ImageBuffer { export interface VideoCodecSWConfig { getCommand( target: TranscodeTarget, - videoStream: VideoStreamInfo, - audioStream?: AudioStreamInfo, + video: VideoStreamInfo, + audio?: AudioStreamInfo, format?: VideoFormat, ): TranscodeCommand; -} - -export interface VideoCodecHWConfig extends VideoCodecSWConfig { - getSupportedCodecs(): Array; + getHlsCommand(options: HlsCommandOptions, video: VideoStreamInfo, audio?: AudioStreamInfo): string[]; } export interface ProbeOptions { @@ -371,6 +386,7 @@ export type JobItem = // Cleanup | { name: JobName.SessionCleanup; data?: IBaseJob } + | { name: JobName.HlsSessionCleanup; data?: IBaseJob } // Tags | { name: JobName.TagCleanup; data?: IBaseJob } @@ -404,7 +420,7 @@ export type JobItem = | { name: JobName.Ocr; data: IEntityJob } // Workflow - | { name: JobName.WorkflowAssetCreate; data: { workflowId: string; assetId: string } } + | { name: JobName.WorkflowAssetTrigger; data: { workflowId: string; assetId: string } } // Editor | { name: JobName.AssetEditThumbnailGeneration; data: IEntityJob }; @@ -523,6 +539,7 @@ export type UserPreferences = { people: { enabled: boolean; sidebarWeb: boolean; + minimumFaces: number; }; ratings: { enabled: boolean; diff --git a/server/src/utils/database.ts b/server/src/utils/database.ts index fbf32c0ac2..cb942b5366 100644 --- a/server/src/utils/database.ts +++ b/server/src/utils/database.ts @@ -71,10 +71,13 @@ export const removeUndefinedKeys = (update: T, template: unkno }; export const ASSET_CHECKSUM_CONSTRAINT = 'UQ_assets_owner_checksum'; +export const VIDEO_STREAM_SESSION_PK_CONSTRAINT = 'video_stream_session_pkey'; -export const isAssetChecksumConstraint = (error: unknown) => { - return (error as PostgresError)?.constraint_name === 'UQ_assets_owner_checksum'; -}; +export const isAssetChecksumConstraint = (error: unknown) => + (error as PostgresError)?.constraint_name === ASSET_CHECKSUM_CONSTRAINT; + +export const isVideoStreamSessionPkConstraint = (error: unknown) => + (error as PostgresError)?.constraint_name === VIDEO_STREAM_SESSION_PK_CONSTRAINT; 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/event.ts b/server/src/utils/event.ts new file mode 100644 index 0000000000..fd791620de --- /dev/null +++ b/server/src/utils/event.ts @@ -0,0 +1,50 @@ +import { ArgOf, EmitEvent } from 'src/repositories/event.repository'; + +export class PendingEvents extends { error?: string } ? T : never }[EmitEvent]> { + private pending = new Map>[]; timeout: NodeJS.Timeout }>(); + private timeoutMs: number; + + constructor({ timeoutMs }: { timeoutMs: number }) { + this.timeoutMs = timeoutMs; + } + + wait(key: string): Promise> { + const completer = Promise.withResolvers>(); + const existing = this.pending.get(key); + if (existing) { + existing.completers.push(completer); + return completer.promise; + } + + const timeout = setTimeout(() => this.complete(key, { error: 'Request timed out' }), this.timeoutMs); + this.pending.set(key, { completers: [completer], timeout }); + return completer.promise; + } + + complete(key: string, value: ArgOf | { error: string }) { + const pending = this.pending.get(key); + if (!pending) { + return; + } + clearTimeout(pending.timeout); + this.pending.delete(key); + if ('error' in value) { + const error = new Error(value.error); + for (const completer of pending.completers) { + completer.reject(error); + } + } else { + for (const completer of pending.completers) { + completer.resolve(value); + } + } + } + + rejectByPrefix(prefix: string, error: string) { + for (const key of this.pending.keys()) { + if (key.startsWith(prefix)) { + this.complete(key, { error }); + } + } + } +} diff --git a/server/src/utils/media.ts b/server/src/utils/media.ts index 49e11edab7..b3a617c36e 100644 --- a/server/src/utils/media.ts +++ b/server/src/utils/media.ts @@ -1,4 +1,4 @@ -import { AUDIO_ENCODER } from 'src/constants'; +import { AUDIO_ENCODER, SUPPORTED_HWA_CODECS } from 'src/constants'; import { SystemConfigFFmpegDto } from 'src/dtos/system-config.dto'; import { ColorMatrix, @@ -13,38 +13,56 @@ import { import { AudioStreamInfo, BitrateDistribution, + HlsCommandOptions, TranscodeCommand, - VideoCodecHWConfig, VideoCodecSWConfig, VideoFormat, VideoInterfaces, VideoStreamInfo, + VideoTuning, } from 'src/types'; +export const isVideoRotated = (videoStream: VideoStreamInfo): boolean => Math.abs(videoStream.rotation) === 90; + +export const isVideoVertical = (videoStream: VideoStreamInfo): boolean => + videoStream.height > videoStream.width || isVideoRotated(videoStream); + +export const getOutputSize = (videoStream: VideoStreamInfo, targetRes: number) => { + const factor = Math.max(videoStream.height, videoStream.width) / Math.min(videoStream.height, videoStream.width); + let larger = Math.round(targetRes * factor); + if (larger % 2 !== 0) { + larger -= 1; + } + return isVideoVertical(videoStream) ? { width: targetRes, height: larger } : { width: larger, height: targetRes }; +}; + export class BaseConfig implements VideoCodecSWConfig { readonly presets = ['veryslow', 'slower', 'slow', 'medium', 'fast', 'faster', 'veryfast', 'superfast', 'ultrafast']; - protected constructor(protected config: SystemConfigFFmpegDto) {} + protected constructor( + protected config: SystemConfigFFmpegDto, + protected tune: VideoTuning = { strictGop: false, lowLatency: false }, + ) {} - static create(config: SystemConfigFFmpegDto, interfaces: VideoInterfaces): VideoCodecSWConfig { + static create(config: SystemConfigFFmpegDto, interfaces: VideoInterfaces, tune?: VideoTuning) { if (config.accel === TranscodeHardwareAcceleration.Disabled) { - return this.getSWCodecConfig(config); + return this.getSWCodecConfig(config, tune); } - return this.getHWCodecConfig(config, interfaces); + return this.getHWCodecConfig(config, interfaces, tune); } - private static getSWCodecConfig(config: SystemConfigFFmpegDto) { + private static getSWCodecConfig(config: SystemConfigFFmpegDto, tune?: VideoTuning): VideoCodecSWConfig { switch (config.targetVideoCodec) { case VideoCodec.H264: { - return new H264Config(config); + return new H264Config(config, tune); } case VideoCodec.Hevc: { - return new HEVCConfig(config); + return new HEVCConfig(config, tune); } case VideoCodec.Vp9: { - return new VP9Config(config); + return new VP9Config(config, tune); } case VideoCodec.Av1: { - return new AV1Config(config); + return new AV1Config(config, tune); } default: { throw new Error(`Codec '${config.targetVideoCodec}' is unsupported`); @@ -52,72 +70,122 @@ export class BaseConfig implements VideoCodecSWConfig { } } - private static getHWCodecConfig(config: SystemConfigFFmpegDto, interfaces: VideoInterfaces) { - let handler: VideoCodecHWConfig; + private static getHWCodecConfig(config: SystemConfigFFmpegDto, interfaces: VideoInterfaces, tune?: VideoTuning) { + if (!SUPPORTED_HWA_CODECS[config.accel].includes(config.targetVideoCodec)) { + throw new Error( + `${config.accel.toUpperCase()} acceleration does not support codec '${config.targetVideoCodec.toUpperCase()}'. Supported codecs: ${SUPPORTED_HWA_CODECS[config.accel]}`, + ); + } + + let handler: VideoCodecSWConfig; switch (config.accel) { case TranscodeHardwareAcceleration.Nvenc: { handler = config.accelDecode - ? new NvencHwDecodeConfig(config, interfaces) - : new NvencSwDecodeConfig(config, interfaces); + ? new NvencHwDecodeConfig(config, interfaces, tune) + : new NvencSwDecodeConfig(config, interfaces, tune); break; } case TranscodeHardwareAcceleration.Qsv: { handler = config.accelDecode - ? new QsvHwDecodeConfig(config, interfaces) - : new QsvSwDecodeConfig(config, interfaces); + ? new QsvHwDecodeConfig(config, interfaces, tune) + : new QsvSwDecodeConfig(config, interfaces, tune); break; } case TranscodeHardwareAcceleration.Vaapi: { handler = config.accelDecode - ? new VaapiHwDecodeConfig(config, interfaces) - : new VaapiSwDecodeConfig(config, interfaces); + ? new VaapiHwDecodeConfig(config, interfaces, tune) + : new VaapiSwDecodeConfig(config, interfaces, tune); break; } case TranscodeHardwareAcceleration.Rkmpp: { handler = config.accelDecode - ? new RkmppHwDecodeConfig(config, interfaces) - : new RkmppSwDecodeConfig(config, interfaces); + ? new RkmppHwDecodeConfig(config, interfaces, tune) + : new RkmppSwDecodeConfig(config, interfaces, tune); break; } default: { throw new Error(`${config.accel.toUpperCase()} acceleration is unsupported`); } } - if (!handler.getSupportedCodecs().includes(config.targetVideoCodec)) { - throw new Error( - `${config.accel.toUpperCase()} acceleration does not support codec '${config.targetVideoCodec.toUpperCase()}'. Supported codecs: ${handler.getSupportedCodecs()}`, - ); - } return handler; } - getCommand( - target: TranscodeTarget, - videoStream: VideoStreamInfo, - audioStream?: AudioStreamInfo, - format?: VideoFormat, - ) { + getCommand(target: TranscodeTarget, video: VideoStreamInfo, audio?: AudioStreamInfo, format?: VideoFormat) { const options = { - inputOptions: this.getBaseInputOptions(videoStream, format), - outputOptions: [...this.getBaseOutputOptions(target, videoStream, audioStream), '-v', 'verbose'], + inputOptions: this.getBaseInputOptions(video, format), + outputOptions: [ + ...this.getBaseOutputOptions(target, video, audio), + ...this.getPresetOptions(), + ...this.getBitrateOptions(), + ...this.getEncoderOptions(), + '-movflags', + 'faststart', + '-fps_mode', + 'passthrough', + '-v', + 'verbose', + ], twoPass: this.eligibleForTwoPass(), - progress: { frameCount: videoStream.frameCount, percentInterval: 5 }, + progress: { frameCount: video.frameCount, percentInterval: 5 }, } as TranscodeCommand; if ([TranscodeTarget.All, TranscodeTarget.Video].includes(target)) { - const filters = this.getFilterOptions(videoStream); + const filters = this.getFilterOptions(video); if (filters.length > 0) { options.outputOptions.push('-vf', filters.join(',')); } } - options.outputOptions.push( + return options; + } + + getHlsCommand(options: HlsCommandOptions, video: VideoStreamInfo, audio?: AudioStreamInfo) { + const args: string[] = this.getBaseInputOptions(video); + if (options.seekSeconds) { + args.push('-ss', String(options.seekSeconds)); + } + args.push( + '-nostdin', + '-nostats', + '-i', + options.inputPath, + ...this.getBaseOutputOptions(options.target, video, audio), ...this.getPresetOptions(), - ...this.getOutputThreadOptions(), ...this.getBitrateOptions(), + ...this.getEncoderOptions(), + '-copyts', + '-r', + `${options.packetCount * options.timeBase}/${options.totalDuration}`, + '-avoid_negative_ts', + 'disabled', + '-f', + 'hls', + '-hls_time', + String(options.segmentDuration), + '-hls_list_size', + '0', + '-hls_segment_type', + 'fmp4', + '-hls_fmp4_init_filename', + options.initFilename, + '-hls_segment_options', + 'movflags=+frag_discont', + '-hls_flags', + 'temp_file', + '-hls_segment_filename', + options.segmentFilename, + '-start_number', + String(options.startSegment), ); - return options; + if ([TranscodeTarget.All, TranscodeTarget.Video].includes(options.target)) { + const filters = this.getFilterOptions(video); + if (filters.length > 0) { + args.push('-vf', filters.join(',')); + } + } + args.push(options.playlistFilename); + return args; } // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -129,23 +197,7 @@ export class BaseConfig implements VideoCodecSWConfig { const videoCodec = [TranscodeTarget.All, TranscodeTarget.Video].includes(target) ? this.getVideoCodec() : 'copy'; const audioCodec = [TranscodeTarget.All, TranscodeTarget.Audio].includes(target) ? this.getAudioEncoder() : 'copy'; - const options = [ - '-c:v', - videoCodec, - '-c:a', - audioCodec, - // Makes a second pass moving the moov atom to the - // beginning of the file for improved playback speed. - '-movflags', - 'faststart', - '-fps_mode', - 'passthrough', - '-map', - `0:${videoStream.index}`, - '-map_metadata', - '-1', - ]; - + const options = ['-c:v', videoCodec, '-c:a', audioCodec, '-map', `0:${videoStream.index}`, '-map_metadata', '-1']; if (audioStream) { options.push('-map', `0:${audioStream.index}`); } @@ -157,18 +209,22 @@ export class BaseConfig implements VideoCodecSWConfig { } if (this.getGopSize() > 0) { options.push('-g', `${this.getGopSize()}`); + if (this.tune.strictGop) { + options.push('-keyint_min', `${this.getGopSize()}`); + } } - - if ( - this.config.targetVideoCodec === VideoCodec.Hevc && - (videoCodec !== 'copy' || videoStream.codecName === 'hevc') - ) { + const isHvc = (videoCodec === 'copy' ? videoStream.codecName : videoCodec) === VideoCodec.Hevc; + if (isHvc) { options.push('-tag:v', 'hvc1'); } return options; } + getEncoderOptions(): string[] { + return []; + } + getFilterOptions(videoStream: VideoStreamInfo) { const options = []; if (this.shouldScale(videoStream)) { @@ -272,25 +328,7 @@ export class BaseConfig implements VideoCodecSWConfig { getScaling(videoStream: VideoStreamInfo, mult = 2) { const targetResolution = this.getTargetResolution(videoStream); - return this.isVideoVertical(videoStream) ? `${targetResolution}:-${mult}` : `-${mult}:${targetResolution}`; - } - - getSize(videoStream: VideoStreamInfo) { - const smaller = this.getTargetResolution(videoStream); - const factor = Math.max(videoStream.height, videoStream.width) / Math.min(videoStream.height, videoStream.width); - let larger = Math.round(smaller * factor); - if (larger % 2 !== 0) { - larger -= 1; - } - return this.isVideoVertical(videoStream) ? { width: smaller, height: larger } : { width: larger, height: smaller }; - } - - isVideoRotated(videoStream: VideoStreamInfo) { - return Math.abs(videoStream.rotation) === 90; - } - - isVideoVertical(videoStream: VideoStreamInfo) { - return videoStream.height > videoStream.width || this.isVideoRotated(videoStream); + return isVideoVertical(videoStream) ? `${targetResolution}:-${mult}` : `-${mult}:${targetResolution}`; } isBitrateConstrained() { @@ -353,23 +391,18 @@ export class BaseConfig implements VideoCodecSWConfig { } } -export class BaseHWConfig extends BaseConfig implements VideoCodecHWConfig { +export class BaseHWConfig extends BaseConfig { protected device: string; - protected interfaces: VideoInterfaces; constructor( protected config: SystemConfigFFmpegDto, - interfaces: VideoInterfaces, + protected interfaces: VideoInterfaces, + tune?: VideoTuning, ) { - super(config); - this.interfaces = interfaces; + super(config, tune); this.device = this.getDevice(interfaces); } - getSupportedCodecs() { - return [VideoCodec.H264, VideoCodec.Hevc]; - } - validateDevices(devices: string[]) { if (devices.length === 0) { throw new Error('No /dev/dri devices found. If using Docker, make sure at least one /dev/dri device is mounted'); @@ -474,24 +507,32 @@ export class ThumbnailConfig extends BaseConfig { } export class H264Config extends BaseConfig { - getOutputThreadOptions() { - const options = super.getOutputThreadOptions(); - if (this.config.threads === 1) { - options.push('-x264-params', 'frame-threads=1:pools=none'); + getEncoderOptions(): string[] { + const out = this.getOutputThreadOptions(); + if (this.tune.strictGop) { + out.push('-sc_threshold:v', '0'); } - - return options; + if (this.config.threads === 1) { + out.push('-x264-params', 'frame-threads=1:pools=none'); + } + return out; } } export class HEVCConfig extends BaseConfig { - getOutputThreadOptions() { - const options = super.getOutputThreadOptions(); - if (this.config.threads === 1) { - options.push('-x265-params', 'frame-threads=1:pools=none'); + getEncoderOptions(): string[] { + const out: string[] = this.getOutputThreadOptions(); + const params: string[] = []; + if (this.tune.strictGop) { + params.push('no-scenecut=1', 'no-open-gop=1'); } - - return options; + if (this.config.threads === 1) { + params.push('frame-threads=1', 'pools=none'); + } + if (params.length > 0) { + out.push('-x265-params', params.join(':')); + } + return out; } } @@ -520,8 +561,8 @@ export class VP9Config extends BaseConfig { return [`-${this.useCQP() ? 'q:v' : 'crf'}`, `${this.config.crf}`, '-b:v', `${bitrates.max}${bitrates.unit}`]; } - getOutputThreadOptions() { - return ['-row-mt', '1', ...super.getOutputThreadOptions()]; + getEncoderOptions(): string[] { + return ['-row-mt', '1', ...this.getOutputThreadOptions()]; } eligibleForTwoPass() { @@ -543,23 +584,22 @@ export class AV1Config extends BaseConfig { } getBitrateOptions() { - const options = ['-crf', `${this.config.crf}`]; - const bitrates = this.getBitrateDistribution(); - const svtparams = []; - if (this.config.threads > 0) { - svtparams.push(`lp=${this.config.threads}`); - } - if (bitrates.max > 0) { - svtparams.push(`mbr=${bitrates.max}${bitrates.unit}`); - } - if (svtparams.length > 0) { - options.push('-svtav1-params', svtparams.join(':')); - } - return options; + return ['-crf', `${this.config.crf}`]; } - getOutputThreadOptions() { - return []; // Already set above with svtav1-params + getEncoderOptions(): string[] { + const params: string[] = []; + if (this.tune.lowLatency) { + params.push('hierarchical-levels=3', 'lookahead=0', 'enable-tf=0'); + } + if (this.config.threads > 0) { + params.push(`lp=${this.config.threads}`); + } + const bitrates = this.getBitrateDistribution(); + if (bitrates.max > 0) { + params.push(`mbr=${bitrates.max}${bitrates.unit}`); + } + return params.length > 0 ? ['-svtav1-params', params.join(':')] : []; } eligibleForTwoPass() { @@ -572,10 +612,6 @@ export class NvencSwDecodeConfig extends BaseHWConfig { return '0'; } - getSupportedCodecs() { - return [VideoCodec.H264, VideoCodec.Hevc, VideoCodec.Av1]; - } - getBaseInputOptions() { return ['-init_hw_device', `cuda=cuda:${this.device}`, '-filter_hw_device', 'cuda']; } @@ -652,6 +688,14 @@ export class NvencSwDecodeConfig extends BaseHWConfig { return []; } + getEncoderOptions(): string[] { + const out = this.getOutputThreadOptions(); + if (this.tune.strictGop) { + out.push('-forced-idr', '1'); + } + return out; + } + getRefs() { const bframes = this.getBFrames(); if (bframes > 0 && bframes < 3 && this.config.refs < 3) { @@ -703,8 +747,8 @@ export class NvencHwDecodeConfig extends NvencSwDecodeConfig { return ['-threads', '1']; } - getOutputThreadOptions() { - return []; + getEncoderOptions(): string[] { + return this.tune.strictGop ? ['-forced-idr', '1'] : []; } } @@ -749,10 +793,6 @@ export class QsvSwDecodeConfig extends BaseHWConfig { return options; } - getSupportedCodecs() { - return [VideoCodec.H264, VideoCodec.Hevc, VideoCodec.Vp9, VideoCodec.Av1]; - } - // recommended from https://github.com/intel/media-delivery/blob/master/doc/benchmarks/intel-iris-xe-max-graphics/intel-iris-xe-max-graphics.md getBFrames() { if (this.config.bframes < 0) { @@ -775,6 +815,14 @@ export class QsvSwDecodeConfig extends BaseHWConfig { getScaling(videoStream: VideoStreamInfo): string { return super.getScaling(videoStream, 1); } + + getEncoderOptions(): string[] { + const out = this.getOutputThreadOptions(); + if (this.tune.strictGop) { + out.push('-idr_interval', '0'); + } + return out; + } } export class QsvHwDecodeConfig extends QsvSwDecodeConfig { @@ -888,13 +936,17 @@ export class VaapiSwDecodeConfig extends BaseHWConfig { return options; } - getSupportedCodecs() { - return [VideoCodec.H264, VideoCodec.Hevc, VideoCodec.Vp9, VideoCodec.Av1]; - } - useCQP() { return this.config.cqMode !== CQMode.Icq || this.config.targetVideoCodec === VideoCodec.Vp9; } + + getEncoderOptions(): string[] { + const out = this.getOutputThreadOptions(); + if (this.tune.strictGop) { + out.push('-idr_interval', '0'); + } + return out; + } } export class VaapiHwDecodeConfig extends VaapiSwDecodeConfig { @@ -988,10 +1040,6 @@ export class RkmppSwDecodeConfig extends BaseHWConfig { return ['-rc_mode', 'CQP', '-qp_init', `${this.config.crf}`]; } - getSupportedCodecs() { - return [VideoCodec.H264, VideoCodec.Hevc]; - } - getVideoCodec(): string { return `${this.config.targetVideoCodec}_rkmpp`; } diff --git a/server/src/utils/misc.ts b/server/src/utils/misc.ts index 37fff07fd9..efcb509941 100644 --- a/server/src/utils/misc.ts +++ b/server/src/utils/misc.ts @@ -15,7 +15,7 @@ import picomatch from 'picomatch'; import parse from 'picomatch/lib/parse'; import { SystemConfig } from 'src/config'; import { CLIP_MODEL_INFO, endpointTags, serverVersion } from 'src/constants'; -import { extraSyncModels } from 'src/dtos/sync.dto'; +import { extraModels } from 'src/decorators'; import { ApiCustomExtension, ImmichCookie, ImmichHeader, MetadataKey } from 'src/enum'; import { LoggingRepository } from 'src/repositories/logging.repository'; @@ -289,7 +289,7 @@ export const useSwagger = (app: INestApplication, { write }: { write: boolean }) const options: SwaggerDocumentOptions = { operationIdFactory: (controllerKey: string, methodKey: string) => methodKey, - extraModels: extraSyncModels, + extraModels, ignoreGlobalPrefix: true, }; diff --git a/server/src/utils/preferences.ts b/server/src/utils/preferences.ts index b25369670a..6b67398d23 100644 --- a/server/src/utils/preferences.ts +++ b/server/src/utils/preferences.ts @@ -21,6 +21,7 @@ const getDefaultPreferences = (): UserPreferences => { people: { enabled: true, sidebarWeb: false, + minimumFaces: 3, }, sharedLinks: { enabled: true, diff --git a/server/src/utils/workflow.spec.ts b/server/src/utils/workflow.spec.ts index 86bdd94e5b..5defe92d90 100644 --- a/server/src/utils/workflow.spec.ts +++ b/server/src/utils/workflow.spec.ts @@ -1,4 +1,5 @@ -import { WorkflowTrigger, WorkflowType } from 'src/enum'; +import { WorkflowTrigger } from '@immich/plugin-sdk'; +import { WorkflowType } from 'src/enum'; import { isMethodCompatible } from 'src/utils/workflow'; const tests: Array<{ trigger: WorkflowTrigger; types: WorkflowType[]; expected: boolean }> = [ diff --git a/server/src/utils/workflow.ts b/server/src/utils/workflow.ts index 879fe4c608..5383db818e 100644 --- a/server/src/utils/workflow.ts +++ b/server/src/utils/workflow.ts @@ -1,9 +1,11 @@ -import { WorkflowTrigger, WorkflowType } from 'src/enum'; +import { WorkflowTrigger } from '@immich/plugin-sdk'; +import { WorkflowType } from 'src/enum'; import { PluginMethodSearchResponse } from 'src/repositories/plugin.repository'; export const triggerMap: Record = { [WorkflowTrigger.AssetCreate]: [WorkflowType.AssetV1], [WorkflowTrigger.PersonRecognized]: [WorkflowType.AssetPersonV1], + [WorkflowTrigger.AssetMetadataExtraction]: [WorkflowType.AssetV1], }; export const getWorkflowTriggers = () => diff --git a/server/test/fixtures/media.stub.ts b/server/test/fixtures/media.stub.ts index f034ab873d..7edd1c2dee 100644 --- a/server/test/fixtures/media.stub.ts +++ b/server/test/fixtures/media.stub.ts @@ -597,7 +597,7 @@ export const train = { packets: { totalDuration: 12_290, packetCount: 1229, - outputFrames: 1303, + outputFrames: 1304, keyframePts: [ 0, 601, 1201, 1802, 2402, 3003, 3604, 4204, 4805, 5405, 6006, 6607, 7207, 7808, 8408, 9009, 9609, 10_210, 10_811, 11_411, 12_062, 12_703, diff --git a/server/test/medium/specs/services/workflow.service.spec.ts b/server/test/medium/specs/services/workflow.service.spec.ts index 07301581cb..b4b52be98c 100644 --- a/server/test/medium/specs/services/workflow.service.spec.ts +++ b/server/test/medium/specs/services/workflow.service.spec.ts @@ -1,5 +1,6 @@ +import { WorkflowTrigger } from '@immich/plugin-sdk'; import { Kysely } from 'kysely'; -import { WorkflowTrigger, WorkflowType } from 'src/enum'; +import { WorkflowType } from 'src/enum'; import { AccessRepository } from 'src/repositories/access.repository'; import { LoggingRepository } from 'src/repositories/logging.repository'; import { PluginRepository } from 'src/repositories/plugin.repository'; diff --git a/server/test/medium/specs/workflow/workflow-core-plugin.spec.ts b/server/test/medium/specs/workflow/workflow-core-plugin.spec.ts index 99f6c67d5c..2bb9de6af1 100644 --- a/server/test/medium/specs/workflow/workflow-core-plugin.spec.ts +++ b/server/test/medium/specs/workflow/workflow-core-plugin.spec.ts @@ -1,8 +1,8 @@ -import { WorkflowStepConfig } from '@immich/plugin-sdk'; +import { WorkflowStepConfig, WorkflowTrigger } from '@immich/plugin-sdk'; import { Kysely } from 'kysely'; import { readFileSync } from 'node:fs'; import { PluginManifestDto } from 'src/dtos/plugin-manifest.dto'; -import { AssetVisibility, LogLevel, WorkflowTrigger } from 'src/enum'; +import { AssetVisibility, LogLevel } from 'src/enum'; import { AccessRepository } from 'src/repositories/access.repository'; import { AlbumRepository } from 'src/repositories/album.repository'; import { AssetRepository } from 'src/repositories/asset.repository'; @@ -12,6 +12,7 @@ import { DatabaseRepository } from 'src/repositories/database.repository'; import { LoggingRepository } from 'src/repositories/logging.repository'; import { PluginRepository } from 'src/repositories/plugin.repository'; import { StorageRepository } from 'src/repositories/storage.repository'; +import { UserRepository } from 'src/repositories/user.repository'; import { WorkflowRepository } from 'src/repositories/workflow.repository'; import { DB } from 'src/schema'; import { WorkflowExecutionService } from 'src/services/workflow-execution.service'; @@ -33,8 +34,9 @@ class WorkflowTestContext extends MediumTestContext { CryptoRepository, DatabaseRepository, LoggingRepository, - StorageRepository, PluginRepository, + StorageRepository, + UserRepository, WorkflowRepository, ], mock: [ConfigRepository], @@ -137,7 +139,7 @@ describe('core plugin', () => { steps: [{ method: 'immich-plugin-core#assetArchive' }], }); - await expect(ctx.sut.handleAssetCreate({ workflowId: workflow.id, assetId: asset.id })).resolves.toBeUndefined(); + await expect(ctx.sut.handleAssetTrigger({ workflowId: workflow.id, assetId: asset.id })).resolves.toBeUndefined(); await expect(ctx.get(AssetRepository).getById(asset.id)).resolves.toMatchObject({ visibility: AssetVisibility.Archive, @@ -154,7 +156,7 @@ describe('core plugin', () => { steps: [{ method: 'immich-plugin-core#assetArchive', config: { inverse: true } }], }); - await expect(ctx.sut.handleAssetCreate({ workflowId: workflow.id, assetId: asset.id })).resolves.toBeUndefined(); + await expect(ctx.sut.handleAssetTrigger({ workflowId: workflow.id, assetId: asset.id })).resolves.toBeUndefined(); await expect(ctx.get(AssetRepository).getById(asset.id)).resolves.toMatchObject({ visibility: AssetVisibility.Timeline, @@ -173,7 +175,7 @@ describe('core plugin', () => { steps: [{ method: 'immich-plugin-core#assetLock' }], }); - await expect(ctx.sut.handleAssetCreate({ workflowId: workflow.id, assetId: asset.id })).resolves.toBeUndefined(); + await expect(ctx.sut.handleAssetTrigger({ workflowId: workflow.id, assetId: asset.id })).resolves.toBeUndefined(); await expect(ctx.get(AssetRepository).getById(asset.id)).resolves.toMatchObject({ visibility: AssetVisibility.Locked, @@ -190,7 +192,7 @@ describe('core plugin', () => { steps: [{ method: 'immich-plugin-core#assetLock', config: { inverse: true } }], }); - await expect(ctx.sut.handleAssetCreate({ workflowId: workflow.id, assetId: asset.id })).resolves.toBeUndefined(); + await expect(ctx.sut.handleAssetTrigger({ workflowId: workflow.id, assetId: asset.id })).resolves.toBeUndefined(); await expect(ctx.get(AssetRepository).getById(asset.id)).resolves.toMatchObject({ visibility: AssetVisibility.Timeline, @@ -209,7 +211,7 @@ describe('core plugin', () => { steps: [{ method: 'immich-plugin-core#assetFavorite' }], }); - await expect(ctx.sut.handleAssetCreate({ workflowId: workflow.id, assetId: asset.id })).resolves.toBeUndefined(); + await expect(ctx.sut.handleAssetTrigger({ workflowId: workflow.id, assetId: asset.id })).resolves.toBeUndefined(); await expect(ctx.get(AssetRepository).getById(asset.id)).resolves.toMatchObject({ isFavorite: true }); }); @@ -224,13 +226,59 @@ describe('core plugin', () => { steps: [{ method: 'immich-plugin-core#assetFavorite', config: { inverse: true } }], }); - await expect(ctx.sut.handleAssetCreate({ workflowId: workflow.id, assetId: asset.id })).resolves.toBeUndefined(); + await expect(ctx.sut.handleAssetTrigger({ workflowId: workflow.id, assetId: asset.id })).resolves.toBeUndefined(); await expect(ctx.get(AssetRepository).getById(asset.id)).resolves.toMatchObject({ isFavorite: false }); }); }); describe('assetAddToAlbums', () => { + it('should create an album by name', async () => { + const { user } = await ctx.newUser(); + const { asset } = await ctx.newAsset({ ownerId: user.id, isFavorite: true }); + + const workflow = await createWorkflow({ + ownerId: user.id, + trigger: WorkflowTrigger.AssetCreate, + steps: [{ method: 'immich-plugin-core#assetAddToAlbums', config: { albumIds: [], albumName: 'Screenshots' } }], + }); + + await expect(ctx.sut.handleAssetTrigger({ workflowId: workflow.id, assetId: asset.id })).resolves.toBeUndefined(); + + const albums = await ctx.get(AlbumRepository).getAll(user.id); + expect(albums).toHaveLength(1); + + const album = albums[0]!; + expect(album.albumName).toEqual('Screenshots'); + + const updated = await ctx.get(WorkflowRepository).get(workflow.id); + expect(updated?.steps[0].config).toEqual({ albumIds: [album.id], albumName: 'Screenshots' }); + + await expect(ctx.get(AlbumRepository).getAssetIds(album.id, [asset.id])).resolves.toContain(asset.id); + }); + + it('should not use the name when there is an albumId', async () => { + const { user } = await ctx.newUser(); + const { asset } = await ctx.newAsset({ ownerId: user.id, isFavorite: true }); + const { album } = await ctx.newAlbum({ ownerId: user.id }); + + const workflow = await createWorkflow({ + ownerId: user.id, + trigger: WorkflowTrigger.AssetCreate, + steps: [ + { method: 'immich-plugin-core#assetAddToAlbums', config: { albumIds: [album.id], albumName: 'Screenshots' } }, + ], + }); + + const albums = await ctx.get(AlbumRepository).getAll(user.id); + expect(albums).toHaveLength(1); + expect(albums[0].albumName).toEqual(album.albumName); + + await expect(ctx.sut.handleAssetTrigger({ workflowId: workflow.id, assetId: asset.id })).resolves.toBeUndefined(); + + await expect(ctx.get(AlbumRepository).getAssetIds(album.id, [asset.id])).resolves.toContain(asset.id); + }); + it('should add an asset to an album', async () => { const { user } = await ctx.newUser(); const { asset } = await ctx.newAsset({ ownerId: user.id, isFavorite: true }); @@ -242,7 +290,7 @@ describe('core plugin', () => { steps: [{ method: 'immich-plugin-core#assetAddToAlbums', config: { albumIds: [album.id] } }], }); - await expect(ctx.sut.handleAssetCreate({ workflowId: workflow.id, assetId: asset.id })).resolves.toBeUndefined(); + await expect(ctx.sut.handleAssetTrigger({ workflowId: workflow.id, assetId: asset.id })).resolves.toBeUndefined(); await expect(ctx.get(AlbumRepository).getAssetIds(album.id, [asset.id])).resolves.toContain(asset.id); }); @@ -261,7 +309,7 @@ describe('core plugin', () => { steps: [{ method: 'immich-plugin-core#assetAddToAlbums', config: { albumIds: [album1.id, album2.id] } }], }); - await expect(ctx.sut.handleAssetCreate({ workflowId: workflow.id, assetId: asset.id })).resolves.toBeUndefined(); + await expect(ctx.sut.handleAssetTrigger({ workflowId: workflow.id, assetId: asset.id })).resolves.toBeUndefined(); await expect(ctx.get(AlbumRepository).getAssetIds(album1.id, [asset.id])).resolves.toContain(asset.id); await expect(ctx.get(AlbumRepository).getAssetIds(album2.id, [asset.id])).resolves.toContain(asset.id); @@ -279,7 +327,7 @@ describe('core plugin', () => { steps: [{ method: 'immich-plugin-core#assetAddToAlbums', config: { albumIds: [album.id] } }], }); - await expect(ctx.sut.handleAssetCreate({ workflowId: workflow.id, assetId: asset.id })).resolves.toBeTruthy(); + await expect(ctx.sut.handleAssetTrigger({ workflowId: workflow.id, assetId: asset.id })).resolves.toBeTruthy(); await expect(ctx.get(AlbumRepository).getAssetIds(album.id, [asset.id])).resolves.not.toContain(asset.id); }); diff --git a/server/test/repositories/storage.repository.mock.ts b/server/test/repositories/storage.repository.mock.ts index 334d7d0d53..c1fb7ceaa6 100644 --- a/server/test/repositories/storage.repository.mock.ts +++ b/server/test/repositories/storage.repository.mock.ts @@ -75,5 +75,6 @@ export const newStorageRepositoryMock = (): Mocked ({ close: vitest.fn(), on: vitest.fn() })), }; }; diff --git a/server/test/utils.ts b/server/test/utils.ts index 75ada7b551..e707971aad 100644 --- a/server/test/utils.ts +++ b/server/test/utils.ts @@ -181,7 +181,11 @@ export const automock = ( const mocks: Mock[] = []; const instance = new Dependency(...args); - for (const property of Object.getOwnPropertyNames(Dependency.prototype)) { + const propertyNames = new Set([ + ...Object.getOwnPropertyNames(Dependency.prototype), + ...Object.getOwnPropertyNames(instance), + ]); + for (const property of propertyNames) { if (property === 'constructor') { continue; } @@ -346,7 +350,7 @@ export const getMocks = () => { trash: automock(TrashRepository), user: automock(UserRepository, { strict: false }), versionHistory: automock(VersionHistoryRepository), - videoStream: automock(VideoStreamRepository), + videoStream: automock(VideoStreamRepository, { strict: false }), view: automock(ViewRepository), // eslint-disable-next-line no-sparse-arrays websocket: automock(WebsocketRepository, { args: [, loggerMock], strict: false }), @@ -500,6 +504,7 @@ export const mockSpawn = vitest.fn((exitCode: number, stdout: string, stderr: st callback(exitCode); } }), + kill: vitest.fn(), } as unknown as ChildProcessWithoutNullStreams; }); diff --git a/server/tsconfig.json b/server/tsconfig.json index e087544f6b..c3aede3e5b 100644 --- a/server/tsconfig.json +++ b/server/tsconfig.json @@ -8,9 +8,9 @@ "experimentalDecorators": true, "allowSyntheticDefaultImports": true, "resolveJsonModule": true, - "target": "es2022", + "target": "es2024", "moduleResolution": "node16", - "lib": ["dom", "es2023"], + "lib": ["dom", "es2024"], "sourceMap": true, "outDir": "./dist", "incremental": true, diff --git a/web/package.json b/web/package.json index 2fb37daaed..62c5a43bd0 100644 --- a/web/package.json +++ b/web/package.json @@ -46,6 +46,8 @@ "geojson": "^0.5.0", "handlebars": "^4.7.8", "happy-dom": "^20.0.0", + "hls-video-element": "^1.5.11", + "hls.js": "^1.6.16", "intl-messageformat": "^11.0.0", "justified-layout": "^4.1.0", "lodash-es": "^4.17.21", @@ -101,7 +103,7 @@ "happy-dom": "^20.0.0", "prettier": "^3.7.4", "prettier-plugin-sort-json": "^4.1.1", - "prettier-plugin-svelte": "^3.3.3", + "prettier-plugin-svelte": "^4.0.0", "rollup-plugin-visualizer": "^7.0.0", "svelte": "5.55.8", "svelte-check": "^4.4.6", diff --git a/web/src/lib/commands.ts b/web/src/lib/commands.ts index ddc90a4a42..7498756ac0 100644 --- a/web/src/lib/commands.ts +++ b/web/src/lib/commands.ts @@ -16,6 +16,7 @@ import { mdiLink, mdiLockOutline, mdiMagnify, + mdiMapMarkerOutline, mdiMapOutline, mdiServer, mdiStateMachine, @@ -93,6 +94,11 @@ export const getPagesProvider = ($t: MessageFormatter) => { onAction: () => goto(Route.people()), $if: () => authManager.authenticated && authManager.preferences.people.enabled, }, + { + title: $t('places'), + icon: mdiMapMarkerOutline, + onAction: () => goto(Route.places()), + }, { title: $t('shared_links'), icon: mdiLink, diff --git a/web/src/lib/components/SchemaConfiguration.svelte b/web/src/lib/components/SchemaConfiguration.svelte index e48f46a402..2cc0b68089 100644 --- a/web/src/lib/components/SchemaConfiguration.svelte +++ b/web/src/lib/components/SchemaConfiguration.svelte @@ -64,7 +64,7 @@ {/each} -{:else if schema.uiHint === 'albumId'} +{:else if schema.uiHint === 'AlbumId'} {:else if schema.enum && schema.array} diff --git a/web/src/lib/components/album-page/AlbumThumbnail.svelte b/web/src/lib/components/album-page/AlbumThumbnail.svelte index 037bb78ab9..e585809dec 100644 --- a/web/src/lib/components/album-page/AlbumThumbnail.svelte +++ b/web/src/lib/components/album-page/AlbumThumbnail.svelte @@ -2,7 +2,7 @@ import AlbumCover from '$lib/components/album-page/AlbumCover.svelte'; import { authManager } from '$lib/managers/auth-manager.svelte'; import { getAlbumInfo } from '@immich/sdk'; - import { IconButton, LoadingSpinner } from '@immich/ui'; + import { IconButton, Text, LoadingSpinner } from '@immich/ui'; import { mdiTrashCanOutline } from '@mdi/js'; import { t } from 'svelte-i18n'; @@ -46,5 +46,22 @@ /> + {:catch} +
+
+ {$t('unknown')} + {albumId} +
+
+ +
+
{/await} diff --git a/web/src/lib/components/asset-viewer/VideoNativeViewer.svelte b/web/src/lib/components/asset-viewer/VideoNativeViewer.svelte index 295c5842a0..8f84466295 100644 --- a/web/src/lib/components/asset-viewer/VideoNativeViewer.svelte +++ b/web/src/lib/components/asset-viewer/VideoNativeViewer.svelte @@ -5,7 +5,7 @@ import { assetViewerManager } from '$lib/managers/asset-viewer-manager.svelte'; import { castManager } from '$lib/managers/cast-manager.svelte'; import { autoPlayVideo, lang, loopVideo as loopVideoPreference } from '$lib/stores/preferences.store'; - import { getAssetMediaUrl, getAssetPlaybackUrl } from '$lib/utils'; + import { getAssetHlsSessionUrl, getAssetHlsUrl, getAssetMediaUrl, getAssetPlaybackUrl } from '$lib/utils'; import { AssetMediaSize, type AssetResponseDto } from '@immich/sdk'; import { Icon, LoadingSpinner } from '@immich/ui'; import { @@ -21,6 +21,9 @@ mdiVolumeMedium, mdiVolumeMute, } from '@mdi/js'; + import Hls, { AbrController, Events, type FragLoadedData, type FragLoadingData, type HlsConfig } from 'hls.js'; + import 'hls-video-element'; + import type HlsVideoElement from 'hls-video-element'; import 'media-chrome/media-control-bar'; import 'media-chrome/media-controller'; import 'media-chrome/media-fullscreen-button'; @@ -28,9 +31,10 @@ import 'media-chrome/media-play-button'; import 'media-chrome/media-playback-rate-button'; import 'media-chrome/media-time-display'; - import 'media-chrome/media-time-range'; + import './immich-time-range'; import 'media-chrome/media-volume-range'; import 'media-chrome/menu/media-playback-rate-menu'; + import 'media-chrome/menu/media-rendition-menu'; import 'media-chrome/menu/media-settings-menu'; import 'media-chrome/menu/media-settings-menu-button'; import 'media-chrome/menu/media-settings-menu-item'; @@ -38,6 +42,8 @@ import { useSwipe, type SwipeCustomEvent } from 'svelte-gestures'; import { t } from 'svelte-i18n'; import { fade } from 'svelte/transition'; + import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte'; + import { mediaCapabilitiesManager } from '$lib/managers/media-capabilities-manager.svelte'; interface Props { asset: AssetResponseDto; @@ -69,14 +75,155 @@ let videoPlayer: HTMLVideoElement | undefined = $state(); let isLoading = $state(true); - let assetFileUrl = $derived( - playOriginalVideo - ? getAssetMediaUrl({ id: assetId, size: AssetMediaSize.Original, cacheKey }) - : getAssetPlaybackUrl({ id: assetId, cacheKey }), - ); + let assetFileUrl = $derived.by(() => { + if (featureFlagsManager.value.realtimeTranscoding) { + return getAssetHlsUrl(assetId); + } + + if (playOriginalVideo) { + return getAssetMediaUrl({ id: assetId, size: AssetMediaSize.Original, cacheKey }); + } + + return getAssetPlaybackUrl({ id: assetId, cacheKey }); + }); const aspectRatio = $derived(asset.width && asset.height ? `${asset.width} / ${asset.height}` : undefined); let showVideo = $state(false); let hasFocused = $state(false); + let activeSession: { assetId: string; id: string } | undefined; + let rebuildCount = 0; + + const MAX_REBUILDS = 1; + const SESSION_ID_REGEX = /\/video\/stream\/([0-9a-f-]{36})\//; + + // hls.js can abandon fetching an in-flight fragment if it thinks it'll take too long, in which case + // it emergency switches to a different variant. This extends the delay even further due to + // cold starting another transcode, so let the fragment finish and have steady ABR decide the next level. + // + // It can also emergency switch between fragments: while a switch's first segment is still loading, + // it can run out of buffer and drop to a lower level for just one segment before continuing at the switched quality. + // This can cause multiple redundant transcoding restarts when it occurs. + // Hold the committed level until its first fragment lands, then resume normal ABR. + class NoAbandonAbrController extends AbrController { + private switchTarget = -1; + + protected override onFragLoading(_event: Events.FRAG_LOADING, data: FragLoadingData) { + if (data.frag.sn === 'initSegment') { + this.switchTarget = data.frag.level; + } + } + + protected override onFragLoaded(event: Events.FRAG_LOADED, data: FragLoadedData) { + if (data.frag.sn !== 'initSegment') { + this.switchTarget = -1; + } + super.onFragLoaded(event, data); + } + + override get nextAutoLevel(): number { + const level = super.nextAutoLevel; + const target = this.hls.levels[this.switchTarget]; + // Hold the committed level, but only while hls.js still considers it healthy. + if (target && level < this.switchTarget && target.loadError === 0 && target.fragmentError === 0) { + return this.switchTarget; + } + return level; + } + + override set nextAutoLevel(level: number) { + super.nextAutoLevel = level; + } + } + + const hlsConfig: Partial = { + abrController: NoAbandonAbrController, + highBufferWatchdogPeriod: 10, + detectStallWithCurrentTimeMs: 10_000, + maxBufferHole: 0.5, + maxBufferLength: 30, + maxMaxBufferLength: 60, + fragLoadPolicy: { + default: { + maxTimeToFirstByteMs: 30_000, + maxLoadTimeMs: 60_000, + timeoutRetry: { maxNumRetry: 5, retryDelayMs: 100, maxRetryDelayMs: 0 }, + errorRetry: { maxNumRetry: 3, retryDelayMs: 1000, maxRetryDelayMs: 8000 }, + }, + }, + useMediaCapabilities: false, + }; + + const releaseSession = () => { + const session = activeSession; + if (!session) { + return; + } + activeSession = undefined; + const url = getAssetHlsSessionUrl(session.assetId, session.id); + void fetch(url, { method: 'DELETE' }).catch(() => console.warn('Failed to release HLS session', session)); + }; + + const isHlsElement = (el: HTMLVideoElement | undefined): el is HlsVideoElement => { + return el?.tagName === 'HLS-VIDEO'; + }; + + const wireHlsListeners = (el: HlsVideoElement, assetId: string, resumeTime?: number) => { + const api = el.api; + if (!api) { + return; + } + + // This is a hack to make the rendition menu use `api.currentLevel` instead of `api.nextLevel`. + // `api.nextLevel` makes the player request the next segment followed by the current segment. + // That backward request causes the server to restart transcoding for no reason. + Object.defineProperty(api, 'nextLevel', { + configurable: true, + get: () => api.currentLevel, + set: (level: number) => { + api.currentLevel = level; + }, + }); + + // eslint-disable-next-line @typescript-eslint/no-misused-promises + api.on(Hls.Events.MANIFEST_PARSED, async () => { + // Defer hls.js's first fragment load until we filter out suboptimal variants + api.stopLoad(); + const id = api.levels[0]?.url[0]?.match(SESSION_ID_REGEX)?.[1]; + if (id) { + activeSession = { assetId, id }; + } + + const keep = await mediaCapabilitiesManager.efficientLevels(api.levels); + for (let i = api.levels.length - 1; i >= 0; i--) { + if (!keep.has(i)) { + api.removeLevel(i); + } + } + + api.startLoad(resumeTime); + }); + + api.on(Hls.Events.FRAG_LOADED, () => (rebuildCount = 0)); + + api.on(Hls.Events.ERROR, (_, data) => { + // 404 on a fragment can mean the server-side session has expired. Refetch + // master for a new session, but give up if it still 404s. + if ( + !data.fatal || + data.details !== Hls.ErrorDetails.FRAG_LOAD_ERROR || + data.response?.code !== 404 || + rebuildCount++ >= MAX_REBUILDS + ) { + console.error('HLS error', JSON.stringify(data)); + return; + } + console.warn('Error loading segment, starting new session'); + activeSession = undefined; + resumeTime = el.currentTime; + el.load(); + // wireHlsListeners must run after el.api is repopulated. + queueMicrotask(() => wireHlsListeners(el, assetId, resumeTime)); + }); + }; onMount(() => { showVideo = true; @@ -84,10 +231,31 @@ $effect(() => { // reactive on `assetFileUrl` changes - if (assetFileUrl) { + if (videoPlayer && assetFileUrl) { hasFocused = false; - videoPlayer?.load(); + rebuildCount = 0; + releaseSession(); + if (isHlsElement(videoPlayer)) { + videoPlayer.config = hlsConfig; + videoPlayer.src = assetFileUrl; + const el = videoPlayer; + queueMicrotask(() => wireHlsListeners(el, assetId)); + } else { + videoPlayer.load(); + } } + return releaseSession; + }); + + const onPagehide = (event: PageTransitionEvent) => { + if (!event.persisted) { + releaseSession(); + } + }; + + $effect(() => { + window.addEventListener('pagehide', onPagehide); + return () => window.removeEventListener('pagehide', onPagehide); }); onDestroy(() => { @@ -144,6 +312,10 @@ videoPlayer?.pause(); } }); + + // The time is only refreshed on HLS fragment decode by default, + // so manually emit events on seek to update it immediately. + const onSeeking = (event: Event) => event.currentTarget?.dispatchEvent(new Event('timeupdate')); {#if showVideo} @@ -172,27 +344,51 @@ style:aspect-ratio={aspectRatio} defaultduration={asset.duration! / 1000} > - + {#if featureFlagsManager.value.realtimeTranscoding} + handleCanPlay(e.currentTarget as HTMLVideoElement)} + onended={onVideoEnded} + onseeking={onSeeking} + onplaying={(e: Event) => { + if (!hasFocused) { + (e.currentTarget as HTMLElement).focus(); + hasFocused = true; + } + }} + onclose={onClose} + poster={getAssetMediaUrl({ id: asset.id, size: AssetMediaSize.Preview, cacheKey })} + > + {:else} + + {/if} {#if extendedControls} {/if} @@ -238,7 +444,7 @@ {/if} - + @@ -248,7 +454,7 @@ {/if} - {#if assetViewerManager.isFaceEditMode} + {#if assetViewerManager.isFaceEditMode && videoPlayer} {/if} {/if} @@ -291,12 +497,12 @@ font-variant-numeric: tabular-nums; } - media-time-range, + immich-time-range, media-volume-range { --media-control-hover-background: none; } - media-time-range:hover, + immich-time-range:hover, media-volume-range:hover { --media-range-thumb-opacity: 1; } diff --git a/web/src/lib/components/asset-viewer/immich-time-range.ts b/web/src/lib/components/asset-viewer/immich-time-range.ts new file mode 100644 index 0000000000..a3de131e73 --- /dev/null +++ b/web/src/lib/components/asset-viewer/immich-time-range.ts @@ -0,0 +1,54 @@ +import { MediaUIEvents } from 'media-chrome/constants'; +import MediaTimeRange from 'media-chrome/media-time-range'; + +const COMMIT_DELAY_MS = 750; + +/** Custom MediaTimeRange that only seeks after pointer release to avoid hammering the server. + * Keyboard input uses timed debouncing instead since there's no release event. */ +class ImmichTimeRange extends MediaTimeRange { + private seeking = false; + private pending: number | undefined; + private idleTimer: ReturnType | undefined; + + override connectedCallback() { + super.connectedCallback(); + this.addEventListener('pointerdown', this.hold); + this.addEventListener('keydown', this.hold); + this.addEventListener('pointerup', this.release); + this.addEventListener('pointercancel', this.release); + this.addEventListener(MediaUIEvents.MEDIA_SEEK_REQUEST, this.intercept, { capture: true }); + } + + private hold(event: Event) { + if (event instanceof KeyboardEvent) { + if (!this.keysUsed.includes(event.key)) { + return; + } + clearTimeout(this.idleTimer); + this.idleTimer = setTimeout(this.release, COMMIT_DELAY_MS); + } + this.seeking = true; + } + + private intercept(event: Event) { + if (!this.seeking) { + return; // not mid-scrub, or this is the request we replay in release() + } + this.pending = (event as CustomEvent).detail; + event.stopImmediatePropagation(); + } + + private release() { + clearTimeout(this.idleTimer); + this.seeking = false; + if (this.pending !== undefined) { + const detail = this.pending; + this.pending = undefined; + this.dispatchEvent(new CustomEvent(MediaUIEvents.MEDIA_SEEK_REQUEST, { bubbles: true, composed: true, detail })); + } + } +} + +if (!globalThis.customElements.get('immich-time-range')) { + globalThis.customElements.define('immich-time-range', ImmichTimeRange); +} diff --git a/web/src/lib/components/assets/thumbnail/Thumbnail.svelte b/web/src/lib/components/assets/thumbnail/Thumbnail.svelte index aef52d0cfd..347ff3e75f 100644 --- a/web/src/lib/components/assets/thumbnail/Thumbnail.svelte +++ b/web/src/lib/components/assets/thumbnail/Thumbnail.svelte @@ -78,6 +78,7 @@ let mouseOver = $state(false); let loaded = $state(false); let thumbError = $state(false); + let skipFade = $state(false); let width = $derived(thumbnailSize || thumbnailWidth || 235); let height = $derived(thumbnailSize || thumbnailHeight || 235); @@ -252,7 +253,12 @@ widthStyle="{width}px" heightStyle="{height}px" curve={selected} - onComplete={(errored) => ((loaded = true), (thumbError = errored))} + onComplete={(errored) => { + const rect = element?.getBoundingClientRect(); + skipFade = !rect || rect.bottom < 0 || rect.top > window.innerHeight; + loaded = true; + thumbError = errored; + }} /> {#if asset.isVideo}
@@ -297,7 +303,10 @@ void) | undefined; + onEndReached?: (() => void) | undefined; showAssetName?: boolean; onReload?: (() => void) | undefined; pageHeaderOffset?: number; @@ -50,7 +55,7 @@ disableAssetSelect = false, showArchiveIcon = false, viewport, - onIntersected = undefined, + onEndReached = undefined, showAssetName = false, onReload = undefined, slidingWindowOffset = 0, @@ -70,24 +75,23 @@ }), ); - const getStyle = (i: number) => { - const geo = geometry; - return `top: ${geo.getTop(i)}px; left: ${geo.getLeft(i)}px; width: ${geo.getWidth(i)}px; height: ${geo.getHeight(i)}px;`; + const getStyle = (index: number) => { + return `top: ${geometry.getTop(index)}px; left: ${geometry.getLeft(index)}px; width: ${geometry.getWidth(index)}px; height: ${geometry.getHeight(index)}px;`; }; - const isIntersecting = (i: number) => { - const geo = geometry; + const isInOrNearViewport = (index: number) => { const window = slidingWindow; - const top = geo.getTop(i); - return top + pageHeaderOffset < window.bottom && top + geo.getHeight(i) > window.top; + const top = geometry.getTop(index); + return top + pageHeaderOffset < window.bottom && top + geometry.getHeight(index) > window.top; }; let shiftKeyIsDown = $state(false); let lastAssetMouseEvent: TimelineAsset | null = $state(null); let scrollTop = $state(0); + let slidingWindow = $derived.by(() => { - const top = (scrollTop || 0) - slidingWindowOffset; - const bottom = top + viewport.height + slidingWindowOffset; + const top = (scrollTop || 0) - slidingWindowOffset - INTERSECTION_EXPAND_TOP; + const bottom = top + viewport.height + slidingWindowOffset + INTERSECTION_EXPAND_BOTTOM; return { top, bottom, @@ -101,17 +105,15 @@ const updateSlidingWindow = () => (scrollTop = document.scrollingElement?.scrollTop ?? 0); - const debouncedOnIntersected = debounce(() => onIntersected?.(), 750, { maxWait: 100, leading: true }); + const debouncedOnEndReached = debounce(() => onEndReached?.(), 750, { maxWait: 100, leading: true }); - let lastIntersectedHeight = 0; + let lastEndReachedHeight = 0; $effect(() => { - // Intersect if there's only one viewport worth of assets left to scroll. if (geometry.containerHeight - slidingWindow.bottom <= viewport.height) { - // Notify we got to (near) the end of scroll. - const intersectedHeight = geometry.containerHeight; - if (lastIntersectedHeight !== intersectedHeight) { - debouncedOnIntersected(); - lastIntersectedHeight = intersectedHeight; + const contentHeight = geometry.containerHeight; + if (lastEndReachedHeight !== contentHeight) { + debouncedOnEndReached(); + lastEndReachedHeight = contentHeight; } } }); @@ -362,10 +364,10 @@ style:height={geometry.containerHeight + 'px'} style:width={geometry.containerWidth + 'px'} > - {#each assets as asset, i (asset.id + '-' + i)} - {#if isIntersecting(i)} + {#each assets as asset, index (asset.id + '-' + index)} + {#if isInOrNearViewport(index)} {@const currentAsset = toTimelineAsset(asset)} -
+
{ @@ -382,8 +384,8 @@ asset={currentAsset} selected={assetInteraction.hasSelectedAsset(currentAsset.id)} selectionCandidate={assetInteraction.hasSelectionCandidate(currentAsset.id)} - thumbnailWidth={geometry.getWidth(i)} - thumbnailHeight={geometry.getHeight(i)} + thumbnailWidth={geometry.getWidth(index)} + thumbnailHeight={geometry.getHeight(index)} /> {#if showAssetName && !isTimelineAsset(asset)}
{ + const getReleaseInfo = (release?: ReleaseEventV1) => { if (!release || !release?.isAvailable || !authManager.user.isAdmin) { return; } diff --git a/web/src/lib/managers/event-manager.svelte.ts b/web/src/lib/managers/event-manager.svelte.ts index 8df3dcfb0e..4a3a560bf9 100644 --- a/web/src/lib/managers/event-manager.svelte.ts +++ b/web/src/lib/managers/event-manager.svelte.ts @@ -7,13 +7,13 @@ import type { LoginResponseDto, PersonResponseDto, QueueResponseDto, + ReleaseEventV1, SharedLinkResponseDto, SystemConfigDto, TagResponseDto, UserAdminResponseDto, WorkflowResponseDto, } from '@immich/sdk'; -import type { ReleaseEvent } from '$lib/types'; import { BaseEventManager } from '$lib/utils/base-event-manager.svelte'; import type { TreeNode } from '$lib/utils/tree-utils'; @@ -86,7 +86,7 @@ export type Events = { WorkflowUpdate: [WorkflowResponseDto]; WorkflowDelete: [WorkflowResponseDto]; - ReleaseEvent: [ReleaseEvent]; + ReleaseEvent: [ReleaseEventV1]; WebsocketConnect: []; }; diff --git a/web/src/lib/managers/media-capabilities-manager.svelte.ts b/web/src/lib/managers/media-capabilities-manager.svelte.ts new file mode 100644 index 0000000000..ccabc6680d --- /dev/null +++ b/web/src/lib/managers/media-capabilities-manager.svelte.ts @@ -0,0 +1,92 @@ +export type Level = { videoCodec?: string; width: number; height: number; bitrate: number; frameRate: number }; + +export const DEFAULT_DECODING_INFO: MediaCapabilitiesDecodingInfo = { + powerEfficient: true, + smooth: true, + supported: true, + keySystemAccess: null, +}; + +class MediaCapabilitiesManager { + private cache = new Map>(); + + init() { + for (const level of [ + { videoCodec: 'av01.0.04M.08', width: 854, height: 480, bitrate: 1_000_000, frameRate: 60 }, + { videoCodec: 'hvc1.1.6.L90.B0', width: 854, height: 480, bitrate: 1_200_000, frameRate: 60 }, + { videoCodec: 'av01.0.08M.08', width: 1280, height: 720, bitrate: 2_000_000, frameRate: 60 }, + { videoCodec: 'hvc1.1.6.L93.B0', width: 1280, height: 720, bitrate: 2_500_000, frameRate: 60 }, + { videoCodec: 'av01.0.09M.08', width: 1920, height: 1080, bitrate: 4_000_000, frameRate: 60 }, + { videoCodec: 'hvc1.1.6.L120.B0', width: 1920, height: 1080, bitrate: 4_500_000, frameRate: 60 }, + { videoCodec: 'av01.0.12M.08', width: 2560, height: 1440, bitrate: 7_000_000, frameRate: 60 }, + { videoCodec: 'hvc1.2.4.L150.B0', width: 2560, height: 1440, bitrate: 8_000_000, frameRate: 60 }, + ]) { + this.cache.set(this.cacheKey(level), this.queryDecodingInfo(level)); + } + + for (const level of [ + { videoCodec: 'avc1.64001e', width: 854, height: 480, bitrate: 2_500_000, frameRate: 60 }, + { videoCodec: 'avc1.64001f', width: 1280, height: 720, bitrate: 5_000_000, frameRate: 60 }, + { videoCodec: 'avc1.640028', width: 1920, height: 1080, bitrate: 8_000_000, frameRate: 60 }, + { videoCodec: 'avc1.640032', width: 2560, height: 1440, bitrate: 16_000_000, frameRate: 60 }, + ]) { + this.cache.set(this.cacheKey(level), Promise.resolve(DEFAULT_DECODING_INFO)); + } + } + + async efficientLevels(levels: Level[]) { + const decodingInfo = await Promise.all(levels.map((level) => this.decodingInfo(level))); + // eslint-disable-next-line svelte/prefer-svelte-reactivity + const lowestBitrateByHeight = new Map(); + for (let i = 0; i < levels.length; i++) { + if (!decodingInfo[i].powerEfficient) { + continue; + } + + const { bitrate, height } = levels[i]; + const cur = lowestBitrateByHeight.get(height); + if (cur === undefined || bitrate < levels[cur].bitrate) { + lowestBitrateByHeight.set(height, i); + } + } + + return new Set(lowestBitrateByHeight.values()); + } + + decodingInfo(level: Level) { + const key = this.cacheKey(level); + const existing = this.cache.get(key); + if (existing) { + return existing; + } + const promise = this.queryDecodingInfo(level); + this.cache.set(key, promise); + return promise; + } + + private async queryDecodingInfo(level: Level) { + try { + return await navigator.mediaCapabilities.decodingInfo({ + type: 'media-source', + video: { + contentType: `video/mp4; codecs="${level.videoCodec}"`, + width: level.width, + height: level.height, + bitrate: level.bitrate, + framerate: level.frameRate, + }, + }); + } catch { + return DEFAULT_DECODING_INFO; + } + } + + private cacheKey({ videoCodec, width, height, frameRate }: Level) { + const resolution = Math.min(width, height); + const fpsBucket = Math.trunc(frameRate / 61) * 60; + return `${videoCodec}|${resolution}|${fpsBucket}`; + } +} + +export const mediaCapabilitiesManager = new MediaCapabilitiesManager(); +mediaCapabilitiesManager.init(); diff --git a/web/src/lib/managers/release-manager.svelte.ts b/web/src/lib/managers/release-manager.svelte.ts index 15baa6de8f..fed57dd3d8 100644 --- a/web/src/lib/managers/release-manager.svelte.ts +++ b/web/src/lib/managers/release-manager.svelte.ts @@ -1,8 +1,8 @@ +import type { ReleaseEventV1 } from '@immich/sdk'; import { eventManager } from '$lib/managers/event-manager.svelte'; -import { type ReleaseEvent } from '$lib/types'; class ReleaseManager { - value = $state(); + value = $state(); constructor() { eventManager.on({ diff --git a/web/src/lib/modals/PluginMethodPicker.svelte b/web/src/lib/modals/PluginMethodPicker.svelte index bbfa2ca83f..230f5a2257 100644 --- a/web/src/lib/modals/PluginMethodPicker.svelte +++ b/web/src/lib/modals/PluginMethodPicker.svelte @@ -24,7 +24,7 @@
{method.title} - {#if method.uiHints.includes('filter')} + {#if method.uiHints.includes('Filter')} {$t('plugin_method_filter_type')} diff --git a/web/src/lib/modals/WorkflowEditTrigger.svelte b/web/src/lib/modals/WorkflowEditTrigger.svelte deleted file mode 100644 index d72bff3c6d..0000000000 --- a/web/src/lib/modals/WorkflowEditTrigger.svelte +++ /dev/null @@ -1,37 +0,0 @@ - - - -
- {#each pluginManager.triggers as item (item.trigger)} - (selected = item)}> -
- {getTriggerName($t, item.trigger)} - {getTriggerDescription($t, item.trigger)} -
-
- {/each} -
-
diff --git a/web/src/lib/modals/WorkflowTemplatePicker.svelte b/web/src/lib/modals/WorkflowTemplatePickerModal.svelte similarity index 87% rename from web/src/lib/modals/WorkflowTemplatePicker.svelte rename to web/src/lib/modals/WorkflowTemplatePickerModal.svelte index 05306671fc..0f3aad0ee6 100644 --- a/web/src/lib/modals/WorkflowTemplatePicker.svelte +++ b/web/src/lib/modals/WorkflowTemplatePickerModal.svelte @@ -2,7 +2,7 @@ import { pluginManager } from '$lib/managers/plugin-manager.svelte'; import { handleCreateWorkflow } from '$lib/services/workflow.service'; import { type PluginTemplateResponseDto } from '@immich/sdk'; - import { FormModal, Icon, ListButton, Text } from '@immich/ui'; + import { Badge, FormModal, Icon, ListButton, Text } from '@immich/ui'; import { mdiFlashOutline } from '@mdi/js'; import { t } from 'svelte-i18n'; @@ -59,6 +59,11 @@ {template.title} {template.description}
+ {#if template.uiHints.includes('SmartAlbum')} +
+ {$t('smart_album')} +
+ {/if}
{/each} diff --git a/web/src/lib/route.ts b/web/src/lib/route.ts index 734be99402..1846f29796 100644 --- a/web/src/lib/route.ts +++ b/web/src/lib/route.ts @@ -152,4 +152,13 @@ export const Route = { // queues queues: () => '/admin/queues', viewQueue: ({ name }: { name: QueueName }) => `/admin/queues/${asQueueSlug(name)}`, + + // continue helper for ensuring same-origin URLs + continue: (url: string | null, fallback: string) => { + if (!url || !url.startsWith('/') || url.startsWith('//')) { + return fallback; + } + + return url; + }, }; diff --git a/web/src/lib/services/workflow.service.ts b/web/src/lib/services/workflow.service.ts index 79358b9ee4..c81b1b2706 100644 --- a/web/src/lib/services/workflow.service.ts +++ b/web/src/lib/services/workflow.service.ts @@ -26,7 +26,7 @@ import type { MessageFormatter } from 'svelte-i18n'; import { goto } from '$app/navigation'; import { eventManager } from '$lib/managers/event-manager.svelte'; import WorkflowDuplicateModal from '$lib/modals/WorkflowDuplicateModal.svelte'; -import WorkflowTemplatePicker from '$lib/modals/WorkflowTemplatePicker.svelte'; +import WorkflowTemplatePickerModal from '$lib/modals/WorkflowTemplatePickerModal.svelte'; import { Route } from '$lib/route'; import { copyToClipboard, downloadJson } from '$lib/utils'; import { handleError } from '$lib/utils/handle-error'; @@ -50,7 +50,7 @@ export const getWorkflowsActions = ($t: MessageFormatter) => { const UseTemplate: ActionItem = { title: $t('browse_templates'), icon: mdiFileDocumentMultipleOutline, - onAction: () => modalManager.show(WorkflowTemplatePicker, {}), + onAction: () => modalManager.show(WorkflowTemplatePickerModal, {}), }; return { Create, UseTemplate }; diff --git a/web/src/lib/stores/websocket.ts b/web/src/lib/stores/websocket.ts index 5765f85a16..fc33812973 100644 --- a/web/src/lib/stores/websocket.ts +++ b/web/src/lib/stores/websocket.ts @@ -3,6 +3,7 @@ import { type AssetResponseDto, type MaintenanceStatusResponseDto, type NotificationDto, + type ReleaseEventV1, type ServerVersionResponseDto, type SyncAssetEditV1, type SyncAssetV2, @@ -15,7 +16,6 @@ import { eventManager } from '$lib/managers/event-manager.svelte'; import { Route } from '$lib/route'; import { maintenanceStore } from '$lib/stores/maintenance.store'; import { notificationManager } from '$lib/stores/notification-manager.svelte'; -import type { ReleaseEvent } from '$lib/types'; import { createEventEmitter } from '$lib/utils/eventemitter'; interface AppRestartEvent { @@ -34,7 +34,7 @@ export interface Events { on_person_thumbnail: (personId: string) => void; on_server_version: (serverVersion: ServerVersionResponseDto) => void; on_config_update: () => void; - on_new_release: (event: ReleaseEvent) => void; + on_new_release: (event: ReleaseEventV1) => void; on_session_delete: (sessionId: string) => void; on_notification: (notification: NotificationDto) => void; diff --git a/web/src/lib/types.ts b/web/src/lib/types.ts index b0e1466da1..41d98df097 100644 --- a/web/src/lib/types.ts +++ b/web/src/lib/types.ts @@ -1,4 +1,4 @@ -import type { QueueResponseDto, ServerVersionResponseDto } from '@immich/sdk'; +import type { QueueResponseDto } from '@immich/sdk'; import type { ActionItem } from '@immich/ui'; import type { DateTime } from 'luxon'; import type { SvelteSet } from 'svelte/reactivity'; @@ -7,14 +7,6 @@ import type { TimelineAsset } from '$lib/managers/timeline-manager/types'; export type LatLng = { lng: number; lat: number }; -export interface ReleaseEvent { - isAvailable: boolean; - /** ISO8601 */ - checkedAt: string; - serverVersion: ServerVersionResponseDto; - releaseVersion: ServerVersionResponseDto; -} - export type QueueSnapshot = { timestamp: number; snapshot?: QueueResponseDto[] }; export type HeaderButtonActionItem = ActionItem & { data?: { title?: string } }; @@ -104,7 +96,7 @@ export type JSONSchemaProperty = { array?: boolean; properties?: Record; required?: string[]; - uiHint?: 'albumId' | 'assetId' | 'personId'; + uiHint?: 'AlbumId' | 'AssetId' | 'PersonId'; }; // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/web/src/lib/utils.spec.ts b/web/src/lib/utils.spec.ts index 9ecc7f548e..992cd803e7 100644 --- a/web/src/lib/utils.spec.ts +++ b/web/src/lib/utils.spec.ts @@ -1,5 +1,5 @@ import { AssetTypeEnum } from '@immich/sdk'; -import { getAssetUrl, getReleaseType } from '$lib/utils'; +import { getAssetUrl, semverToName } from '$lib/utils'; import { assetFactory } from '@test-data/factories/asset-factory'; import { sharedLinkFactory } from '@test-data/factories/shared-link-factory'; @@ -161,26 +161,13 @@ describe('utils', () => { expect(url).toContain(asset.id); }); }); - - describe(getReleaseType.name, () => { - it('should return "major" for major version changes', () => { - expect(getReleaseType({ major: 1, minor: 0, patch: 0 }, { major: 2, minor: 0, patch: 0 })).toBe('major'); - expect(getReleaseType({ major: 1, minor: 0, patch: 0 }, { major: 3, minor: 2, patch: 1 })).toBe('major'); + describe('semverToName', () => { + it('should not append release candidate tag if prelease is not set', () => { + expect(semverToName({ major: 3, minor: 0, patch: 0, prerelease: null })).toEqual('v3.0.0'); }); - it('should return "minor" for minor version changes', () => { - expect(getReleaseType({ major: 1, minor: 0, patch: 0 }, { major: 1, minor: 1, patch: 0 })).toBe('minor'); - expect(getReleaseType({ major: 1, minor: 0, patch: 0 }, { major: 1, minor: 2, patch: 1 })).toBe('minor'); - }); - - it('should return "patch" for patch version changes', () => { - expect(getReleaseType({ major: 1, minor: 0, patch: 0 }, { major: 1, minor: 0, patch: 1 })).toBe('patch'); - expect(getReleaseType({ major: 1, minor: 0, patch: 0 }, { major: 1, minor: 0, patch: 5 })).toBe('patch'); - }); - - it('should return "none" for matching versions', () => { - expect(getReleaseType({ major: 1, minor: 0, patch: 0 }, { major: 1, minor: 0, patch: 0 })).toBe('none'); - expect(getReleaseType({ major: 1, minor: 2, patch: 3 }, { major: 1, minor: 2, patch: 3 })).toBe('none'); + it('should append release candidate if set', () => { + expect(semverToName({ major: 3, minor: 0, patch: 0, prerelease: 0 })).toEqual('v3.0.0-rc.0'); }); }); }); diff --git a/web/src/lib/utils.ts b/web/src/lib/utils.ts index f0c2270532..3bd0114e19 100644 --- a/web/src/lib/utils.ts +++ b/web/src/lib/utils.ts @@ -245,6 +245,14 @@ export const getAssetPlaybackUrl = (options: AssetUrlOptions) => { return createUrl(getAssetPlaybackPath(id), { ...authManager.params, c }); }; +export const getAssetHlsUrl = (id: string) => { + return createUrl(`/assets/${id}/video/stream/main.m3u8`, authManager.params); +}; + +export const getAssetHlsSessionUrl = (id: string, sessionId: string) => { + return createUrl(`/assets/${id}/video/stream/${sessionId}`, authManager.params); +}; + export const getProfileImageUrl = (user: UserResponseDto) => createUrl(getUserProfileImagePath(user.id), { updatedAt: user.profileChangedAt }); @@ -416,26 +424,8 @@ export function createDateFormatter(localeCode: string | undefined): DateFormatt }; } -export const getReleaseType = ( - current: ServerVersionResponseDto, - newVersion: ServerVersionResponseDto, -): 'major' | 'minor' | 'patch' | 'none' => { - if (current.major !== newVersion.major) { - return 'major'; - } - - if (current.minor !== newVersion.minor) { - return 'minor'; - } - - if (current.patch !== newVersion.patch) { - return 'patch'; - } - - return 'none'; -}; - -export const semverToName = ({ major, minor, patch }: ServerVersionResponseDto) => `v${major}.${minor}.${patch}`; +export const semverToName = ({ major, minor, patch, prerelease }: ServerVersionResponseDto) => + `v${major}.${minor}.${patch}${prerelease === null ? '' : `-rc.${prerelease}`}`; export const withoutIcons = (actions: ActionItem[]): ActionItem[] => actions.map((action) => ({ ...action, icon: undefined })); diff --git a/web/src/routes/(user)/search/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/search/[[photos=photos]]/[[assetId=id]]/+page.svelte index 86a3040d5a..40bdc4b01f 100644 --- a/web/src/routes/(user)/search/[[photos=photos]]/[[assetId=id]]/+page.svelte +++ b/web/src/routes/(user)/search/[[photos=photos]]/[[assetId=id]]/+page.svelte @@ -309,7 +309,7 @@ + import { serverConfigManager } from '$lib/managers/server-config-manager.svelte'; import SettingAccordion from '$lib/components/shared-components/settings/SettingAccordion.svelte'; import { authManager } from '$lib/managers/auth-manager.svelte'; import { handleError } from '$lib/utils/handle-error'; @@ -21,6 +22,7 @@ // People let peopleEnabled = $state(authManager.preferences.people?.enabled ?? false); let peopleSidebar = $state(authManager.preferences.people?.sidebarWeb ?? false); + let peopleMinFaces = $state(authManager.preferences.people?.minimumFaces ?? serverConfigManager.value.minFaces); // Ratings let ratingsEnabled = $state(authManager.preferences.ratings?.enabled ?? false); @@ -43,7 +45,7 @@ albums: { defaultAssetOrder }, folders: { enabled: foldersEnabled, sidebarWeb: foldersSidebar }, memories: { enabled: memoriesEnabled, duration: memoriesDuration }, - people: { enabled: peopleEnabled, sidebarWeb: peopleSidebar }, + people: { enabled: peopleEnabled, sidebarWeb: peopleSidebar, minimumFaces: peopleMinFaces }, ratings: { enabled: ratingsEnabled }, sharedLinks: { enabled: sharedLinksEnabled, sidebarWeb: sharedLinkSidebar }, tags: { enabled: tagsEnabled, sidebarWeb: tagsSidebar }, @@ -117,6 +119,9 @@ + + + {/if}
diff --git a/web/src/routes/(user)/utilities/geolocation/+page.svelte b/web/src/routes/(user)/utilities/geolocation/+page.svelte index 18287a7b13..061872e42c 100644 --- a/web/src/routes/(user)/utilities/geolocation/+page.svelte +++ b/web/src/routes/(user)/utilities/geolocation/+page.svelte @@ -38,6 +38,8 @@ withCoordinates: true, }; + const isOwnAsset = (asset: TimelineAsset) => asset.ownerId === authManager.user.id; + const handleUpdate = async () => { if (!point) { return; @@ -54,7 +56,7 @@ await updateAssets({ assetBulkUpdateDto: { - ids: assetMultiSelectManager.assets.map((asset) => asset.id), + ids: assetMultiSelectManager.assets.filter((asset) => isOwnAsset(asset)).map((asset) => asset.id), latitude: point.lat, longitude: point.lng, }, @@ -124,7 +126,7 @@ }, 1500); point = { lat: asset.latitude, lng: asset.longitude }; void setQueryValue('at', asset.id); - } else { + } else if (isOwnAsset(asset)) { onClick(timelineManager, timelineDay.getAssets(), timelineDay.groupTitle, asset); } }; @@ -199,6 +201,9 @@ onThumbnailClick={handleThumbnailClick} > {#snippet customThumbnailLayout(asset: TimelineAsset)} + {#if !isOwnAsset(asset)} +
+ {/if} {#if hasGps(asset)}
{asset.city || $t('gps')} diff --git a/web/src/routes/(user)/workflows/[workflowId]/WorkflowStepCard.svelte b/web/src/routes/(user)/workflows/[workflowId]/WorkflowStepCard.svelte index 661b90bd73..6110572e5b 100644 --- a/web/src/routes/(user)/workflows/[workflowId]/WorkflowStepCard.svelte +++ b/web/src/routes/(user)/workflows/[workflowId]/WorkflowStepCard.svelte @@ -1,5 +1,25 @@ + +
event.preventDefault()}> -
+
+
diff --git a/web/src/routes/auth/login/+page.ts b/web/src/routes/auth/login/+page.ts index 03a053fcd5..dceb340505 100644 --- a/web/src/routes/auth/login/+page.ts +++ b/web/src/routes/auth/login/+page.ts @@ -8,7 +8,8 @@ import type { PageLoad } from './$types'; export const load = (async ({ parent, url }) => { await parent(); - const continueUrl = url.searchParams.get('continue') || Route.photos(); + const continueUrl = Route.continue(url.searchParams.get('continue'), Route.photos()); + if (authManager.authenticated) { redirect(307, continueUrl); } diff --git a/web/src/routes/auth/pin-prompt/+page.svelte b/web/src/routes/auth/pin-prompt/+page.svelte index d6648889a8..fff02e054f 100644 --- a/web/src/routes/auth/pin-prompt/+page.svelte +++ b/web/src/routes/auth/pin-prompt/+page.svelte @@ -30,7 +30,7 @@ await new Promise((resolve) => setTimeout(resolve, 1000)); - await goto(data.continueUrl); + await goto(Route.continue(data.continueUrl, Route.photos())); } catch (error) { handleError(error, $t('wrong_pin_code')); isBadPinCode = true;