diff --git a/.github/.nvmrc b/.github/.nvmrc
index 8e35034890..5bf4400f22 100644
--- a/.github/.nvmrc
+++ b/.github/.nvmrc
@@ -1 +1 @@
-24.14.1
+24.15.0
diff --git a/.github/workflows/build-mobile.yml b/.github/workflows/build-mobile.yml
index c2a3918cfe..72e8b10aeb 100644
--- a/.github/workflows/build-mobile.yml
+++ b/.github/workflows/build-mobile.yml
@@ -103,7 +103,7 @@ jobs:
- name: Restore Gradle Cache
id: cache-gradle-restore
- uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
+ uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: |
~/.gradle/caches
@@ -160,7 +160,7 @@ jobs:
- name: Save Gradle Cache
id: cache-gradle-save
- uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
+ uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
if: github.ref == 'refs/heads/main'
with:
path: |
@@ -210,7 +210,7 @@ jobs:
working-directory: ./mobile
- name: Setup Ruby
- uses: ruby/setup-ruby@e65c17d16e57e481586a6a5a0282698790062f92 # v1.300.0
+ uses: ruby/setup-ruby@7372622e62b60b3cb750dcd2b9e32c247ffec26a # v1.302.0
with:
ruby-version: '3.3'
bundler-cache: true
diff --git a/.github/workflows/check-openapi.yml b/.github/workflows/check-openapi.yml
index 3b3eb774cb..f2d03f7400 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@e6faebce24cf20ac38653d0d2c7f4aa80aaafc79 # v0.0.38
+ uses: oasdiff/oasdiff-action/breaking@f8cb9308b42121e793f835bd14c0b8090420430c # v0.0.39
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/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 2378b032b6..f2d99ae1e8 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -57,7 +57,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
- uses: github/codeql-action/init@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1
+ uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2
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@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1
+ uses: github/codeql-action/autobuild@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2
# ℹ️ 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@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1
+ uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2
with:
category: '/language:${{matrix.language}}'
diff --git a/.github/workflows/docs-deploy.yml b/.github/workflows/docs-deploy.yml
index 8aa063e1bb..57ea2d41d4 100644
--- a/.github/workflows/docs-deploy.yml
+++ b/.github/workflows/docs-deploy.yml
@@ -211,7 +211,7 @@ jobs:
run: 'mise run //deployment:tf apply'
- name: Comment
- uses: actions-cool/maintain-one-comment@4b2dbf086015f892dcb5e8c1106f5fccd6c1476b # v3.2.0
+ uses: actions-cool/maintain-one-comment@909842216bc8e8658364c572ec52100f4c2cc50a # v3.3.0
if: ${{ steps.parameters.outputs.event == 'pr' }}
with:
token: ${{ steps.token.outputs.token }}
diff --git a/.github/workflows/docs-destroy.yml b/.github/workflows/docs-destroy.yml
index bb24a017fe..0e9c37a66c 100644
--- a/.github/workflows/docs-destroy.yml
+++ b/.github/workflows/docs-destroy.yml
@@ -42,7 +42,7 @@ jobs:
run: 'mise run //deployment:tf destroy -- -refresh=false'
- name: Comment
- uses: actions-cool/maintain-one-comment@4b2dbf086015f892dcb5e8c1106f5fccd6c1476b # v3.2.0
+ uses: actions-cool/maintain-one-comment@909842216bc8e8658364c572ec52100f4c2cc50a # v3.3.0
with:
token: ${{ steps.token.outputs.token }}
number: ${{ github.event.number }}
diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml
index 5731c06372..2aa028b22e 100644
--- a/.github/workflows/prepare-release.yml
+++ b/.github/workflows/prepare-release.yml
@@ -142,7 +142,7 @@ jobs:
github-token: ${{ steps.generate-token.outputs.token }}
- name: Create draft release
- uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2.6.1
+ uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2.6.2
with:
draft: true
tag_name: ${{ needs.bump_version.outputs.version }}
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 4558b90866..46b1baed7e 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -392,6 +392,8 @@ jobs:
node-version-file: './server/.nvmrc'
cache: 'pnpm'
cache-dependency-path: '**/pnpm-lock.yaml'
+ - name: Setup Mise
+ uses: immich-app/devtools/actions/use-mise@035e80a7d4355d5f087ffb95db9e4a0944c04e56 # use-mise-action-v1.1.3
- name: Run pnpm install
run: SHARP_IGNORE_GLOBAL_LIBVIPS=true pnpm install --frozen-lockfile
- name: Run medium tests
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 496e7539e1..dbf9688b9b 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -13,10 +13,6 @@
"editor.wordBasedSuggestions": "off"
},
"[javascript]": {
- "editor.codeActionsOnSave": {
- "source.organizeImports": "explicit",
- "source.removeUnusedImports": "explicit"
- },
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
},
@@ -29,18 +25,11 @@
"editor.formatOnSave": true
},
"[svelte]": {
- "editor.codeActionsOnSave": {
- "source.organizeImports": "explicit",
- "source.removeUnusedImports": "explicit"
- },
"editor.defaultFormatter": "svelte.svelte-vscode",
- "editor.formatOnSave": true
+ "editor.formatOnSave": true,
+ "tailwindCSS.lint.suggestCanonicalClasses": "ignore"
},
"[typescript]": {
- "editor.codeActionsOnSave": {
- "source.organizeImports": "explicit",
- "source.removeUnusedImports": "explicit"
- },
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
},
diff --git a/cli/.nvmrc b/cli/.nvmrc
index 8e35034890..5bf4400f22 100644
--- a/cli/.nvmrc
+++ b/cli/.nvmrc
@@ -1 +1 @@
-24.14.1
+24.15.0
diff --git a/cli/package.json b/cli/package.json
index 108e65f945..7b42f77ff1 100644
--- a/cli/package.json
+++ b/cli/package.json
@@ -68,6 +68,6 @@
"micromatch": "^4.0.8"
},
"volta": {
- "node": "24.14.1"
+ "node": "24.15.0"
}
}
diff --git a/deployment/mise.toml b/deployment/mise.toml
index 8a01695fa7..61a50bb666 100644
--- a/deployment/mise.toml
+++ b/deployment/mise.toml
@@ -1,6 +1,6 @@
[tools]
-terragrunt = "1.0.0"
-opentofu = "1.11.5"
+terragrunt = "1.0.2"
+opentofu = "1.11.6"
[tasks."tg:fmt"]
run = "terragrunt hclfmt"
diff --git a/docker/docker-compose.prod.yml b/docker/docker-compose.prod.yml
index 700f68ff19..25751879f3 100644
--- a/docker/docker-compose.prod.yml
+++ b/docker/docker-compose.prod.yml
@@ -85,7 +85,7 @@ services:
container_name: immich_prometheus
ports:
- 9090:9090
- image: prom/prometheus@sha256:dda13e28bf95a5e5ca5b8ed56852006094c1c8e8871d9c9dbeed30aa6e55271f
+ image: prom/prometheus@sha256:e4254400b85610324913f0dc4acf92603d9984e7519414c5a12811aa6146acc3
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus-data:/prometheus
diff --git a/docs/.nvmrc b/docs/.nvmrc
index 8e35034890..5bf4400f22 100644
--- a/docs/.nvmrc
+++ b/docs/.nvmrc
@@ -1 +1 @@
-24.14.1
+24.15.0
diff --git a/docs/docs/administration/postgres-standalone.md b/docs/docs/administration/postgres-standalone.md
index 84681fdfa6..aa19e28cc1 100644
--- a/docs/docs/administration/postgres-standalone.md
+++ b/docs/docs/administration/postgres-standalone.md
@@ -81,7 +81,7 @@ VectorChord is the successor extension to pgvecto.rs, allowing for higher perfor
### Migrating from pgvecto.rs
-Support for pgvecto.rs will be dropped in a later release, hence we recommend all users currently using pgvecto.rs to migrate to VectorChord at their convenience. There are two primary approaches to do so.
+Support for pgvecto.rs has been dropped as of 3.0, hence all users currently using pgvecto.rs should migrate to VectorChord. There are two primary approaches to do so.
The easiest option is to have both extensions installed during the migration:
diff --git a/docs/docs/features/supported-formats.md b/docs/docs/features/supported-formats.md
index 86ac264cc3..917950dd2a 100644
--- a/docs/docs/features/supported-formats.md
+++ b/docs/docs/features/supported-formats.md
@@ -18,6 +18,7 @@ For the full list, refer to the [Immich source code](https://github.com/immich-a
| `JPEG 2000` | `.jp2` | :white_check_mark: | |
| `JPEG` | `.jpeg` `.jpg` `.jpe` `.insp` | :white_check_mark: | |
| `JPEG XL` | `.jxl` | :white_check_mark: | |
+| `MPO` | `.mpo` | :white_check_mark: | Multi-Picture |
| `PNG` | `.png` | :white_check_mark: | |
| `PSD` | `.psd` | :white_check_mark: | Adobe Photoshop |
| `RAW` | `.raw` | :white_check_mark: | |
diff --git a/docs/docs/guides/remote-access.md b/docs/docs/guides/remote-access.md
index 518b003c3a..09e34c5107 100644
--- a/docs/docs/guides/remote-access.md
+++ b/docs/docs/guides/remote-access.md
@@ -39,7 +39,7 @@ You can learn how to set up Tailscale together with Immich with the [tutorial vi
### Cons
- The Tailscale client usually needs to run as root on your devices and it increases the attack surface slightly compared to a minimal Wireguard server. e.g., an [RCE vulnerability](https://github.com/tailscale/tailscale/security/advisories/GHSA-vqp6-rc3h-83cp) was discovered in the Windows Tailscale client in November 2022.
-- Tailscale is a paid service. However, there is a generous [free tier](https://tailscale.com/pricing/) that permits up to 3 users and up to 100 devices.
+- Tailscale is a paid service. However, there is a generous [free tier](https://tailscale.com/pricing/) suitable for personal use.
- Tailscale needs to be installed and running on both server-side and client-side.
## Option 3: Reverse Proxy
diff --git a/docs/docs/install/environment-variables.md b/docs/docs/install/environment-variables.md
index b29c233153..1b67637ac0 100644
--- a/docs/docs/install/environment-variables.md
+++ b/docs/docs/install/environment-variables.md
@@ -81,7 +81,7 @@ Information on the current workers can be found [here](/administration/jobs-work
| `DB_PASSWORD` | Database password | `postgres` | server, database\*1 |
| `DB_DATABASE_NAME` | Database name | `immich` | server, database\*1 |
| `DB_SSL_MODE` | Database SSL mode | | server |
-| `DB_VECTOR_EXTENSION`\*2 | Database vector extension (one of [`vectorchord`, `pgvector`, `pgvecto.rs`]) | | server |
+| `DB_VECTOR_EXTENSION`\*2 | Database vector extension (one of [`vectorchord`, `pgvector`]) | | server |
| `DB_SKIP_MIGRATIONS` | Whether to skip running migrations on startup (one of [`true`, `false`]) | `false` | server |
| `DB_STORAGE_TYPE` | Optimize concurrent IO on SSDs or sequential IO on HDDs ([`SSD`, `HDD`])\*3 | `SSD` | database |
diff --git a/docs/docs/install/upgrading.md b/docs/docs/install/upgrading.md
index 12e5c9c342..38fc056f80 100644
--- a/docs/docs/install/upgrading.md
+++ b/docs/docs/install/upgrading.md
@@ -130,7 +130,3 @@ These storage mediums have different performance characteristics. As a result, t
#### Can I use the new database image as a general PostgreSQL image outside of Immich?
It’s a standard PostgreSQL container image that additionally contains the VectorChord, pgvector, and (optionally) pgvecto.rs extensions. If you were using the previous pgvecto.rs image for other purposes, you can similarly do so with this image.
-
-#### If pgvecto.rs and pgvector still work, why should I switch to VectorChord?
-
-VectorChord is faster, more stable, uses less RAM, and (with the settings Immich uses) offers higher-quality results than pgvector and pgvecto.rs. This translates to better search and facial recognition experiences. In addition, pgvecto.rs support will be dropped in the future, so changing it sooner will avoid disruption.
diff --git a/docs/package.json b/docs/package.json
index f976791279..d469d1ffef 100644
--- a/docs/package.json
+++ b/docs/package.json
@@ -17,10 +17,10 @@
"write-heading-ids": "docusaurus write-heading-ids"
},
"dependencies": {
- "@docusaurus/core": "~3.9.0",
- "@docusaurus/preset-classic": "~3.9.0",
- "@docusaurus/theme-common": "~3.9.0",
- "@docusaurus/theme-mermaid": "~3.9.0",
+ "@docusaurus/core": "~3.10.0",
+ "@docusaurus/preset-classic": "~3.10.0",
+ "@docusaurus/theme-common": "~3.10.0",
+ "@docusaurus/theme-mermaid": "~3.10.0",
"@mdi/js": "^7.3.67",
"@mdi/react": "^1.6.1",
"@mdx-js/react": "^3.0.0",
@@ -36,9 +36,9 @@
"url": "^0.11.0"
},
"devDependencies": {
- "@docusaurus/module-type-aliases": "~3.9.0",
+ "@docusaurus/module-type-aliases": "~3.10.0",
"@docusaurus/tsconfig": "^3.10.0",
- "@docusaurus/types": "^3.7.0",
+ "@docusaurus/types": "^3.10.0",
"prettier": "^3.7.4",
"typescript": "^6.0.0"
},
@@ -58,6 +58,6 @@
"node": ">=20"
},
"volta": {
- "node": "24.14.1"
+ "node": "24.15.0"
}
}
diff --git a/e2e/.nvmrc b/e2e/.nvmrc
index 8e35034890..5bf4400f22 100644
--- a/e2e/.nvmrc
+++ b/e2e/.nvmrc
@@ -1 +1 @@
-24.14.1
+24.15.0
diff --git a/e2e/package.json b/e2e/package.json
index 6b72c1b36d..a58c709a6d 100644
--- a/e2e/package.json
+++ b/e2e/package.json
@@ -58,6 +58,6 @@
"vitest": "^4.0.0"
},
"volta": {
- "node": "24.14.1"
+ "node": "24.15.0"
}
}
diff --git a/e2e/src/responses.ts b/e2e/src/responses.ts
index 3d7971d6f0..5fd887c44b 100644
--- a/e2e/src/responses.ts
+++ b/e2e/src/responses.ts
@@ -2,82 +2,44 @@ import { expect } from 'vitest';
export const errorDto = {
unauthorized: {
- error: 'Unauthorized',
- statusCode: 401,
message: 'Authentication required',
- correlationId: expect.any(String),
},
unauthorizedWithMessage: (message: string) => ({
- error: 'Unauthorized',
- statusCode: 401,
message,
- correlationId: expect.any(String),
}),
forbidden: {
- error: 'Forbidden',
- statusCode: 403,
message: expect.any(String),
- correlationId: expect.any(String),
},
missingPermission: (permission: string) => ({
- error: 'Forbidden',
- statusCode: 403,
message: `Missing required permission: ${permission}`,
- correlationId: expect.any(String),
}),
wrongPassword: {
- error: 'Bad Request',
- statusCode: 400,
message: 'Wrong password',
- correlationId: expect.any(String),
},
invalidToken: {
- error: 'Unauthorized',
- statusCode: 401,
message: 'Invalid user token',
- correlationId: expect.any(String),
},
invalidShareKey: {
- error: 'Unauthorized',
- statusCode: 401,
message: 'Invalid share key',
- correlationId: expect.any(String),
},
passwordRequired: {
- error: 'Unauthorized',
- statusCode: 401,
message: 'Password required',
- correlationId: expect.any(String),
},
badRequest: (message: any = null) => ({
- error: 'Bad Request',
- statusCode: 400,
message: message ?? expect.anything(),
- correlationId: expect.any(String),
+ }),
+ validationError: (errors?: ReadonlyArray<{ path: ReadonlyArray; message: string }>) => ({
+ message: 'Validation failed',
+ errors: errors ? expect.arrayContaining(errors.map((e) => expect.objectContaining(e))) : expect.any(Array),
}),
noPermission: {
- error: 'Bad Request',
- statusCode: 400,
message: expect.stringContaining('Not found or no'),
- correlationId: expect.any(String),
},
incorrectLogin: {
- error: 'Unauthorized',
- statusCode: 401,
message: 'Incorrect email or password',
- correlationId: expect.any(String),
},
alreadyHasAdmin: {
- error: 'Bad Request',
- statusCode: 400,
message: 'The server already has an admin',
- correlationId: expect.any(String),
- },
- invalidEmail: {
- error: 'Bad Request',
- statusCode: 400,
- message: ['email must be an email'],
- correlationId: expect.any(String),
},
};
diff --git a/e2e/src/specs/server/api/album.e2e-spec.ts b/e2e/src/specs/server/api/album.e2e-spec.ts
index 3725de8d26..e1e5178476 100644
--- a/e2e/src/specs/server/api/album.e2e-spec.ts
+++ b/e2e/src/specs/server/api/album.e2e-spec.ts
@@ -154,23 +154,31 @@ describe('/albums', () => {
expect(body).toEqual(
expect.arrayContaining([
expect.objectContaining({
- ownerId: user1.userId,
albumName: user1SharedLink,
+ albumUsers: expect.arrayContaining([
+ { role: AlbumUserRole.Owner, user: expect.objectContaining({ id: user1.userId }) },
+ ]),
shared: true,
}),
expect.objectContaining({
- ownerId: user1.userId,
albumName: user1SharedEditorUser,
+ albumUsers: expect.arrayContaining([
+ { role: AlbumUserRole.Owner, user: expect.objectContaining({ id: user1.userId }) },
+ ]),
shared: true,
}),
expect.objectContaining({
- ownerId: user1.userId,
albumName: user1SharedViewerUser,
+ albumUsers: expect.arrayContaining([
+ { role: AlbumUserRole.Owner, user: expect.objectContaining({ id: user1.userId }) },
+ ]),
shared: true,
}),
expect.objectContaining({
- ownerId: user2.userId,
albumName: user2SharedUser,
+ albumUsers: expect.arrayContaining([
+ { role: AlbumUserRole.Owner, user: expect.objectContaining({ id: user2.userId }) },
+ ]),
shared: true,
}),
]),
@@ -184,23 +192,31 @@ describe('/albums', () => {
expect(body).toEqual(
expect.arrayContaining([
expect.objectContaining({
- ownerId: user1.userId,
albumName: user1SharedEditorUser,
+ albumUsers: expect.arrayContaining([
+ { role: AlbumUserRole.Owner, user: expect.objectContaining({ id: user1.userId }) },
+ ]),
shared: true,
}),
expect.objectContaining({
- ownerId: user1.userId,
albumName: user1SharedViewerUser,
+ albumUsers: expect.arrayContaining([
+ { role: AlbumUserRole.Owner, user: expect.objectContaining({ id: user1.userId }) },
+ ]),
shared: true,
}),
expect.objectContaining({
- ownerId: user1.userId,
albumName: user1SharedLink,
+ albumUsers: expect.arrayContaining([
+ { role: AlbumUserRole.Owner, user: expect.objectContaining({ id: user1.userId }) },
+ ]),
shared: true,
}),
expect.objectContaining({
- ownerId: user1.userId,
albumName: user1NotShared,
+ albumUsers: expect.arrayContaining([
+ { role: AlbumUserRole.Owner, user: expect.objectContaining({ id: user1.userId }) },
+ ]),
shared: false,
}),
]),
@@ -216,23 +232,31 @@ describe('/albums', () => {
expect(body).toEqual(
expect.arrayContaining([
expect.objectContaining({
- ownerId: user1.userId,
albumName: user1SharedEditorUser,
+ albumUsers: expect.arrayContaining([
+ { role: AlbumUserRole.Owner, user: expect.objectContaining({ id: user1.userId }) },
+ ]),
shared: true,
}),
expect.objectContaining({
- ownerId: user1.userId,
albumName: user1SharedViewerUser,
+ albumUsers: expect.arrayContaining([
+ { role: AlbumUserRole.Owner, user: expect.objectContaining({ id: user1.userId }) },
+ ]),
shared: true,
}),
expect.objectContaining({
- ownerId: user1.userId,
albumName: user1SharedLink,
+ albumUsers: expect.arrayContaining([
+ { role: AlbumUserRole.Owner, user: expect.objectContaining({ id: user1.userId }) },
+ ]),
shared: true,
}),
expect.objectContaining({
- ownerId: user2.userId,
albumName: user2SharedUser,
+ albumUsers: expect.arrayContaining([
+ { role: AlbumUserRole.Owner, user: expect.objectContaining({ id: user2.userId }) },
+ ]),
shared: true,
}),
]),
@@ -248,8 +272,10 @@ describe('/albums', () => {
expect(body).toEqual(
expect.arrayContaining([
expect.objectContaining({
- ownerId: user1.userId,
albumName: user1NotShared,
+ albumUsers: expect.arrayContaining([
+ { role: AlbumUserRole.Owner, user: expect.objectContaining({ id: user1.userId }) },
+ ]),
shared: false,
}),
]),
@@ -286,13 +312,17 @@ describe('/albums', () => {
expect(body).toEqual(
expect.arrayContaining([
expect.objectContaining({
- ownerId: user4.userId,
albumName: user4DeletedAsset,
+ albumUsers: expect.arrayContaining([
+ { role: AlbumUserRole.Owner, user: expect.objectContaining({ id: user4.userId }) },
+ ]),
shared: false,
}),
expect.objectContaining({
- ownerId: user4.userId,
albumName: user4Empty,
+ albumUsers: expect.arrayContaining([
+ { role: AlbumUserRole.Owner, user: expect.objectContaining({ id: user4.userId }) },
+ ]),
shared: false,
}),
]),
@@ -362,16 +392,17 @@ describe('/albums', () => {
.set('Authorization', `Bearer ${user1.accessToken}`);
expect(status).toBe(200);
- expect(body).toEqual({
- ...user2Albums[0],
- contributorCounts: [{ userId: user1.userId, assetCount: 1 }],
- assetCount: 1,
- lastModifiedAssetTimestamp: expect.any(String),
- endDate: expect.any(String),
- startDate: expect.any(String),
- albumUsers: expect.any(Array),
- shared: true,
- });
+ expect(body).toEqual(
+ expect.objectContaining({
+ contributorCounts: [{ userId: user1.userId, assetCount: 1 }],
+ assetCount: 1,
+ lastModifiedAssetTimestamp: expect.any(String),
+ endDate: expect.any(String),
+ startDate: expect.any(String),
+ albumUsers: expect.any(Array),
+ shared: true,
+ }),
+ );
});
});
@@ -397,15 +428,13 @@ describe('/albums', () => {
id: expect.any(String),
createdAt: expect.any(String),
updatedAt: expect.any(String),
- ownerId: user1.userId,
albumName: 'New album',
description: '',
albumThumbnailAssetId: null,
shared: false,
- albumUsers: [],
+ albumUsers: [{ role: AlbumUserRole.Owner, user: expect.objectContaining({ id: user1.userId }) }],
hasSharedLink: false,
assetCount: 0,
- owner: expect.objectContaining({ email: user1.userEmail }),
isActivityEnabled: true,
order: AssetOrder.Desc,
});
@@ -621,11 +650,11 @@ describe('/albums', () => {
expect(status).toBe(200);
expect(body).toEqual(
expect.objectContaining({
- albumUsers: [
+ albumUsers: expect.arrayContaining([
expect.objectContaining({
user: expect.objectContaining({ id: user2.userId }),
}),
- ],
+ ]),
}),
);
});
@@ -637,7 +666,7 @@ describe('/albums', () => {
.send({ albumUsers: [{ userId: user1.userId, role: AlbumUserRole.Editor }] });
expect(status).toBe(400);
- expect(body).toEqual(errorDto.badRequest('Cannot be shared with owner'));
+ expect(body).toEqual(errorDto.badRequest('User already added'));
});
it('should not be able to add existing user to shared album', async () => {
@@ -663,7 +692,7 @@ describe('/albums', () => {
albumUsers: [{ userId: user2.userId, role: AlbumUserRole.Viewer }],
});
- expect(album.albumUsers[0].role).toEqual(AlbumUserRole.Viewer);
+ expect(album.albumUsers[1].role).toEqual(AlbumUserRole.Viewer);
const { status } = await request(app)
.put(`/albums/${album.id}/user/${user2.userId}`)
@@ -678,7 +707,10 @@ describe('/albums', () => {
.set('Authorization', `Bearer ${user1.accessToken}`);
expect(body).toEqual(
expect.objectContaining({
- albumUsers: [expect.objectContaining({ role: AlbumUserRole.Editor })],
+ albumUsers: [
+ expect.objectContaining({ role: AlbumUserRole.Owner }),
+ expect.objectContaining({ role: AlbumUserRole.Editor }),
+ ],
}),
);
});
@@ -689,7 +721,7 @@ describe('/albums', () => {
albumUsers: [{ userId: user2.userId, role: AlbumUserRole.Viewer }],
});
- expect(album.albumUsers[0].role).toEqual(AlbumUserRole.Viewer);
+ expect(album.albumUsers[1].role).toEqual(AlbumUserRole.Viewer);
const { status, body } = await request(app)
.put(`/albums/${album.id}/user/${user2.userId}`)
diff --git a/e2e/src/specs/server/api/library.e2e-spec.ts b/e2e/src/specs/server/api/library.e2e-spec.ts
index 719436a66d..ccb594610c 100644
--- a/e2e/src/specs/server/api/library.e2e-spec.ts
+++ b/e2e/src/specs/server/api/library.e2e-spec.ts
@@ -110,7 +110,9 @@ describe('/libraries', () => {
});
expect(status).toBe(400);
- expect(body).toEqual(errorDto.badRequest(['[importPaths] Array must have unique items']));
+ expect(body).toEqual(
+ errorDto.validationError([{ path: ['importPaths'], message: 'Array must have unique items' }]),
+ );
});
it('should not create an external library with duplicate exclusion patterns', async () => {
@@ -125,7 +127,9 @@ describe('/libraries', () => {
});
expect(status).toBe(400);
- expect(body).toEqual(errorDto.badRequest(['[exclusionPatterns] Array must have unique items']));
+ expect(body).toEqual(
+ errorDto.validationError([{ path: ['exclusionPatterns'], message: 'Array must have unique items' }]),
+ );
});
});
@@ -157,7 +161,9 @@ describe('/libraries', () => {
.send({ name: '' });
expect(status).toBe(400);
- expect(body).toEqual(errorDto.badRequest(['[name] Too small: expected string to have >=1 characters']));
+ expect(body).toEqual(
+ errorDto.validationError([{ path: ['name'], message: 'Too small: expected string to have >=1 characters' }]),
+ );
});
it('should change the import paths', async () => {
@@ -181,7 +187,9 @@ describe('/libraries', () => {
.send({ importPaths: [''] });
expect(status).toBe(400);
- expect(body).toEqual(errorDto.badRequest(['[importPaths] Array items must not be empty']));
+ expect(body).toEqual(
+ errorDto.validationError([{ path: ['importPaths'], message: 'Array items must not be empty' }]),
+ );
});
it('should reject duplicate import paths', async () => {
@@ -191,7 +199,9 @@ describe('/libraries', () => {
.send({ importPaths: ['/path', '/path'] });
expect(status).toBe(400);
- expect(body).toEqual(errorDto.badRequest(['[importPaths] Array must have unique items']));
+ expect(body).toEqual(
+ errorDto.validationError([{ path: ['importPaths'], message: 'Array must have unique items' }]),
+ );
});
it('should change the exclusion pattern', async () => {
@@ -215,7 +225,9 @@ describe('/libraries', () => {
.send({ exclusionPatterns: ['**/*.jpg', '**/*.jpg'] });
expect(status).toBe(400);
- expect(body).toEqual(errorDto.badRequest(['[exclusionPatterns] Array must have unique items']));
+ expect(body).toEqual(
+ errorDto.validationError([{ path: ['exclusionPatterns'], message: 'Array must have unique items' }]),
+ );
});
it('should reject an empty exclusion pattern', async () => {
@@ -225,7 +237,9 @@ describe('/libraries', () => {
.send({ exclusionPatterns: [''] });
expect(status).toBe(400);
- expect(body).toEqual(errorDto.badRequest(['[exclusionPatterns] Array items must not be empty']));
+ expect(body).toEqual(
+ errorDto.validationError([{ path: ['exclusionPatterns'], message: 'Array items must not be empty' }]),
+ );
});
});
diff --git a/e2e/src/specs/server/api/map.e2e-spec.ts b/e2e/src/specs/server/api/map.e2e-spec.ts
index c280deb134..86664b2dc4 100644
--- a/e2e/src/specs/server/api/map.e2e-spec.ts
+++ b/e2e/src/specs/server/api/map.e2e-spec.ts
@@ -109,7 +109,9 @@ describe('/map', () => {
.get('/map/reverse-geocode?lon=123')
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(400);
- expect(body).toEqual(errorDto.badRequest(['[lat] Invalid input: expected number, received NaN']));
+ expect(body).toEqual(
+ errorDto.validationError([{ path: ['lat'], message: 'Invalid input: expected number, received NaN' }]),
+ );
});
it('should throw an error if a lat is not a number', async () => {
@@ -117,7 +119,9 @@ describe('/map', () => {
.get('/map/reverse-geocode?lat=abc&lon=123.456')
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(400);
- expect(body).toEqual(errorDto.badRequest(['[lat] Invalid input: expected number, received NaN']));
+ expect(body).toEqual(
+ errorDto.validationError([{ path: ['lat'], message: 'Invalid input: expected number, received NaN' }]),
+ );
});
it('should throw an error if a lat is out of range', async () => {
@@ -125,7 +129,9 @@ describe('/map', () => {
.get('/map/reverse-geocode?lat=91&lon=123.456')
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(400);
- expect(body).toEqual(errorDto.badRequest(['[lat] Too big: expected number to be <=90']));
+ expect(body).toEqual(
+ errorDto.validationError([{ path: ['lat'], message: 'Too big: expected number to be <=90' }]),
+ );
});
it('should throw an error if a lon is not provided', async () => {
@@ -133,7 +139,9 @@ describe('/map', () => {
.get('/map/reverse-geocode?lat=75')
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(400);
- expect(body).toEqual(errorDto.badRequest(['[lon] Invalid input: expected number, received NaN']));
+ expect(body).toEqual(
+ errorDto.validationError([{ path: ['lon'], message: 'Invalid input: expected number, received NaN' }]),
+ );
});
const reverseGeocodeTestCases = [
diff --git a/e2e/src/specs/server/api/oauth.e2e-spec.ts b/e2e/src/specs/server/api/oauth.e2e-spec.ts
index 9dcb431a4b..4bf4f197b1 100644
--- a/e2e/src/specs/server/api/oauth.e2e-spec.ts
+++ b/e2e/src/specs/server/api/oauth.e2e-spec.ts
@@ -105,7 +105,11 @@ describe(`/oauth`, () => {
it(`should throw an error if a redirect uri is not provided`, async () => {
const { status, body } = await request(app).post('/oauth/authorize').send({});
expect(status).toBe(400);
- expect(body).toEqual(errorDto.badRequest(['[redirectUri] Invalid input: expected string, received undefined']));
+ expect(body).toEqual(
+ errorDto.validationError([
+ { path: ['redirectUri'], message: 'Invalid input: expected string, received undefined' },
+ ]),
+ );
});
it('should return a redirect uri', async () => {
@@ -164,13 +168,17 @@ describe(`/oauth`, () => {
it(`should throw an error if a url is not provided`, async () => {
const { status, body } = await request(app).post('/oauth/callback').send({});
expect(status).toBe(400);
- expect(body).toEqual(errorDto.badRequest(['[url] Invalid input: expected string, received undefined']));
+ expect(body).toEqual(
+ errorDto.validationError([{ path: ['url'], message: 'Invalid input: expected string, received undefined' }]),
+ );
});
it(`should throw an error if the url is empty`, async () => {
const { status, body } = await request(app).post('/oauth/callback').send({ url: '' });
expect(status).toBe(400);
- expect(body).toEqual(errorDto.badRequest(['[url] Too small: expected string to have >=1 characters']));
+ expect(body).toEqual(
+ errorDto.validationError([{ path: ['url'], message: 'Too small: expected string to have >=1 characters' }]),
+ );
});
it(`should throw an error if the state is not provided`, async () => {
@@ -332,9 +340,7 @@ describe(`/oauth`, () => {
const { status, body } = await request(app).post('/oauth/callback').send(callbackParams);
expect(status).toBe(500);
expect(body).toMatchObject({
- error: 'Internal Server Error',
message: 'Failed to finish oauth',
- statusCode: 500,
});
});
@@ -353,7 +359,7 @@ describe(`/oauth`, () => {
const callbackParams = await loginWithOAuth('oauth-no-auto-register');
const { status, body } = await request(app).post('/oauth/callback').send(callbackParams);
expect(status).toBe(400);
- expect(body).toEqual(errorDto.badRequest('User does not exist and auto registering is disabled.'));
+ expect(body).toEqual(errorDto.badRequest('OAuth authentication failed'));
});
it('should link to an existing user by email', async () => {
@@ -377,7 +383,11 @@ describe(`/oauth`, () => {
it(`should throw an error if the logout_token is not provided`, async () => {
const { status, body } = await request(app).post('/oauth/backchannel-logout').send({});
expect(status).toBe(400);
- expect(body).toEqual(errorDto.badRequest(['[logout_token] Invalid input: expected string, received undefined']));
+ expect(body).toEqual(
+ errorDto.validationError([
+ { path: ['logout_token'], message: 'Invalid input: expected string, received undefined' },
+ ]),
+ );
});
it(`should throw an error if an invalid logout token is provided`, async () => {
@@ -495,11 +505,10 @@ describe(`/oauth`, () => {
});
it('should reject OAuth discovery over HTTP', async () => {
- const { status, body } = await request(app)
+ const { status } = await request(app)
.post('/oauth/authorize')
.send({ redirectUri: 'http://127.0.0.1:2285/auth/login' });
expect(status).toBe(500);
- expect(body).toMatchObject({ statusCode: 500 });
});
});
});
diff --git a/e2e/src/specs/server/api/shared-link.e2e-spec.ts b/e2e/src/specs/server/api/shared-link.e2e-spec.ts
index 1d069d0f54..8cdf2dc03c 100644
--- a/e2e/src/specs/server/api/shared-link.e2e-spec.ts
+++ b/e2e/src/specs/server/api/shared-link.e2e-spec.ts
@@ -341,7 +341,9 @@ describe('/shared-links', () => {
.set('Authorization', `Bearer ${user1.accessToken}`);
expect(status).toBe(400);
- expect(body).toEqual(errorDto.badRequest());
+ expect(body).toEqual(
+ errorDto.validationError([{ path: [], message: 'Invalid input: expected object, received undefined' }]),
+ );
});
it('should require an asset/album id', async () => {
diff --git a/e2e/src/specs/server/api/stack.e2e-spec.ts b/e2e/src/specs/server/api/stack.e2e-spec.ts
index 91dd0d2a8e..76bf514dc8 100644
--- a/e2e/src/specs/server/api/stack.e2e-spec.ts
+++ b/e2e/src/specs/server/api/stack.e2e-spec.ts
@@ -41,7 +41,9 @@ describe('/stacks', () => {
.send({ assetIds: [asset.id] });
expect(status).toBe(400);
- expect(body).toEqual(errorDto.badRequest());
+ expect(body).toEqual(
+ errorDto.validationError([{ path: ['assetIds'], message: 'Too small: expected array to have >=2 items' }]),
+ );
});
it('should require a valid id', async () => {
@@ -51,7 +53,12 @@ describe('/stacks', () => {
.send({ assetIds: [uuidDto.invalid, uuidDto.invalid] });
expect(status).toBe(400);
- expect(body).toEqual(errorDto.badRequest());
+ expect(body).toEqual(
+ errorDto.validationError([
+ { path: ['assetIds', 0], message: 'Invalid UUID' },
+ { path: ['assetIds', 1], message: 'Invalid UUID' },
+ ]),
+ );
});
it('should require access', async () => {
diff --git a/e2e/src/specs/server/api/tag.e2e-spec.ts b/e2e/src/specs/server/api/tag.e2e-spec.ts
index 7b5a2f16de..d303a1e98d 100644
--- a/e2e/src/specs/server/api/tag.e2e-spec.ts
+++ b/e2e/src/specs/server/api/tag.e2e-spec.ts
@@ -309,7 +309,7 @@ describe('/tags', () => {
.get(`/tags/${uuidDto.invalid}`)
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(400);
- expect(body).toEqual(errorDto.badRequest(['[id] Invalid UUID']));
+ expect(body).toEqual(errorDto.validationError([{ path: ['id'], message: 'Invalid UUID' }]));
});
it('should get tag details', async () => {
@@ -427,7 +427,7 @@ describe('/tags', () => {
.delete(`/tags/${uuidDto.invalid}`)
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(400);
- expect(body).toEqual(errorDto.badRequest(['[id] Invalid UUID']));
+ expect(body).toEqual(errorDto.validationError([{ path: ['id'], message: 'Invalid UUID' }]));
});
it('should delete a tag', async () => {
diff --git a/e2e/src/specs/server/api/user-admin.e2e-spec.ts b/e2e/src/specs/server/api/user-admin.e2e-spec.ts
index 6751b21e84..df6fea84bc 100644
--- a/e2e/src/specs/server/api/user-admin.e2e-spec.ts
+++ b/e2e/src/specs/server/api/user-admin.e2e-spec.ts
@@ -108,14 +108,20 @@ describe('/admin/users', () => {
expect(body).toEqual(errorDto.forbidden);
});
- for (const key of ['password', 'email', 'name', 'quotaSizeInBytes', 'shouldChangePassword', 'notify']) {
+ for (const [key, message] of [
+ ['password', 'Invalid input: expected string, received null'],
+ ['email', 'Invalid input: expected email, received object'],
+ ['name', 'Invalid input: expected string, received null'],
+ ['shouldChangePassword', 'Invalid input: expected boolean, received null'],
+ ['notify', 'Invalid input: expected boolean, received null'],
+ ] as const) {
it(`should not allow null ${key}`, async () => {
const { status, body } = await request(app)
.post(`/admin/users`)
.set('Authorization', `Bearer ${admin.accessToken}`)
.send({ ...createUserDto.user1, [key]: null });
expect(status).toBe(400);
- expect(body).toEqual(errorDto.badRequest());
+ expect(body).toEqual(errorDto.validationError([{ path: [key], message }]));
});
}
@@ -153,14 +159,19 @@ describe('/admin/users', () => {
expect(body).toEqual(errorDto.forbidden);
});
- for (const key of ['password', 'email', 'name', 'shouldChangePassword']) {
+ for (const [key, message] of [
+ ['password', 'Invalid input: expected string, received null'],
+ ['email', 'Invalid input: expected email, received object'],
+ ['name', 'Invalid input: expected string, received null'],
+ ['shouldChangePassword', 'Invalid input: expected boolean, received null'],
+ ] as const) {
it(`should not allow null ${key}`, async () => {
const { status, body } = await request(app)
.put(`/admin/users/${uuidDto.notFound}`)
.set('Authorization', `Bearer ${admin.accessToken}`)
.send({ [key]: null });
expect(status).toBe(400);
- expect(body).toEqual(errorDto.badRequest());
+ expect(body).toEqual(errorDto.validationError([{ path: [key], message }]));
});
}
diff --git a/e2e/src/specs/server/api/user.e2e-spec.ts b/e2e/src/specs/server/api/user.e2e-spec.ts
index ee13a29c1b..8a2197efde 100644
--- a/e2e/src/specs/server/api/user.e2e-spec.ts
+++ b/e2e/src/specs/server/api/user.e2e-spec.ts
@@ -120,7 +120,7 @@ describe('/users', () => {
.set('Authorization', `Bearer ${nonAdmin.accessToken}`);
expect(status).toBe(400);
- expect(body).toMatchObject(errorDto.badRequest('Email already in use by another account'));
+ expect(body).toMatchObject(errorDto.badRequest('Email is not available'));
});
it('should update my email', async () => {
@@ -179,7 +179,9 @@ describe('/users', () => {
expect(status).toBe(400);
expect(body).toEqual(
- errorDto.badRequest(['[download.archiveSize] Invalid input: expected int, received number']),
+ errorDto.validationError([
+ { path: ['download', 'archiveSize'], message: 'Invalid input: expected int, received number' },
+ ]),
);
});
@@ -207,7 +209,9 @@ describe('/users', () => {
expect(status).toBe(400);
expect(body).toEqual(
- errorDto.badRequest(['[download.includeEmbeddedVideos] Invalid input: expected boolean, received number']),
+ errorDto.validationError([
+ { path: ['download', 'includeEmbeddedVideos'], message: 'Invalid input: expected boolean, received number' },
+ ]),
);
});
diff --git a/e2e/src/ui/generators/timeline/model-objects.ts b/e2e/src/ui/generators/timeline/model-objects.ts
index e300de1161..f5654afd5e 100644
--- a/e2e/src/ui/generators/timeline/model-objects.ts
+++ b/e2e/src/ui/generators/timeline/model-objects.ts
@@ -32,8 +32,12 @@ export function generateThumbhash(rng: SeededRandom): string {
return Array.from({ length: 10 }, () => rng.nextInt(0, 256).toString(16).padStart(2, '0')).join('');
}
-export function generateDuration(rng: SeededRandom): string {
- return `${rng.nextInt(GENERATION_CONSTANTS.MIN_VIDEO_DURATION_SECONDS, GENERATION_CONSTANTS.MAX_VIDEO_DURATION_SECONDS)}.${rng.nextInt(0, 1000).toString().padStart(3, '0')}`;
+export function generateDuration(rng: SeededRandom): number {
+ return (
+ rng.nextInt(GENERATION_CONSTANTS.MIN_VIDEO_DURATION_SECONDS, GENERATION_CONSTANTS.MAX_VIDEO_DURATION_SECONDS) *
+ 1000 +
+ rng.nextInt(0, 1000)
+ );
}
export function generateUUID(): string {
diff --git a/e2e/src/ui/generators/timeline/rest-response.ts b/e2e/src/ui/generators/timeline/rest-response.ts
index 8fc9ce331d..83a60556be 100644
--- a/e2e/src/ui/generators/timeline/rest-response.ts
+++ b/e2e/src/ui/generators/timeline/rest-response.ts
@@ -3,6 +3,7 @@
*/
import {
+ AlbumUserRole,
AssetTypeEnum,
AssetVisibility,
UserAvatarColor,
@@ -420,9 +421,7 @@ export function getAlbum(
albumThumbnailAssetId: album.thumbnailAssetId,
createdAt: album.createdAt,
updatedAt: album.updatedAt,
- ownerId: albumOwner.id,
- owner: albumOwner,
- albumUsers: [], // Empty array for non-shared album
+ albumUsers: [{ user: albumOwner, role: AlbumUserRole.Owner }],
shared: false,
hasSharedLink: false,
isActivityEnabled: true,
diff --git a/e2e/src/ui/generators/timeline/timeline-config.ts b/e2e/src/ui/generators/timeline/timeline-config.ts
index 992480eef9..4dea2f4f78 100644
--- a/e2e/src/ui/generators/timeline/timeline-config.ts
+++ b/e2e/src/ui/generators/timeline/timeline-config.ts
@@ -43,7 +43,7 @@ export type MockTimelineAsset = {
isTrashed: boolean;
isVideo: boolean;
isImage: boolean;
- duration: string | null;
+ duration: number | null;
projectionType: string | null;
livePhotoVideoId: string | null;
city: string | null;
diff --git a/e2e/src/ui/mock-network/base-network.ts b/e2e/src/ui/mock-network/base-network.ts
index 7c4aee59e3..3dc3580396 100644
--- a/e2e/src/ui/mock-network/base-network.ts
+++ b/e2e/src/ui/mock-network/base-network.ts
@@ -223,6 +223,7 @@ export const setupBaseMockApiRoutes = async (context: BrowserContext, adminUserI
'.jp2',
'.jpe',
'.jxl',
+ '.mpo',
'.svg',
'.tif',
'.tiff',
diff --git a/e2e/src/ui/specs/timeline/timeline.e2e-spec.ts b/e2e/src/ui/specs/timeline/timeline.e2e-spec.ts
index 6a7ce82672..c2a3b8e724 100644
--- a/e2e/src/ui/specs/timeline/timeline.e2e-spec.ts
+++ b/e2e/src/ui/specs/timeline/timeline.e2e-spec.ts
@@ -304,7 +304,7 @@ test.describe('Timeline', () => {
await page.keyboard.down('Shift');
await thumbnailUtils.withAssetId(page, assets[2].id).hover();
await expect(
- thumbnailUtils.locator(page).locator('.absolute.top-0.h-full.w-full.bg-immich-primary.opacity-40'),
+ thumbnailUtils.locator(page).locator('.absolute.top-0.size-full.bg-immich-primary.opacity-40'),
).toHaveCount(3);
await thumbnailUtils.selectButton(page, assets[2].id).click();
await page.keyboard.up('Shift');
@@ -349,7 +349,7 @@ test.describe('Timeline', () => {
expect(visibleMockAssetsYearMonths).toContain(month);
}
});
- test('Deep link to last photo, scroll up', async ({ page }) => {
+ test.skip('Deep link to last photo, scroll up', async ({ page }) => {
const lastAsset = assets.at(-1)!;
await pageUtils.deepLinkPhotosPage(page, lastAsset.id);
@@ -361,7 +361,7 @@ test.describe('Timeline', () => {
await thumbnailUtils.expectInViewport(page, '14e5901f-fd7f-40c0-b186-4d7e7fc67968');
});
- test('Deep link to first bucket, scroll down', async ({ page }) => {
+ test.skip('Deep link to first bucket, scroll down', async ({ page }) => {
const lastAsset = assets.at(0)!;
await pageUtils.deepLinkPhotosPage(page, lastAsset.id);
await timelineUtils.locator(page).hover();
@@ -440,7 +440,7 @@ test.describe('Timeline', () => {
await thumbnailUtils.expectInViewport(page, asset.id);
await thumbnailUtils.expectSelectedDisabled(page, asset.id);
});
- test('Add photos to album', async ({ page }) => {
+ test.skip('Add photos to album', async ({ page }) => {
const album = timelineRestData.album;
await pageUtils.openAlbumPage(page, album.id);
await page.locator('nav button[aria-label="Add photos"]').click();
@@ -752,7 +752,7 @@ test.describe('Timeline', () => {
await page.getByText('Photos', { exact: true }).click();
await thumbnailUtils.expectInViewport(page, assetToFavorite.id);
});
- test('open /favorites, archive photo, unarchive photo', async ({ page }) => {
+ test.skip('open /favorites, archive photo, unarchive photo', async ({ page }) => {
await pageUtils.openFavorites(page);
const assetToArchive = getAsset(timelineRestData, 'ad31e29f-2069-4574-b9a9-ad86523c92cb')!;
await thumbnailUtils.withAssetId(page, assetToArchive.id).hover();
diff --git a/e2e/test-assets b/e2e/test-assets
index 0eac5a3738..6742055402 160000
--- a/e2e/test-assets
+++ b/e2e/test-assets
@@ -1 +1 @@
-Subproject commit 0eac5a37384c151be88381b41f9e28d8d59a4466
+Subproject commit 6742055402de1aa48f93d12ded7d18f4057f9d1f
diff --git a/i18n/en.json b/i18n/en.json
index add755c05d..cc30d9e350 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -1761,6 +1761,7 @@
"play_original_video": "Play original video",
"play_original_video_setting_description": "Prefer playback of original videos rather than transcoded videos. If original asset is not compatible it may not playback correctly.",
"play_transcoded_video": "Play transcoded video",
+ "playback_speed": "Playback speed",
"please_auth_to_access": "Please authenticate to access",
"port": "Port",
"preferences_settings_subtitle": "Manage the app's preferences",
@@ -2436,6 +2437,7 @@
"workflows": "Workflows",
"workflows_help_text": "Workflows automate actions on your assets based on triggers and filters",
"wrong_pin_code": "Wrong PIN code",
+ "x_of_total": "{x}/{total}",
"year": "Year",
"years_ago": "{years, plural, one {# year} other {# years}} ago",
"yes": "Yes",
diff --git a/machine-learning/Dockerfile b/machine-learning/Dockerfile
index 8126ff0859..c6f9f01675 100644
--- a/machine-learning/Dockerfile
+++ b/machine-learning/Dockerfile
@@ -48,14 +48,14 @@ FROM python:3.13-slim-trixie@sha256:d168b8d9eb761f4d3fe305ebd04aeb7e7f2de0297cec
RUN apt-get update && \
apt-get install --no-install-recommends -yqq ocl-icd-libopencl1 wget && \
- wget -nv https://github.com/intel/intel-graphics-compiler/releases/download/v2.28.4/intel-igc-core-2_2.28.4+20760_amd64.deb && \
- wget -nv https://github.com/intel/intel-graphics-compiler/releases/download/v2.28.4/intel-igc-opencl-2_2.28.4+20760_amd64.deb && \
- wget -nv https://github.com/intel/compute-runtime/releases/download/26.05.37020.3/intel-opencl-icd_26.05.37020.3-0_amd64.deb && \
+ wget -nv https://github.com/intel/intel-graphics-compiler/releases/download/v2.32.7/intel-igc-core-2_2.32.7+21184_amd64.deb && \
+ wget -nv https://github.com/intel/intel-graphics-compiler/releases/download/v2.32.7/intel-igc-opencl-2_2.32.7+21184_amd64.deb && \
+ wget -nv https://github.com/intel/compute-runtime/releases/download/26.14.37833.4/intel-opencl-icd_26.14.37833.4-0_amd64.deb && \
wget -nv https://github.com/intel/intel-graphics-compiler/releases/download/igc-1.0.17537.24/intel-igc-core_1.0.17537.24_amd64.deb && \
wget -nv https://github.com/intel/intel-graphics-compiler/releases/download/igc-1.0.17537.24/intel-igc-opencl_1.0.17537.24_amd64.deb && \
wget -nv https://github.com/intel/compute-runtime/releases/download/24.35.30872.36/intel-opencl-icd-legacy1_24.35.30872.36_amd64.deb && \
# TODO: Figure out how to get renovate to manage this differently versioned libigdgmm file
- wget -nv https://github.com/intel/compute-runtime/releases/download/26.05.37020.3/libigdgmm12_22.9.0_amd64.deb && \
+ wget -nv https://github.com/intel/compute-runtime/releases/download/26.14.37833.4/libigdgmm12_22.9.0_amd64.deb && \
dpkg -i *.deb && \
rm *.deb && \
apt-get remove wget -yqq && \
@@ -68,7 +68,7 @@ ENV LD_PRELOAD=/usr/lib/libmimalloc.so.2 \
RUN apt-get update && \
# Pascal support was dropped in 9.11
- apt-get install --no-install-recommends -yqq libcudnn9-cuda-12=9.10.2.21-1 && \
+ apt-get install --no-install-recommends -yqq libcudnn9-cuda-12=9.10.2.21-1 tzdata && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
@@ -112,7 +112,7 @@ ARG RKNN_TOOLKIT_VERSION="v2.3.0"
ENV LD_PRELOAD=/usr/lib/libmimalloc.so.2 \
MACHINE_LEARNING_MODEL_ARENA=false
-ADD --checksum=sha256:73993ed4b440460825f21611731564503cc1d5a0c123746477da6cd574f34885 "https://github.com/airockchip/rknn-toolkit2/raw/refs/tags/${RKNN_TOOLKIT_VERSION}/rknpu2/runtime/Linux/librknn_api/aarch64/librknnrt.so" /usr/lib/
+ADD --chmod=644 --checksum=sha256:73993ed4b440460825f21611731564503cc1d5a0c123746477da6cd574f34885 "https://github.com/airockchip/rknn-toolkit2/raw/refs/tags/${RKNN_TOOLKIT_VERSION}/rknpu2/runtime/Linux/librknn_api/aarch64/librknnrt.so" /usr/lib/
FROM prod-${DEVICE} AS prod
diff --git a/machine-learning/immich_ml/main.py b/machine-learning/immich_ml/main.py
index e7e3a719bb..4fca7a2e2b 100644
--- a/machine-learning/immich_ml/main.py
+++ b/machine-learning/immich_ml/main.py
@@ -183,7 +183,10 @@ async def predict(
text: str | None = Form(default=None),
) -> Any:
if image is not None:
- inputs: Image | str = await run(lambda: decode_pil(image))
+ decoded = await run(lambda: decode_pil(image))
+ if decoded.width == 0 or decoded.height == 0:
+ raise HTTPException(400, "Image has zero width or height")
+ inputs: Image | str = decoded
elif text is not None:
inputs = text
else:
diff --git a/machine-learning/pyproject.toml b/machine-learning/pyproject.toml
index 640996f54a..d61df51e38 100644
--- a/machine-learning/pyproject.toml
+++ b/machine-learning/pyproject.toml
@@ -9,12 +9,12 @@ dependencies = [
"aiocache>=0.12.1,<1.0",
"fastapi>=0.95.2,<1.0",
"gunicorn>=21.1.0",
- "huggingface-hub>=0.20.1,<1.0",
+ "huggingface-hub>=1.0,<2.0",
"insightface>=0.7.3,<1.0",
"numpy<2.4.0",
"opencv-python-headless>=4.7.0.72,<5.0",
"orjson>=3.9.5",
- "pillow>=12.2,<12.3",
+ "pillow>=12.2,<13",
"pydantic>=2.0.0,<3",
"pydantic-settings>=2.5.2,<3",
"python-multipart>=0.0.6,<1.0",
diff --git a/machine-learning/test_main.py b/machine-learning/test_main.py
index 0182c57c67..cce334e40e 100644
--- a/machine-learning/test_main.py
+++ b/machine-learning/test_main.py
@@ -1198,6 +1198,19 @@ class TestLoad:
mock_model.model_format = ModelFormat.ONNX
+@pytest.mark.parametrize("size", [(0, 100), (100, 0), (0, 0)])
+def test_predict_rejects_empty_image(size: tuple[int, int], deployed_app: TestClient) -> None:
+ with mock.patch("immich_ml.main.decode_pil", return_value=Image.new("RGB", size)):
+ response = deployed_app.post(
+ "http://localhost:3003/predict",
+ data={"entries": json.dumps({"clip": {"visual": {"modelName": "ViT-B-32__openai"}}})},
+ files={"image": b"fake image bytes"},
+ )
+
+ assert response.status_code == 400
+ assert "zero" in response.json()["detail"].lower()
+
+
def test_root_endpoint(deployed_app: TestClient) -> None:
response = deployed_app.get("http://localhost:3003")
diff --git a/mise.toml b/mise.toml
index c4700fd924..7fa3473d62 100644
--- a/mise.toml
+++ b/mise.toml
@@ -14,18 +14,27 @@ config_roots = [
]
[tools]
-node = "24.14.1"
-flutter = "3.41.6"
-pnpm = "10.33.0"
-terragrunt = "1.0.0"
-opentofu = "1.11.5"
+node = "24.15.0"
+flutter = "3.41.7"
+pnpm = "10.33.1"
+terragrunt = "1.0.2"
+opentofu = "1.11.6"
java = "21.0.2"
[tools."github:CQLabs/homebrew-dcm"]
-version = "1.35.1"
+version = "1.37.0"
bin = "dcm"
postinstall = "chmod +x $MISE_TOOL_INSTALL_PATH/dcm"
+[tools."github:jellyfin/jellyfin-ffmpeg"]
+version = "7.1.3-6"
+
+[tools."github:jellyfin/jellyfin-ffmpeg".platforms]
+linux-x64 = { asset_pattern = "jellyfin-ffmpeg_*_portable_linux64-gpl.tar.xz" }
+linux-arm64 = { asset_pattern = "jellyfin-ffmpeg_*_portable_linuxarm64-gpl.tar.xz" }
+macos-x64 = { asset_pattern = "jellyfin-ffmpeg_*_portable_mac64-gpl.tar.xz" }
+macos-arm64 = { asset_pattern = "jellyfin-ffmpeg_*_portable_macarm64-gpl.tar.xz" }
+
[settings]
experimental = true
pin = true
diff --git a/mobile/.vscode/settings.json b/mobile/.vscode/settings.json
index 051c18ce6a..517086e98a 100644
--- a/mobile/.vscode/settings.json
+++ b/mobile/.vscode/settings.json
@@ -1,5 +1,5 @@
{
- "dart.flutterSdkPath": ".fvm/versions/3.41.7",
+ "dart.flutterSdkPath": ".fvm/versions/3.41.9",
"dart.lineLength": 120,
"[dart]": {
"editor.rulers": [
diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/Messages.g.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/Messages.g.kt
index 949aa03734..b49664dea5 100644
--- a/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/Messages.g.kt
+++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/Messages.g.kt
@@ -216,7 +216,7 @@ data class PlatformAsset (
val updatedAt: Long? = null,
val width: Long? = null,
val height: Long? = null,
- val durationInSeconds: Long,
+ val durationMs: Long,
val orientation: Long,
val isFavorite: Boolean,
val adjustmentTime: Long? = null,
@@ -234,14 +234,14 @@ data class PlatformAsset (
val updatedAt = pigeonVar_list[4] as Long?
val width = pigeonVar_list[5] as Long?
val height = pigeonVar_list[6] as Long?
- val durationInSeconds = pigeonVar_list[7] as Long
+ val durationMs = pigeonVar_list[7] as Long
val orientation = pigeonVar_list[8] as Long
val isFavorite = pigeonVar_list[9] as Boolean
val adjustmentTime = pigeonVar_list[10] as Long?
val latitude = pigeonVar_list[11] as Double?
val longitude = pigeonVar_list[12] as Double?
val playbackStyle = pigeonVar_list[13] as PlatformAssetPlaybackStyle
- return PlatformAsset(id, name, type, createdAt, updatedAt, width, height, durationInSeconds, orientation, isFavorite, adjustmentTime, latitude, longitude, playbackStyle)
+ return PlatformAsset(id, name, type, createdAt, updatedAt, width, height, durationMs, orientation, isFavorite, adjustmentTime, latitude, longitude, playbackStyle)
}
}
fun toList(): List {
@@ -253,7 +253,7 @@ data class PlatformAsset (
updatedAt,
width,
height,
- durationInSeconds,
+ durationMs,
orientation,
isFavorite,
adjustmentTime,
@@ -270,7 +270,7 @@ data class PlatformAsset (
return true
}
val other = other as PlatformAsset
- return MessagesPigeonUtils.deepEquals(this.id, other.id) && MessagesPigeonUtils.deepEquals(this.name, other.name) && MessagesPigeonUtils.deepEquals(this.type, other.type) && MessagesPigeonUtils.deepEquals(this.createdAt, other.createdAt) && MessagesPigeonUtils.deepEquals(this.updatedAt, other.updatedAt) && MessagesPigeonUtils.deepEquals(this.width, other.width) && MessagesPigeonUtils.deepEquals(this.height, other.height) && MessagesPigeonUtils.deepEquals(this.durationInSeconds, other.durationInSeconds) && MessagesPigeonUtils.deepEquals(this.orientation, other.orientation) && MessagesPigeonUtils.deepEquals(this.isFavorite, other.isFavorite) && MessagesPigeonUtils.deepEquals(this.adjustmentTime, other.adjustmentTime) && MessagesPigeonUtils.deepEquals(this.latitude, other.latitude) && MessagesPigeonUtils.deepEquals(this.longitude, other.longitude) && MessagesPigeonUtils.deepEquals(this.playbackStyle, other.playbackStyle)
+ return MessagesPigeonUtils.deepEquals(this.id, other.id) && MessagesPigeonUtils.deepEquals(this.name, other.name) && MessagesPigeonUtils.deepEquals(this.type, other.type) && MessagesPigeonUtils.deepEquals(this.createdAt, other.createdAt) && MessagesPigeonUtils.deepEquals(this.updatedAt, other.updatedAt) && MessagesPigeonUtils.deepEquals(this.width, other.width) && MessagesPigeonUtils.deepEquals(this.height, other.height) && MessagesPigeonUtils.deepEquals(this.durationMs, other.durationMs) && MessagesPigeonUtils.deepEquals(this.orientation, other.orientation) && MessagesPigeonUtils.deepEquals(this.isFavorite, other.isFavorite) && MessagesPigeonUtils.deepEquals(this.adjustmentTime, other.adjustmentTime) && MessagesPigeonUtils.deepEquals(this.latitude, other.latitude) && MessagesPigeonUtils.deepEquals(this.longitude, other.longitude) && MessagesPigeonUtils.deepEquals(this.playbackStyle, other.playbackStyle)
}
override fun hashCode(): Int {
@@ -282,7 +282,7 @@ data class PlatformAsset (
result = 31 * result + MessagesPigeonUtils.deepHash(this.updatedAt)
result = 31 * result + MessagesPigeonUtils.deepHash(this.width)
result = 31 * result + MessagesPigeonUtils.deepHash(this.height)
- result = 31 * result + MessagesPigeonUtils.deepHash(this.durationInSeconds)
+ result = 31 * result + MessagesPigeonUtils.deepHash(this.durationMs)
result = 31 * result + MessagesPigeonUtils.deepHash(this.orientation)
result = 31 * result + MessagesPigeonUtils.deepHash(this.isFavorite)
result = 31 * result + MessagesPigeonUtils.deepHash(this.adjustmentTime)
diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImplBase.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImplBase.kt
index eea66db2f6..777a565fe3 100644
--- a/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImplBase.kt
+++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImplBase.kt
@@ -178,7 +178,7 @@ open class NativeSyncApiImplBase(context: Context) : ImmichPlugin() {
val height = c.getInt(heightColumn).toLong()
// Duration is milliseconds
val duration = if (rawMediaType == MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE) 0L
- else c.getLong(durationColumn) / 1000
+ else c.getLong(durationColumn)
val orientation = c.getInt(orientationColumn)
val isFavorite = if (favoriteColumn == -1) false else c.getInt(favoriteColumn) != 0
diff --git a/mobile/dcm_global.yaml b/mobile/dcm_global.yaml
index ffe77eede8..0518849062 100644
--- a/mobile/dcm_global.yaml
+++ b/mobile/dcm_global.yaml
@@ -1 +1 @@
-version: '>=1.29.0 <=1.36.0'
+version: '>=1.29.0 <=1.37.0'
diff --git a/mobile/drift_schemas/main/drift_schema_v23.json b/mobile/drift_schemas/main/drift_schema_v23.json
new file mode 100644
index 0000000000..490484f7e8
--- /dev/null
+++ b/mobile/drift_schemas/main/drift_schema_v23.json
@@ -0,0 +1,3351 @@
+{
+ "_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": "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": [
+ 0,
+ 1
+ ],
+ "type": "table",
+ "data": {
+ "name": "remote_album_entity",
+ "was_declared_in_moor": false,
+ "columns": [
+ {
+ "name": "id",
+ "getter_name": "id",
+ "moor_type": "string",
+ "nullable": false,
+ "customConstraints": null,
+ "default_dart": null,
+ "default_client_dart": null,
+ "dsl_features": []
+ },
+ {
+ "name": "name",
+ "getter_name": "name",
+ "moor_type": "string",
+ "nullable": false,
+ "customConstraints": null,
+ "default_dart": null,
+ "default_client_dart": null,
+ "dsl_features": []
+ },
+ {
+ "name": "description",
+ "getter_name": "description",
+ "moor_type": "string",
+ "nullable": false,
+ "customConstraints": null,
+ "default_dart": "const CustomExpression('\\'\\'')",
+ "default_client_dart": null,
+ "dsl_features": []
+ },
+ {
+ "name": "created_at",
+ "getter_name": "createdAt",
+ "moor_type": "dateTime",
+ "nullable": false,
+ "customConstraints": null,
+ "default_dart": "const CustomExpression('CURRENT_TIMESTAMP')",
+ "default_client_dart": null,
+ "dsl_features": []
+ },
+ {
+ "name": "updated_at",
+ "getter_name": "updatedAt",
+ "moor_type": "dateTime",
+ "nullable": false,
+ "customConstraints": null,
+ "default_dart": "const CustomExpression('CURRENT_TIMESTAMP')",
+ "default_client_dart": null,
+ "dsl_features": []
+ },
+ {
+ "name": "owner_id",
+ "getter_name": "ownerId",
+ "moor_type": "string",
+ "nullable": false,
+ "customConstraints": null,
+ "defaultConstraints": "REFERENCES user_entity (id) ON DELETE CASCADE",
+ "dialectAwareDefaultConstraints": {
+ "sqlite": "REFERENCES user_entity (id) ON DELETE CASCADE"
+ },
+ "default_dart": null,
+ "default_client_dart": null,
+ "dsl_features": [
+ {
+ "foreign_key": {
+ "to": {
+ "table": "user_entity",
+ "column": "id"
+ },
+ "initially_deferred": false,
+ "on_update": null,
+ "on_delete": "cascade"
+ }
+ }
+ ]
+ },
+ {
+ "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": [
+ 4
+ ],
+ "type": "index",
+ "data": {
+ "on": 4,
+ "name": "idx_remote_album_owner_id",
+ "sql": "CREATE INDEX IF NOT EXISTS idx_remote_album_owner_id ON remote_album_entity (owner_id)",
+ "unique": false,
+ "columns": []
+ }
+ },
+ {
+ "id": 9,
+ "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": 10,
+ "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": 11,
+ "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": 12,
+ "references": [
+ 1
+ ],
+ "type": "index",
+ "data": {
+ "on": 1,
+ "name": "idx_remote_asset_owner_checksum",
+ "sql": "CREATE INDEX IF NOT EXISTS idx_remote_asset_owner_checksum ON remote_asset_entity (owner_id, checksum)",
+ "unique": false,
+ "columns": []
+ }
+ },
+ {
+ "id": 13,
+ "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": 14,
+ "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": 15,
+ "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": 16,
+ "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": 17,
+ "references": [
+ 1
+ ],
+ "type": "index",
+ "data": {
+ "on": 1,
+ "name": "idx_remote_asset_local_date_time_day",
+ "sql": "CREATE INDEX IF NOT EXISTS idx_remote_asset_local_date_time_day ON remote_asset_entity (STRFTIME('%Y-%m-%d', local_date_time))",
+ "unique": false,
+ "columns": []
+ }
+ },
+ {
+ "id": 18,
+ "references": [
+ 1
+ ],
+ "type": "index",
+ "data": {
+ "on": 1,
+ "name": "idx_remote_asset_local_date_time_month",
+ "sql": "CREATE INDEX IF NOT EXISTS idx_remote_asset_local_date_time_month ON remote_asset_entity (STRFTIME('%Y-%m', local_date_time))",
+ "unique": false,
+ "columns": []
+ }
+ },
+ {
+ "id": 19,
+ "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": 20,
+ "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": 21,
+ "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": 22,
+ "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": 23,
+ "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": 24,
+ "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": 25,
+ "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": 26,
+ "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": 27,
+ "references": [
+ 1,
+ 26
+ ],
+ "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": 28,
+ "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": 29,
+ "references": [
+ 1,
+ 28
+ ],
+ "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": 30,
+ "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": 31,
+ "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": 32,
+ "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": 33,
+ "references": [
+ 21
+ ],
+ "type": "index",
+ "data": {
+ "on": 21,
+ "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": 34,
+ "references": [
+ 22
+ ],
+ "type": "index",
+ "data": {
+ "on": 22,
+ "name": "idx_lat_lng",
+ "sql": "CREATE INDEX IF NOT EXISTS idx_lat_lng ON remote_exif_entity (latitude, longitude)",
+ "unique": false,
+ "columns": []
+ }
+ },
+ {
+ "id": 35,
+ "references": [
+ 23
+ ],
+ "type": "index",
+ "data": {
+ "on": 23,
+ "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": 36,
+ "references": [
+ 25
+ ],
+ "type": "index",
+ "data": {
+ "on": 25,
+ "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": 37,
+ "references": [
+ 28
+ ],
+ "type": "index",
+ "data": {
+ "on": 28,
+ "name": "idx_person_owner_id",
+ "sql": "CREATE INDEX IF NOT EXISTS idx_person_owner_id ON person_entity (owner_id)",
+ "unique": false,
+ "columns": []
+ }
+ },
+ {
+ "id": 38,
+ "references": [
+ 29
+ ],
+ "type": "index",
+ "data": {
+ "on": 29,
+ "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": 39,
+ "references": [
+ 29
+ ],
+ "type": "index",
+ "data": {
+ "on": 29,
+ "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": 40,
+ "references": [
+ 31
+ ],
+ "type": "index",
+ "data": {
+ "on": 31,
+ "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": [
+ 31
+ ],
+ "type": "index",
+ "data": {
+ "on": 31,
+ "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": [
+ 32
+ ],
+ "type": "index",
+ "data": {
+ "on": 32,
+ "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, \"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), \"owner_id\" TEXT NOT NULL REFERENCES user_entity (id) ON DELETE CASCADE, \"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_remote_album_owner_id",
+ "sql": [
+ {
+ "dialect": "sqlite",
+ "sql": "CREATE INDEX IF NOT EXISTS idx_remote_album_owner_id ON remote_album_entity (owner_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": "idx_remote_asset_owner_checksum",
+ "sql": [
+ {
+ "dialect": "sqlite",
+ "sql": "CREATE INDEX IF NOT EXISTS idx_remote_asset_owner_checksum ON remote_asset_entity (owner_id, checksum)"
+ }
+ ]
+ },
+ {
+ "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_local_date_time_day",
+ "sql": [
+ {
+ "dialect": "sqlite",
+ "sql": "CREATE INDEX IF NOT EXISTS idx_remote_asset_local_date_time_day ON remote_asset_entity (STRFTIME('%Y-%m-%d', local_date_time))"
+ }
+ ]
+ },
+ {
+ "name": "idx_remote_asset_local_date_time_month",
+ "sql": [
+ {
+ "dialect": "sqlite",
+ "sql": "CREATE INDEX IF NOT EXISTS idx_remote_asset_local_date_time_month ON remote_asset_entity (STRFTIME('%Y-%m', local_date_time))"
+ }
+ ]
+ },
+ {
+ "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": "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_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_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/drift_schemas/main/drift_schema_v24.json b/mobile/drift_schemas/main/drift_schema_v24.json
new file mode 100644
index 0000000000..94c65bd09a
--- /dev/null
+++ b/mobile/drift_schemas/main/drift_schema_v24.json
@@ -0,0 +1,3301 @@
+{
+ "_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": "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": "idx_remote_asset_owner_checksum",
+ "sql": "CREATE INDEX IF NOT EXISTS idx_remote_asset_owner_checksum ON remote_asset_entity (owner_id, checksum)",
+ "unique": false,
+ "columns": []
+ }
+ },
+ {
+ "id": 12,
+ "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": 13,
+ "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": 14,
+ "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": 15,
+ "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": 16,
+ "references": [
+ 1
+ ],
+ "type": "index",
+ "data": {
+ "on": 1,
+ "name": "idx_remote_asset_local_date_time_day",
+ "sql": "CREATE INDEX IF NOT EXISTS idx_remote_asset_local_date_time_day ON remote_asset_entity (STRFTIME('%Y-%m-%d', local_date_time))",
+ "unique": false,
+ "columns": []
+ }
+ },
+ {
+ "id": 17,
+ "references": [
+ 1
+ ],
+ "type": "index",
+ "data": {
+ "on": 1,
+ "name": "idx_remote_asset_local_date_time_month",
+ "sql": "CREATE INDEX IF NOT EXISTS idx_remote_asset_local_date_time_month ON remote_asset_entity (STRFTIME('%Y-%m', local_date_time))",
+ "unique": false,
+ "columns": []
+ }
+ },
+ {
+ "id": 18,
+ "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": 19,
+ "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": 20,
+ "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": 21,
+ "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": 22,
+ "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": 23,
+ "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": 24,
+ "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": 25,
+ "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": 26,
+ "references": [
+ 1,
+ 25
+ ],
+ "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": 27,
+ "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": 28,
+ "references": [
+ 1,
+ 27
+ ],
+ "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": 29,
+ "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": 30,
+ "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": 31,
+ "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": 32,
+ "references": [
+ 20
+ ],
+ "type": "index",
+ "data": {
+ "on": 20,
+ "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": 33,
+ "references": [
+ 21
+ ],
+ "type": "index",
+ "data": {
+ "on": 21,
+ "name": "idx_lat_lng",
+ "sql": "CREATE INDEX IF NOT EXISTS idx_lat_lng ON remote_exif_entity (latitude, longitude)",
+ "unique": false,
+ "columns": []
+ }
+ },
+ {
+ "id": 34,
+ "references": [
+ 22
+ ],
+ "type": "index",
+ "data": {
+ "on": 22,
+ "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": [
+ 24
+ ],
+ "type": "index",
+ "data": {
+ "on": 24,
+ "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": [
+ 27
+ ],
+ "type": "index",
+ "data": {
+ "on": 27,
+ "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": [
+ 28
+ ],
+ "type": "index",
+ "data": {
+ "on": 28,
+ "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": [
+ 28
+ ],
+ "type": "index",
+ "data": {
+ "on": 28,
+ "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": [
+ 30
+ ],
+ "type": "index",
+ "data": {
+ "on": 30,
+ "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": 40,
+ "references": [
+ 30
+ ],
+ "type": "index",
+ "data": {
+ "on": 30,
+ "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": 41,
+ "references": [
+ 31
+ ],
+ "type": "index",
+ "data": {
+ "on": 31,
+ "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, \"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": "idx_remote_asset_owner_checksum",
+ "sql": [
+ {
+ "dialect": "sqlite",
+ "sql": "CREATE INDEX IF NOT EXISTS idx_remote_asset_owner_checksum ON remote_asset_entity (owner_id, checksum)"
+ }
+ ]
+ },
+ {
+ "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_local_date_time_day",
+ "sql": [
+ {
+ "dialect": "sqlite",
+ "sql": "CREATE INDEX IF NOT EXISTS idx_remote_asset_local_date_time_day ON remote_asset_entity (STRFTIME('%Y-%m-%d', local_date_time))"
+ }
+ ]
+ },
+ {
+ "name": "idx_remote_asset_local_date_time_month",
+ "sql": [
+ {
+ "dialect": "sqlite",
+ "sql": "CREATE INDEX IF NOT EXISTS idx_remote_asset_local_date_time_month ON remote_asset_entity (STRFTIME('%Y-%m', local_date_time))"
+ }
+ ]
+ },
+ {
+ "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": "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_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_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 f88d624b89..fbffa69ba5 100644
--- a/mobile/ios/Runner.xcodeproj/project.pbxproj
+++ b/mobile/ios/Runner.xcodeproj/project.pbxproj
@@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
- objectVersion = 77;
+ objectVersion = 54;
objects = {
/* Begin PBXBuildFile section */
@@ -751,7 +751,7 @@
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 240;
CUSTOM_GROUP_ID = group.app.immich.share;
- DEVELOPMENT_TEAM = 2F67MQ8R79;
+ DEVELOPMENT_TEAM = 2W7AC6T8T5;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
@@ -760,7 +760,7 @@
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.121.0;
- PRODUCT_BUNDLE_IDENTIFIER = app.alextran.immich.profile;
+ PRODUCT_BUNDLE_IDENTIFIER = app.futo.immich.profile;
PRODUCT_NAME = "Immich-Profile";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@@ -895,7 +895,7 @@
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 240;
CUSTOM_GROUP_ID = group.app.immich.share;
- DEVELOPMENT_TEAM = 2F67MQ8R79;
+ DEVELOPMENT_TEAM = 2W7AC6T8T5;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
@@ -904,7 +904,7 @@
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.121.0;
- PRODUCT_BUNDLE_IDENTIFIER = app.alextran.immich.vdebug;
+ PRODUCT_BUNDLE_IDENTIFIER = app.futo.immich.debug;
PRODUCT_NAME = "Immich-Debug";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@@ -925,7 +925,7 @@
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 240;
CUSTOM_GROUP_ID = group.app.immich.share;
- DEVELOPMENT_TEAM = 2F67MQ8R79;
+ DEVELOPMENT_TEAM = 2W7AC6T8T5;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
@@ -958,7 +958,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 240;
- DEVELOPMENT_TEAM = 2F67MQ8R79;
+ DEVELOPMENT_TEAM = 2W7AC6T8T5;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GENERATE_INFOPLIST_FILE = YES;
@@ -975,7 +975,7 @@
MARKETING_VERSION = 1.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
- PRODUCT_BUNDLE_IDENTIFIER = app.alextran.immich.vdebug.Widget;
+ PRODUCT_BUNDLE_IDENTIFIER = app.futo.immich.debug.Widget;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
@@ -1001,7 +1001,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 240;
- DEVELOPMENT_TEAM = 2F67MQ8R79;
+ DEVELOPMENT_TEAM = 2W7AC6T8T5;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GENERATE_INFOPLIST_FILE = YES;
@@ -1041,7 +1041,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 240;
- DEVELOPMENT_TEAM = 2F67MQ8R79;
+ DEVELOPMENT_TEAM = 2W7AC6T8T5;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GENERATE_INFOPLIST_FILE = YES;
@@ -1057,7 +1057,7 @@
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MARKETING_VERSION = 1.0;
MTL_FAST_MATH = YES;
- PRODUCT_BUNDLE_IDENTIFIER = app.alextran.immich.profile.Widget;
+ PRODUCT_BUNDLE_IDENTIFIER = app.futo.immich.profile.Widget;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
@@ -1081,7 +1081,7 @@
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 240;
CUSTOM_GROUP_ID = group.app.immich.share;
- DEVELOPMENT_TEAM = 2F67MQ8R79;
+ DEVELOPMENT_TEAM = 2W7AC6T8T5;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GENERATE_INFOPLIST_FILE = YES;
@@ -1098,7 +1098,7 @@
MARKETING_VERSION = 1.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
- PRODUCT_BUNDLE_IDENTIFIER = app.alextran.immich.vdebug.ShareExtension;
+ PRODUCT_BUNDLE_IDENTIFIER = app.futo.immich.debug.ShareExtension;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SKIP_INSTALL = YES;
@@ -1125,7 +1125,7 @@
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 240;
CUSTOM_GROUP_ID = group.app.immich.share;
- DEVELOPMENT_TEAM = 2F67MQ8R79;
+ DEVELOPMENT_TEAM = 2W7AC6T8T5;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GENERATE_INFOPLIST_FILE = YES;
@@ -1166,7 +1166,7 @@
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 240;
CUSTOM_GROUP_ID = group.app.immich.share;
- DEVELOPMENT_TEAM = 2F67MQ8R79;
+ DEVELOPMENT_TEAM = 2W7AC6T8T5;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GENERATE_INFOPLIST_FILE = YES;
@@ -1182,7 +1182,7 @@
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MARKETING_VERSION = 1.0;
MTL_FAST_MATH = YES;
- PRODUCT_BUNDLE_IDENTIFIER = app.alextran.immich.profile.ShareExtension;
+ PRODUCT_BUNDLE_IDENTIFIER = app.futo.immich.profile.ShareExtension;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SKIP_INSTALL = YES;
diff --git a/mobile/ios/Runner/Sync/Messages.g.swift b/mobile/ios/Runner/Sync/Messages.g.swift
index bf7940226e..2933fc89af 100644
--- a/mobile/ios/Runner/Sync/Messages.g.swift
+++ b/mobile/ios/Runner/Sync/Messages.g.swift
@@ -192,7 +192,7 @@ struct PlatformAsset: Hashable {
var updatedAt: Int64? = nil
var width: Int64? = nil
var height: Int64? = nil
- var durationInSeconds: Int64
+ var durationMs: Int64
var orientation: Int64
var isFavorite: Bool
var adjustmentTime: Int64? = nil
@@ -210,7 +210,7 @@ struct PlatformAsset: Hashable {
let updatedAt: Int64? = nilOrValue(pigeonVar_list[4])
let width: Int64? = nilOrValue(pigeonVar_list[5])
let height: Int64? = nilOrValue(pigeonVar_list[6])
- let durationInSeconds = pigeonVar_list[7] as! Int64
+ let durationMs = pigeonVar_list[7] as! Int64
let orientation = pigeonVar_list[8] as! Int64
let isFavorite = pigeonVar_list[9] as! Bool
let adjustmentTime: Int64? = nilOrValue(pigeonVar_list[10])
@@ -226,7 +226,7 @@ struct PlatformAsset: Hashable {
updatedAt: updatedAt,
width: width,
height: height,
- durationInSeconds: durationInSeconds,
+ durationMs: durationMs,
orientation: orientation,
isFavorite: isFavorite,
adjustmentTime: adjustmentTime,
@@ -244,7 +244,7 @@ struct PlatformAsset: Hashable {
updatedAt,
width,
height,
- durationInSeconds,
+ durationMs,
orientation,
isFavorite,
adjustmentTime,
@@ -257,7 +257,7 @@ struct PlatformAsset: Hashable {
if Swift.type(of: lhs) != Swift.type(of: rhs) {
return false
}
- return deepEqualsMessages(lhs.id, rhs.id) && deepEqualsMessages(lhs.name, rhs.name) && deepEqualsMessages(lhs.type, rhs.type) && deepEqualsMessages(lhs.createdAt, rhs.createdAt) && deepEqualsMessages(lhs.updatedAt, rhs.updatedAt) && deepEqualsMessages(lhs.width, rhs.width) && deepEqualsMessages(lhs.height, rhs.height) && deepEqualsMessages(lhs.durationInSeconds, rhs.durationInSeconds) && deepEqualsMessages(lhs.orientation, rhs.orientation) && deepEqualsMessages(lhs.isFavorite, rhs.isFavorite) && deepEqualsMessages(lhs.adjustmentTime, rhs.adjustmentTime) && deepEqualsMessages(lhs.latitude, rhs.latitude) && deepEqualsMessages(lhs.longitude, rhs.longitude) && deepEqualsMessages(lhs.playbackStyle, rhs.playbackStyle)
+ return deepEqualsMessages(lhs.id, rhs.id) && deepEqualsMessages(lhs.name, rhs.name) && deepEqualsMessages(lhs.type, rhs.type) && deepEqualsMessages(lhs.createdAt, rhs.createdAt) && deepEqualsMessages(lhs.updatedAt, rhs.updatedAt) && deepEqualsMessages(lhs.width, rhs.width) && deepEqualsMessages(lhs.height, rhs.height) && deepEqualsMessages(lhs.durationMs, rhs.durationMs) && deepEqualsMessages(lhs.orientation, rhs.orientation) && deepEqualsMessages(lhs.isFavorite, rhs.isFavorite) && deepEqualsMessages(lhs.adjustmentTime, rhs.adjustmentTime) && deepEqualsMessages(lhs.latitude, rhs.latitude) && deepEqualsMessages(lhs.longitude, rhs.longitude) && deepEqualsMessages(lhs.playbackStyle, rhs.playbackStyle)
}
func hash(into hasher: inout Hasher) {
@@ -269,7 +269,7 @@ struct PlatformAsset: Hashable {
deepHashMessages(value: updatedAt, hasher: &hasher)
deepHashMessages(value: width, hasher: &hasher)
deepHashMessages(value: height, hasher: &hasher)
- deepHashMessages(value: durationInSeconds, hasher: &hasher)
+ deepHashMessages(value: durationMs, hasher: &hasher)
deepHashMessages(value: orientation, hasher: &hasher)
deepHashMessages(value: isFavorite, hasher: &hasher)
deepHashMessages(value: adjustmentTime, hasher: &hasher)
diff --git a/mobile/ios/Runner/Sync/MessagesImpl.swift b/mobile/ios/Runner/Sync/MessagesImpl.swift
index 8022fb06d2..ec96729d8f 100644
--- a/mobile/ios/Runner/Sync/MessagesImpl.swift
+++ b/mobile/ios/Runner/Sync/MessagesImpl.swift
@@ -171,7 +171,7 @@ class NativeSyncApiImpl: ImmichPlugin, NativeSyncApi, FlutterPlugin {
id: asset.localIdentifier,
name: "",
type: 0,
- durationInSeconds: 0,
+ durationMs: 0,
orientation: 0,
isFavorite: false,
playbackStyle: .unknown
diff --git a/mobile/ios/Runner/Sync/PHAssetExtensions.swift b/mobile/ios/Runner/Sync/PHAssetExtensions.swift
index 0fc1dfc701..d4d947d5bb 100644
--- a/mobile/ios/Runner/Sync/PHAssetExtensions.swift
+++ b/mobile/ios/Runner/Sync/PHAssetExtensions.swift
@@ -21,7 +21,7 @@ extension PHAsset {
updatedAt: modificationDate.map { Int64($0.timeIntervalSince1970) },
width: Int64(pixelWidth),
height: Int64(pixelHeight),
- durationInSeconds: Int64(duration),
+ durationMs: Int64(duration * 1000),
orientation: 0,
isFavorite: isFavorite,
adjustmentTime: adjustmentTimestamp,
diff --git a/mobile/ios/ShareExtension/Info.plist b/mobile/ios/ShareExtension/Info.plist
index 0f52fbffdf..dbed75e380 100644
--- a/mobile/ios/ShareExtension/Info.plist
+++ b/mobile/ios/ShareExtension/Info.plist
@@ -1,35 +1,35 @@
-
- AppGroupId
- $(CUSTOM_GROUP_ID)
- NSExtension
-
- NSExtensionAttributes
-
- IntentsSupported
-
- INSendMessageIntent
-
- NSExtensionActivationRule
- SUBQUERY ( extensionItems, $extensionItem, SUBQUERY ( $extensionItem.attachments,
+
+ AppGroupId
+ $(CUSTOM_GROUP_ID)
+ NSExtension
+
+ NSExtensionAttributes
+
+ IntentsSupported
+
+ INSendMessageIntent
+
+ NSExtensionActivationRule
+ SUBQUERY ( extensionItems, $extensionItem, SUBQUERY ( $extensionItem.attachments,
$attachment, ( ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.file-url"
|| ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.image" || ANY
$attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.text" || ANY
$attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.movie" || ANY
$attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.url" ) ).@count > 0
).@count > 0
- PHSupportedMediaTypes
-
- Video
- Image
-
-
- NSExtensionMainStoryboard
- MainInterface
- NSExtensionPointIdentifier
- com.apple.share-services
-
-
-
\ No newline at end of file
+ PHSupportedMediaTypes
+
+ Video
+ Image
+
+
+ NSExtensionMainStoryboard
+ MainInterface
+ NSExtensionPointIdentifier
+ com.apple.share-services
+
+
+
diff --git a/mobile/ios/ShareExtension/ShareExtension.entitlements b/mobile/ios/ShareExtension/ShareExtension.entitlements
index 4ad1a257d8..d16dcca065 100644
--- a/mobile/ios/ShareExtension/ShareExtension.entitlements
+++ b/mobile/ios/ShareExtension/ShareExtension.entitlements
@@ -1,10 +1,10 @@
-
- com.apple.security.application-groups
-
- group.app.immich.share
-
-
-
+
+ com.apple.security.application-groups
+
+ group.app.immich.share
+
+
+
\ No newline at end of file
diff --git a/mobile/ios/WidgetExtension/WidgetExtension.entitlements b/mobile/ios/WidgetExtension/WidgetExtension.entitlements
index 4ad1a257d8..d16dcca065 100644
--- a/mobile/ios/WidgetExtension/WidgetExtension.entitlements
+++ b/mobile/ios/WidgetExtension/WidgetExtension.entitlements
@@ -1,10 +1,10 @@
-
- com.apple.security.application-groups
-
- group.app.immich.share
-
-
-
+
+ com.apple.security.application-groups
+
+ group.app.immich.share
+
+
+
\ No newline at end of file
diff --git a/mobile/ios/fastlane/Appfile b/mobile/ios/fastlane/Appfile
index e233ba2dcc..77318e3603 100644
--- a/mobile/ios/fastlane/Appfile
+++ b/mobile/ios/fastlane/Appfile
@@ -1,5 +1,5 @@
app_identifier "app.alextran.immich" # The bundle identifier of your app
-apple_id "alex.tran1502@gmail.com" # Your Apple email address
+apple_id "altran@futo.org" # Your Apple email address
# For more information about the Appfile, see:
diff --git a/mobile/ios/fastlane/Fastfile b/mobile/ios/fastlane/Fastfile
index 9c31ced00d..ff9fc4580f 100644
--- a/mobile/ios/fastlane/Fastfile
+++ b/mobile/ios/fastlane/Fastfile
@@ -17,10 +17,11 @@ default_platform(:ios)
platform :ios do
# Constants
- TEAM_ID = "2F67MQ8R79"
- CODE_SIGN_IDENTITY = "Apple Distribution: Hau Tran (#{TEAM_ID})"
+ TEAM_ID = "2W7AC6T8T5"
+ CODE_SIGN_IDENTITY = "Apple Distribution: FUTO Holdings, Inc. (#{TEAM_ID})"
BASE_BUNDLE_ID = "app.alextran.immich"
-
+ DEV_BUNDLE_ID = "tech.futo.immich.testflight"
+
# Helper method to get App Store Connect API key
def get_api_key
app_store_connect_api_key(
@@ -44,47 +45,45 @@ def get_version_from_pubspec
end
# Helper method to configure code signing for all targets
- def configure_code_signing(bundle_id_suffix: "", profile_name_main:, profile_name_share:, profile_name_widget:)
- bundle_suffix = bundle_id_suffix.empty? ? "" : ".#{bundle_id_suffix}"
-
+ def configure_code_signing(base_bundle_id:, profile_name_main:, profile_name_share:, profile_name_widget:)
# Runner (main app)
update_code_signing_settings(
use_automatic_signing: false,
path: "./Runner.xcodeproj",
team_id: ENV["FASTLANE_TEAM_ID"] || TEAM_ID,
code_sign_identity: CODE_SIGN_IDENTITY,
- bundle_identifier: "#{BASE_BUNDLE_ID}#{bundle_suffix}",
+ bundle_identifier: base_bundle_id,
profile_name: profile_name_main,
targets: ["Runner"]
)
-
+
# ShareExtension
update_code_signing_settings(
use_automatic_signing: false,
path: "./Runner.xcodeproj",
team_id: ENV["FASTLANE_TEAM_ID"] || TEAM_ID,
code_sign_identity: CODE_SIGN_IDENTITY,
- bundle_identifier: "#{BASE_BUNDLE_ID}#{bundle_suffix}.ShareExtension",
+ bundle_identifier: "#{base_bundle_id}.ShareExtension",
profile_name: profile_name_share,
targets: ["ShareExtension"]
)
-
+
# WidgetExtension
update_code_signing_settings(
use_automatic_signing: false,
path: "./Runner.xcodeproj",
team_id: ENV["FASTLANE_TEAM_ID"] || TEAM_ID,
code_sign_identity: CODE_SIGN_IDENTITY,
- bundle_identifier: "#{BASE_BUNDLE_ID}#{bundle_suffix}.Widget",
+ bundle_identifier: "#{base_bundle_id}.Widget",
profile_name: profile_name_widget,
targets: ["WidgetExtension"]
)
end
-
+
# Helper method to build and upload to TestFlight
def build_and_upload(
api_key:,
- bundle_id_suffix: "",
+ base_bundle_id:,
configuration: "Release",
distribute_external: true,
version_number: nil,
@@ -92,9 +91,8 @@ end
profile_name_share:,
profile_name_widget:
)
- bundle_suffix = bundle_id_suffix.empty? ? "" : ".#{bundle_id_suffix}"
- app_identifier = "#{BASE_BUNDLE_ID}#{bundle_suffix}"
-
+ app_identifier = base_bundle_id
+
# Set version number if provided
if version_number
increment_version_number(version_number: version_number)
@@ -138,31 +136,31 @@ end
desc "iOS Development Build to TestFlight (requires separate bundle ID)"
lane :gha_testflight_dev do
api_key = get_api_key
-
+
# Download and install provisioning profiles from App Store Connect
# Certificate is imported by GHA workflow into build.keychain
# Capture profile names after each sigh call
- sigh(api_key: api_key, app_identifier: "#{BASE_BUNDLE_ID}.development", force: true)
+ sigh(api_key: api_key, app_identifier: DEV_BUNDLE_ID, force: true)
main_profile_name = lane_context[SharedValues::SIGH_NAME]
-
- sigh(api_key: api_key, app_identifier: "#{BASE_BUNDLE_ID}.development.ShareExtension", force: true)
+
+ sigh(api_key: api_key, app_identifier: "#{DEV_BUNDLE_ID}.ShareExtension", force: true)
share_profile_name = lane_context[SharedValues::SIGH_NAME]
-
- sigh(api_key: api_key, app_identifier: "#{BASE_BUNDLE_ID}.development.Widget", force: true)
+
+ sigh(api_key: api_key, app_identifier: "#{DEV_BUNDLE_ID}.Widget", force: true)
widget_profile_name = lane_context[SharedValues::SIGH_NAME]
-
+
# Configure code signing for dev bundle IDs using the downloaded profile names
configure_code_signing(
- bundle_id_suffix: "development",
+ base_bundle_id: DEV_BUNDLE_ID,
profile_name_main: main_profile_name,
profile_name_share: share_profile_name,
profile_name_widget: widget_profile_name
)
-
+
# Build and upload
build_and_upload(
api_key: api_key,
- bundle_id_suffix: "development",
+ base_bundle_id: DEV_BUNDLE_ID,
configuration: "Profile",
distribute_external: false,
profile_name_main: main_profile_name,
@@ -189,6 +187,7 @@ end
# Configure code signing for production bundle IDs
configure_code_signing(
+ base_bundle_id: BASE_BUNDLE_ID,
profile_name_main: main_profile_name,
profile_name_share: share_profile_name,
profile_name_widget: widget_profile_name
@@ -197,6 +196,7 @@ end
# Build and upload with version number
build_and_upload(
api_key: api_key,
+ base_bundle_id: BASE_BUNDLE_ID,
version_number: get_version_from_pubspec,
distribute_external: false,
profile_name_main: main_profile_name,
@@ -243,30 +243,30 @@ end
desc "iOS Build Only (no TestFlight upload)"
lane :gha_build_only do
- # Use the same build process as production, just skip the upload
- # This ensures PR builds validate the same way as production builds
-
+ # Use the same build process as the dev TestFlight lane, just skip the upload
+ # This ensures PR builds validate the same way as dev TestFlight builds
+
api_key = get_api_key
-
+
# Download and install provisioning profiles from App Store Connect
# Certificate is imported by GHA workflow into build.keychain
- sigh(api_key: api_key, app_identifier: "#{BASE_BUNDLE_ID}.development", force: true)
+ sigh(api_key: api_key, app_identifier: DEV_BUNDLE_ID, force: true)
main_profile_name = lane_context[SharedValues::SIGH_NAME]
-
- sigh(api_key: api_key, app_identifier: "#{BASE_BUNDLE_ID}.development.ShareExtension", force: true)
+
+ sigh(api_key: api_key, app_identifier: "#{DEV_BUNDLE_ID}.ShareExtension", force: true)
share_profile_name = lane_context[SharedValues::SIGH_NAME]
-
- sigh(api_key: api_key, app_identifier: "#{BASE_BUNDLE_ID}.development.Widget", force: true)
+
+ sigh(api_key: api_key, app_identifier: "#{DEV_BUNDLE_ID}.Widget", force: true)
widget_profile_name = lane_context[SharedValues::SIGH_NAME]
-
+
# Configure code signing for dev bundle IDs
configure_code_signing(
- bundle_id_suffix: "development",
+ base_bundle_id: DEV_BUNDLE_ID,
profile_name_main: main_profile_name,
profile_name_share: share_profile_name,
profile_name_widget: widget_profile_name
)
-
+
# Build the app (same as gha_testflight_dev but without upload)
build_app(
scheme: "Runner",
@@ -277,9 +277,9 @@ end
xcargs: "-skipMacroValidation CODE_SIGN_IDENTITY='#{CODE_SIGN_IDENTITY}' CODE_SIGN_STYLE=Manual",
export_options: {
provisioningProfiles: {
- "#{BASE_BUNDLE_ID}.development" => main_profile_name,
- "#{BASE_BUNDLE_ID}.development.ShareExtension" => share_profile_name,
- "#{BASE_BUNDLE_ID}.development.Widget" => widget_profile_name
+ DEV_BUNDLE_ID => main_profile_name,
+ "#{DEV_BUNDLE_ID}.ShareExtension" => share_profile_name,
+ "#{DEV_BUNDLE_ID}.Widget" => widget_profile_name
},
signingStyle: "manual",
signingCertificate: CODE_SIGN_IDENTITY
diff --git a/mobile/lib/domain/models/album/album.model.dart b/mobile/lib/domain/models/album/album.model.dart
index 4558e1d5be..ef67d729ec 100644
--- a/mobile/lib/domain/models/album/album.model.dart
+++ b/mobile/lib/domain/models/album/album.model.dart
@@ -8,6 +8,7 @@ enum AlbumUserRole {
// do not change this order!
editor,
viewer,
+ owner,
}
// Model for an album stored in the server
diff --git a/mobile/lib/domain/models/asset/base_asset.model.dart b/mobile/lib/domain/models/asset/base_asset.model.dart
index 85c42fd24f..15f705c65b 100644
--- a/mobile/lib/domain/models/asset/base_asset.model.dart
+++ b/mobile/lib/domain/models/asset/base_asset.model.dart
@@ -25,7 +25,7 @@ sealed class BaseAsset {
final DateTime updatedAt;
final int? width;
final int? height;
- final int? durationInSeconds;
+ final int? durationMs;
final bool isFavorite;
final String? livePhotoVideoId;
final bool isEdited;
@@ -38,7 +38,7 @@ sealed class BaseAsset {
required this.updatedAt,
this.width,
this.height,
- this.durationInSeconds,
+ this.durationMs,
this.isFavorite = false,
this.livePhotoVideoId,
required this.isEdited,
@@ -53,15 +53,17 @@ sealed class BaseAsset {
AssetPlaybackStyle get playbackStyle {
if (isVideo) return AssetPlaybackStyle.video;
if (isMotionPhoto) return AssetPlaybackStyle.livePhoto;
- if (isImage && durationInSeconds != null && durationInSeconds! > 0) return AssetPlaybackStyle.imageAnimated;
+ if (isImage && durationMs != null && durationMs! > 0) {
+ return AssetPlaybackStyle.imageAnimated;
+ }
if (isImage) return AssetPlaybackStyle.image;
return AssetPlaybackStyle.unknown;
}
Duration get duration {
- final durationInSeconds = this.durationInSeconds;
- if (durationInSeconds != null) {
- return Duration(seconds: durationInSeconds);
+ final durationMs = this.durationMs;
+ if (durationMs != null) {
+ return Duration(milliseconds: durationMs);
}
return const Duration();
}
@@ -88,7 +90,7 @@ sealed class BaseAsset {
updatedAt: $updatedAt,
width: ${width ?? ""},
height: ${height ?? ""},
- durationInSeconds: ${durationInSeconds ?? ""},
+ durationMs: ${durationMs ?? ""},
isFavorite: $isFavorite,
isEdited: $isEdited,
}''';
@@ -104,7 +106,7 @@ sealed class BaseAsset {
updatedAt == other.updatedAt &&
width == other.width &&
height == other.height &&
- durationInSeconds == other.durationInSeconds &&
+ durationMs == other.durationMs &&
isFavorite == other.isFavorite &&
isEdited == other.isEdited;
}
@@ -119,7 +121,7 @@ sealed class BaseAsset {
updatedAt.hashCode ^
width.hashCode ^
height.hashCode ^
- durationInSeconds.hashCode ^
+ durationMs.hashCode ^
isFavorite.hashCode ^
isEdited.hashCode;
}
diff --git a/mobile/lib/domain/models/asset/local_asset.model.dart b/mobile/lib/domain/models/asset/local_asset.model.dart
index 6766f4c3a2..04aa6cd846 100644
--- a/mobile/lib/domain/models/asset/local_asset.model.dart
+++ b/mobile/lib/domain/models/asset/local_asset.model.dart
@@ -23,7 +23,7 @@ class LocalAsset extends BaseAsset {
required super.updatedAt,
super.width,
super.height,
- super.durationInSeconds,
+ super.durationMs,
super.isFavorite = false,
super.livePhotoVideoId,
this.orientation = 0,
@@ -58,7 +58,7 @@ class LocalAsset extends BaseAsset {
updatedAt: $updatedAt,
width: ${width ?? ""},
height: ${height ?? ""},
- durationInSeconds: ${durationInSeconds ?? ""},
+ durationMs: ${durationMs ?? ""},
playbackStyle: $playbackStyle,
remoteId: ${remoteId ?? ""},
cloudId: ${cloudId ?? ""},
@@ -108,7 +108,7 @@ class LocalAsset extends BaseAsset {
DateTime? updatedAt,
int? width,
int? height,
- int? durationInSeconds,
+ int? durationMs,
bool? isFavorite,
int? orientation,
AssetPlaybackStyle? playbackStyle,
@@ -128,7 +128,7 @@ class LocalAsset extends BaseAsset {
updatedAt: updatedAt ?? this.updatedAt,
width: width ?? this.width,
height: height ?? this.height,
- durationInSeconds: durationInSeconds ?? this.durationInSeconds,
+ durationMs: durationMs ?? this.durationMs,
isFavorite: isFavorite ?? this.isFavorite,
orientation: orientation ?? this.orientation,
playbackStyle: playbackStyle ?? this.playbackStyle,
diff --git a/mobile/lib/domain/models/asset/remote_asset.model.dart b/mobile/lib/domain/models/asset/remote_asset.model.dart
index 745e8f46ff..36dc6242e1 100644
--- a/mobile/lib/domain/models/asset/remote_asset.model.dart
+++ b/mobile/lib/domain/models/asset/remote_asset.model.dart
@@ -22,7 +22,7 @@ class RemoteAsset extends BaseAsset {
required super.updatedAt,
super.width,
super.height,
- super.durationInSeconds,
+ super.durationMs,
super.isFavorite = false,
this.thumbHash,
this.visibility = AssetVisibility.timeline,
@@ -57,7 +57,7 @@ class RemoteAsset extends BaseAsset {
updatedAt: $updatedAt,
width: ${width ?? ""},
height: ${height ?? ""},
- durationInSeconds: ${durationInSeconds ?? ""},
+ durationMs: ${durationMs ?? ""},
localId: ${localId ?? ""},
isFavorite: $isFavorite,
thumbHash: ${thumbHash ?? ""},
@@ -102,7 +102,7 @@ class RemoteAsset extends BaseAsset {
DateTime? updatedAt,
int? width,
int? height,
- int? durationInSeconds,
+ int? durationMs,
bool? isFavorite,
String? thumbHash,
AssetVisibility? visibility,
@@ -121,7 +121,7 @@ class RemoteAsset extends BaseAsset {
updatedAt: updatedAt ?? this.updatedAt,
width: width ?? this.width,
height: height ?? this.height,
- durationInSeconds: durationInSeconds ?? this.durationInSeconds,
+ durationMs: durationMs ?? this.durationMs,
isFavorite: isFavorite ?? this.isFavorite,
thumbHash: thumbHash ?? this.thumbHash,
visibility: visibility ?? this.visibility,
@@ -146,7 +146,7 @@ class RemoteAssetExif extends RemoteAsset {
required super.updatedAt,
super.width,
super.height,
- super.durationInSeconds,
+ super.durationMs,
super.isFavorite = false,
super.thumbHash,
super.visibility = AssetVisibility.timeline,
@@ -178,7 +178,7 @@ class RemoteAssetExif extends RemoteAsset {
DateTime? updatedAt,
int? width,
int? height,
- int? durationInSeconds,
+ int? durationMs,
bool? isFavorite,
String? thumbHash,
AssetVisibility? visibility,
@@ -198,7 +198,7 @@ class RemoteAssetExif extends RemoteAsset {
updatedAt: updatedAt ?? this.updatedAt,
width: width ?? this.width,
height: height ?? this.height,
- durationInSeconds: durationInSeconds ?? this.durationInSeconds,
+ durationMs: durationMs ?? this.durationMs,
isFavorite: isFavorite ?? this.isFavorite,
thumbHash: thumbHash ?? this.thumbHash,
visibility: visibility ?? this.visibility,
diff --git a/mobile/lib/domain/models/exif.model.dart b/mobile/lib/domain/models/exif.model.dart
index 45b787d586..97c0ba3823 100644
--- a/mobile/lib/domain/models/exif.model.dart
+++ b/mobile/lib/domain/models/exif.model.dart
@@ -29,7 +29,7 @@ class ExifInfo {
bool get hasCoordinates => latitude != null && longitude != null && latitude != 0 && longitude != 0;
String get exposureTime {
- if (exposureSeconds == null) {
+ if (exposureSeconds == null || exposureSeconds! <= 0 || exposureSeconds!.isNaN) {
return "";
}
if (exposureSeconds! < 1) {
diff --git a/mobile/lib/domain/services/local_sync.service.dart b/mobile/lib/domain/services/local_sync.service.dart
index 029482978a..1d9ab1e490 100644
--- a/mobile/lib/domain/services/local_sync.service.dart
+++ b/mobile/lib/domain/services/local_sync.service.dart
@@ -337,7 +337,7 @@ class LocalSyncService {
a.createdAt.isAtSameMomentAs(b.createdAt) &&
a.width == b.width &&
a.height == b.height &&
- a.durationInSeconds == b.durationInSeconds;
+ a.durationMs == b.durationMs;
}
final firstAdjustment = a.adjustmentTime?.millisecondsSinceEpoch ?? 0;
@@ -346,7 +346,7 @@ class LocalSyncService {
a.createdAt.isAtSameMomentAs(b.createdAt) &&
a.width == b.width &&
a.height == b.height &&
- a.durationInSeconds == b.durationInSeconds &&
+ a.durationMs == b.durationMs &&
a.latitude == b.latitude &&
a.longitude == b.longitude;
}
@@ -432,7 +432,7 @@ extension PlatformToLocalAsset on PlatformAsset {
updatedAt: tryFromSecondsSinceEpoch(updatedAt, isUtc: true) ?? DateTime.timestamp(),
width: width,
height: height,
- durationInSeconds: durationInSeconds,
+ durationMs: durationMs,
isFavorite: isFavorite,
orientation: orientation,
playbackStyle: _toPlaybackStyle(playbackStyle),
diff --git a/mobile/lib/domain/services/remote_album.service.dart b/mobile/lib/domain/services/remote_album.service.dart
index 945ba8eb3f..f060ba9290 100644
--- a/mobile/lib/domain/services/remote_album.service.dart
+++ b/mobile/lib/domain/services/remote_album.service.dart
@@ -7,8 +7,8 @@ import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/domain/models/user.model.dart';
import 'package:immich_mobile/infrastructure/repositories/remote_album.repository.dart';
import 'package:immich_mobile/models/albums/album_search.model.dart';
-import 'package:immich_mobile/repositories/drift_album_api_repository.dart';
import 'package:immich_mobile/providers/album/album_sort_by_options.provider.dart';
+import 'package:immich_mobile/repositories/drift_album_api_repository.dart';
class RemoteAlbumService {
final DriftRemoteAlbumRepository _repository;
@@ -28,10 +28,6 @@ class RemoteAlbumService {
return _repository.get(albumId);
}
- Future getByName(String albumName, String ownerId) {
- return _repository.getByName(albumName, ownerId);
- }
-
Future> sortAlbums(
List albums,
AlbumSortMode sortMode, {
@@ -86,8 +82,18 @@ class RemoteAlbumService {
return filtered;
}
- Future createAlbum({required String title, required List assetIds, String? description}) async {
- final album = await _albumApiRepository.createDriftAlbum(title, description: description, assetIds: assetIds);
+ Future createAlbum({
+ required String title,
+ required UserDto owner,
+ required List assetIds,
+ String? description,
+ }) async {
+ final album = await _albumApiRepository.createDriftAlbum(
+ title,
+ owner,
+ description: description,
+ assetIds: assetIds,
+ );
await _repository.create(album, assetIds);
return album;
@@ -101,8 +107,10 @@ class RemoteAlbumService {
bool? isActivityEnabled,
AlbumAssetOrder? order,
}) async {
+ final owner = await _repository.getOwner(albumId);
final updatedAlbum = await _albumApiRepository.updateAlbum(
albumId,
+ owner,
name: name,
description: description,
thumbnailAssetId: thumbnailAssetId,
diff --git a/mobile/lib/domain/services/sync_linked_album.service.dart b/mobile/lib/domain/services/sync_linked_album.service.dart
index b61ca1c965..3bc76083b8 100644
--- a/mobile/lib/domain/services/sync_linked_album.service.dart
+++ b/mobile/lib/domain/services/sync_linked_album.service.dart
@@ -1,8 +1,11 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/album/local_album.model.dart';
+import 'package:immich_mobile/domain/models/store.model.dart';
+import 'package:immich_mobile/domain/services/store.service.dart';
import 'package:immich_mobile/infrastructure/repositories/local_album.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/remote_album.repository.dart';
import 'package:immich_mobile/providers/infrastructure/album.provider.dart';
+import 'package:immich_mobile/providers/infrastructure/store.provider.dart';
import 'package:immich_mobile/repositories/drift_album_api_repository.dart';
import 'package:immich_mobile/utils/debug_print.dart';
import 'package:logging/logging.dart';
@@ -12,6 +15,7 @@ final syncLinkedAlbumServiceProvider = Provider(
ref.watch(localAlbumRepository),
ref.watch(remoteAlbumRepository),
ref.watch(driftAlbumApiRepositoryProvider),
+ ref.watch(storeServiceProvider),
),
);
@@ -19,8 +23,14 @@ class SyncLinkedAlbumService {
final DriftLocalAlbumRepository _localAlbumRepository;
final DriftRemoteAlbumRepository _remoteAlbumRepository;
final DriftAlbumApiRepository _albumApiRepository;
+ final StoreService _storeService;
- SyncLinkedAlbumService(this._localAlbumRepository, this._remoteAlbumRepository, this._albumApiRepository);
+ SyncLinkedAlbumService(
+ this._localAlbumRepository,
+ this._remoteAlbumRepository,
+ this._albumApiRepository,
+ this._storeService,
+ );
final _log = Logger("SyncLinkedAlbumService");
@@ -103,7 +113,11 @@ class SyncLinkedAlbumService {
/// Creates a new remote album and links it to the local album
Future _createAndLinkNewRemoteAlbum(LocalAlbum localAlbum) async {
dPrint(() => "Creating new remote album for local album: ${localAlbum.name}");
- final newRemoteAlbum = await _albumApiRepository.createDriftAlbum(localAlbum.name, assetIds: []);
+ final newRemoteAlbum = await _albumApiRepository.createDriftAlbum(
+ localAlbum.name,
+ _storeService.get(StoreKey.currentUser),
+ assetIds: [],
+ );
await _remoteAlbumRepository.create(newRemoteAlbum, []);
return _localAlbumRepository.linkRemoteAlbum(localAlbum.id, newRemoteAlbum.id);
}
diff --git a/mobile/lib/domain/services/sync_stream.service.dart b/mobile/lib/domain/services/sync_stream.service.dart
index 9769c2eeec..906e352b49 100644
--- a/mobile/lib/domain/services/sync_stream.service.dart
+++ b/mobile/lib/domain/services/sync_stream.service.dart
@@ -3,6 +3,7 @@
import 'dart:async';
import 'dart:convert';
+import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/domain/models/store.model.dart';
import 'package:immich_mobile/domain/models/sync_event.model.dart';
import 'package:immich_mobile/entities/store.entity.dart';
@@ -192,17 +193,22 @@ class SyncStreamService {
final remoteSyncAssets = data.cast();
await _syncStreamRepository.updateAssetsV1(remoteSyncAssets);
if (CurrentPlatform.isAndroid && Store.get(StoreKey.manageLocalMediaAndroid, false)) {
- final hasPermission = await _localFilesManager.hasManageMediaPermission();
- if (hasPermission) {
- await _handleRemoteTrashed(remoteSyncAssets.where((e) => e.deletedAt != null).map((e) => e.checksum));
- await _applyRemoteRestoreToLocal();
- } else {
- _logger.warning("sync Trashed Assets cannot proceed because MANAGE_MEDIA permission is missing");
- }
+ await _syncAssetTrashStatus(remoteSyncAssets.where((e) => e.deletedAt != null).map((e) => e.id).toList());
+ }
+ return;
+ case SyncEntityType.assetV2:
+ final remoteSyncAssets = data.cast();
+ await _syncStreamRepository.updateAssetsV2(remoteSyncAssets);
+ if (CurrentPlatform.isAndroid && Store.get(StoreKey.manageLocalMediaAndroid, false)) {
+ await _syncAssetTrashStatus(remoteSyncAssets.where((e) => e.deletedAt != null).map((e) => e.id).toList());
}
return;
case SyncEntityType.assetDeleteV1:
- return _syncStreamRepository.deleteAssetsV1(data.cast());
+ final remoteSyncAssets = data.cast();
+ if (CurrentPlatform.isAndroid && Store.get(StoreKey.manageLocalMediaAndroid, false)) {
+ await _syncAssetDeletion(remoteSyncAssets.map((e) => e.assetId).toList());
+ }
+ return _syncStreamRepository.deleteAssetsV1(remoteSyncAssets);
case SyncEntityType.assetExifV1:
return _syncStreamRepository.updateAssetsExifV1(data.cast());
case SyncEntityType.assetEditV1:
@@ -215,8 +221,12 @@ class SyncStreamService {
return _syncStreamRepository.deleteAssetsMetadataV1(data.cast());
case SyncEntityType.partnerAssetV1:
return _syncStreamRepository.updateAssetsV1(data.cast(), debugLabel: 'partner');
+ case SyncEntityType.partnerAssetV2:
+ return _syncStreamRepository.updateAssetsV2(data.cast(), debugLabel: 'partner');
case SyncEntityType.partnerAssetBackfillV1:
return _syncStreamRepository.updateAssetsV1(data.cast(), debugLabel: 'partner backfill');
+ case SyncEntityType.partnerAssetBackfillV2:
+ return _syncStreamRepository.updateAssetsV2(data.cast(), debugLabel: 'partner backfill');
case SyncEntityType.partnerAssetDeleteV1:
return _syncStreamRepository.deleteAssetsV1(data.cast(), debugLabel: "partner");
case SyncEntityType.partnerAssetExifV1:
@@ -225,6 +235,8 @@ class SyncStreamService {
return _syncStreamRepository.updateAssetsExifV1(data.cast(), debugLabel: 'partner backfill');
case SyncEntityType.albumV1:
return _syncStreamRepository.updateAlbumsV1(data.cast());
+ case SyncEntityType.albumV2:
+ return _syncStreamRepository.updateAlbumsV2(data.cast());
case SyncEntityType.albumDeleteV1:
return _syncStreamRepository.deleteAlbumsV1(data.cast());
case SyncEntityType.albumUserV1:
@@ -235,10 +247,16 @@ class SyncStreamService {
return _syncStreamRepository.deleteAlbumUsersV1(data.cast());
case SyncEntityType.albumAssetCreateV1:
return _syncStreamRepository.updateAssetsV1(data.cast(), debugLabel: 'album asset create');
+ case SyncEntityType.albumAssetCreateV2:
+ return _syncStreamRepository.updateAssetsV2(data.cast(), debugLabel: 'album asset create');
case SyncEntityType.albumAssetUpdateV1:
return _syncStreamRepository.updateAssetsV1(data.cast(), debugLabel: 'album asset update');
+ case SyncEntityType.albumAssetUpdateV2:
+ return _syncStreamRepository.updateAssetsV2(data.cast(), debugLabel: 'album asset update');
case SyncEntityType.albumAssetBackfillV1:
return _syncStreamRepository.updateAssetsV1(data.cast(), debugLabel: 'album asset backfill');
+ case SyncEntityType.albumAssetBackfillV2:
+ return _syncStreamRepository.updateAssetsV2(data.cast(), debugLabel: 'album asset backfill');
case SyncEntityType.albumAssetExifCreateV1:
return _syncStreamRepository.updateAssetsExifV1(data.cast(), debugLabel: 'album asset exif create');
case SyncEntityType.albumAssetExifUpdateV1:
@@ -340,6 +358,47 @@ class SyncStreamService {
}
}
+ Future handleWsAssetUploadReadyV2Batch(List batchData) async {
+ if (batchData.isEmpty) return;
+
+ _logger.info('Processing batch of ${batchData.length} AssetUploadReadyV2 events');
+
+ final List assets = [];
+ final List exifs = [];
+
+ try {
+ for (final data in batchData) {
+ if (data is! Map) {
+ continue;
+ }
+
+ final payload = data;
+ final assetData = payload['asset'];
+ final exifData = payload['exif'];
+
+ if (assetData == null || exifData == null) {
+ continue;
+ }
+
+ final asset = SyncAssetV2.fromJson(assetData);
+ final exif = SyncAssetExifV1.fromJson(exifData);
+
+ if (asset != null && exif != null) {
+ assets.add(asset);
+ exifs.add(exif);
+ }
+ }
+
+ if (assets.isNotEmpty && exifs.isNotEmpty) {
+ await _syncStreamRepository.updateAssetsV2(assets, debugLabel: 'websocket-batch');
+ await _syncStreamRepository.updateAssetsExifV1(exifs, debugLabel: 'websocket-batch');
+ _logger.info('Successfully processed ${assets.length} assets in batch');
+ }
+ } catch (error, stackTrace) {
+ _logger.severe("Error processing AssetUploadReadyV2 websocket batch events", error, stackTrace);
+ }
+ }
+
Future handleWsAssetEditReadyV1(dynamic data) async {
_logger.info('Processing AssetEditReadyV1 event');
@@ -380,28 +439,67 @@ class SyncStreamService {
}
}
- Future _handleRemoteTrashed(Iterable checksums) async {
- if (checksums.isEmpty) {
+ Future handleWsAssetEditReadyV2(dynamic data) async {
+ _logger.info('Processing AssetEditReadyV2 event');
+
+ try {
+ if (data is! Map) {
+ throw ArgumentError("Invalid data format for AssetEditReadyV2 event");
+ }
+
+ final payload = data;
+
+ if (payload['asset'] == null) {
+ throw ArgumentError("Missing 'asset' field in AssetEditReadyV2 event data");
+ }
+
+ final asset = SyncAssetV2.fromJson(payload['asset']);
+ if (asset == null) {
+ throw ArgumentError("Failed to parse 'asset' field in AssetEditReadyV2 event data");
+ }
+
+ final assetEdits = (payload['edit'] as List)
+ .map((e) => SyncAssetEditV1.fromJson(e))
+ .whereType()
+ .toList();
+
+ await _syncStreamRepository.updateAssetsV2([asset], debugLabel: 'websocket-edit');
+ await _syncStreamRepository.replaceAssetEditsV1(asset.id, assetEdits, debugLabel: 'websocket-edit');
+
+ _logger.info(
+ 'Successfully processed AssetEditReadyV2 event for asset ${asset.id} with ${assetEdits.length} edits',
+ );
+ } catch (error, stackTrace) {
+ _logger.severe("Error processing AssetEditReadyV2 websocket event", error, stackTrace);
+ }
+ }
+
+ Future _handleRemoteDeleted(Iterable remoteIds) async {
+ if (remoteIds.isEmpty) {
return Future.value();
} else {
- final localAssetsToTrash = await _localAssetRepository.getAssetsFromBackupAlbums(checksums);
+ final localAssetsToTrash = await _localAssetRepository.getAssetsFromBackupAlbums(remoteIds);
if (localAssetsToTrash.isNotEmpty) {
- final mediaUrls = await Future.wait(
- localAssetsToTrash.values
- .expand((e) => e)
- .map((localAsset) => _storageRepository.getAssetEntityForAsset(localAsset).then((e) => e?.getMediaUrl())),
- );
- _logger.info("Moving to trash ${mediaUrls.join(", ")} assets");
- final result = await _localFilesManager.moveToTrash(mediaUrls.nonNulls.toList());
- if (result) {
- await _trashedLocalAssetRepository.trashLocalAsset(localAssetsToTrash);
- }
+ await _trashLocalAssets(localAssetsToTrash);
} else {
- _logger.info("No assets found in backup-enabled albums for assets: $checksums");
+ _logger.info("No assets found in backup-enabled albums for remote assets: $remoteIds");
}
}
}
+ Future _trashLocalAssets(Map> localAssetsToTrash) async {
+ final mediaUrls = await Future.wait(
+ localAssetsToTrash.values
+ .expand((e) => e)
+ .map((localAsset) => _storageRepository.getAssetEntityForAsset(localAsset).then((e) => e?.getMediaUrl())),
+ );
+ _logger.info("Moving to trash ${mediaUrls.join(", ")} assets");
+ final result = await _localFilesManager.moveToTrash(mediaUrls.nonNulls.toList());
+ if (result) {
+ await _trashedLocalAssetRepository.trashLocalAsset(localAssetsToTrash);
+ }
+ }
+
Future _applyRemoteRestoreToLocal() async {
final assetsToRestore = await _trashedLocalAssetRepository.getToRestore();
if (assetsToRestore.isNotEmpty) {
@@ -411,4 +509,23 @@ class SyncStreamService {
_logger.info("No remote assets found for restoration");
}
}
+
+ Future _syncAssetTrashStatus(List remoteIds) async {
+ if (!(await _localFilesManager.hasManageMediaPermission())) {
+ _logger.warning("Syncing asset trash status cannot proceed because MANAGE_MEDIA permission is missing");
+ return;
+ }
+
+ await _handleRemoteDeleted(remoteIds);
+ await _applyRemoteRestoreToLocal();
+ }
+
+ Future _syncAssetDeletion(List remoteIds) async {
+ if (!(await _localFilesManager.hasManageMediaPermission())) {
+ _logger.warning("Syncing asset deletion cannot proceed because MANAGE_MEDIA permission is missing");
+ return;
+ }
+
+ await _handleRemoteDeleted(remoteIds);
+ }
}
diff --git a/mobile/lib/domain/utils/background_sync.dart b/mobile/lib/domain/utils/background_sync.dart
index 7c9b6ae061..030e77cd54 100644
--- a/mobile/lib/domain/utils/background_sync.dart
+++ b/mobile/lib/domain/utils/background_sync.dart
@@ -186,7 +186,7 @@ class BackgroundSyncManager {
});
}
- Future syncWebsocketBatch(List batchData) {
+ Future syncWebsocketBatchV1(List batchData) {
if (_syncWebsocketTask != null) {
return _syncWebsocketTask!.future;
}
@@ -196,7 +196,17 @@ class BackgroundSyncManager {
});
}
- Future syncWebsocketEdit(dynamic data) {
+ Future syncWebsocketBatchV2(List batchData) {
+ if (_syncWebsocketTask != null) {
+ return _syncWebsocketTask!.future;
+ }
+ _syncWebsocketTask = _handleWsAssetUploadReadyV2Batch(batchData);
+ return _syncWebsocketTask!.whenComplete(() {
+ _syncWebsocketTask = null;
+ });
+ }
+
+ Future syncWebsocketEditV1(dynamic data) {
if (_syncWebsocketTask != null) {
return _syncWebsocketTask!.future;
}
@@ -206,6 +216,16 @@ class BackgroundSyncManager {
});
}
+ Future syncWebsocketEditV2(dynamic data) {
+ if (_syncWebsocketTask != null) {
+ return _syncWebsocketTask!.future;
+ }
+ _syncWebsocketTask = _handleWsAssetEditReadyV2(data);
+ return _syncWebsocketTask!.whenComplete(() {
+ _syncWebsocketTask = null;
+ });
+ }
+
Future syncLinkedAlbum() {
if (_linkedAlbumSyncTask != null) {
return _linkedAlbumSyncTask!.future;
@@ -242,7 +262,17 @@ Cancelable _handleWsAssetUploadReadyV1Batch(List batchData) => ru
debugLabel: 'websocket-batch',
);
+Cancelable _handleWsAssetUploadReadyV2Batch(List batchData) => runInIsolateGentle(
+ computation: (ref) => ref.read(syncStreamServiceProvider).handleWsAssetUploadReadyV2Batch(batchData),
+ debugLabel: 'websocket-batch',
+);
+
Cancelable _handleWsAssetEditReadyV1(dynamic data) => runInIsolateGentle(
computation: (ref) => ref.read(syncStreamServiceProvider).handleWsAssetEditReadyV1(data),
debugLabel: 'websocket-edit',
);
+
+Cancelable _handleWsAssetEditReadyV2(dynamic data) => runInIsolateGentle(
+ computation: (ref) => ref.read(syncStreamServiceProvider).handleWsAssetEditReadyV2(data),
+ debugLabel: 'websocket-edit',
+);
diff --git a/mobile/lib/extensions/asset_extensions.dart b/mobile/lib/extensions/asset_extensions.dart
index 6e8101bd04..3a994f9cb8 100644
--- a/mobile/lib/extensions/asset_extensions.dart
+++ b/mobile/lib/extensions/asset_extensions.dart
@@ -1,6 +1,5 @@
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/domain/models/exif.model.dart';
-import 'package:immich_mobile/extensions/string_extensions.dart';
import 'package:immich_mobile/infrastructure/utils/exif.converter.dart';
import 'package:openapi/api.dart' as api;
@@ -14,7 +13,7 @@ extension DTOToAsset on api.AssetResponseDto {
updatedAt: updatedAt,
ownerId: ownerId,
visibility: visibility.toAssetVisibility(),
- durationInSeconds: duration?.toDuration()?.inSeconds ?? 0,
+ durationMs: duration,
height: height?.toInt(),
width: width?.toInt(),
isFavorite: isFavorite,
@@ -36,7 +35,7 @@ extension DTOToAsset on api.AssetResponseDto {
updatedAt: updatedAt,
ownerId: ownerId,
visibility: visibility.toAssetVisibility(),
- durationInSeconds: duration?.toDuration()?.inSeconds ?? 0,
+ durationMs: duration,
height: height?.toInt(),
width: width?.toInt(),
isFavorite: isFavorite,
diff --git a/mobile/lib/extensions/string_extensions.dart b/mobile/lib/extensions/string_extensions.dart
index ae31565044..d30c221f96 100644
--- a/mobile/lib/extensions/string_extensions.dart
+++ b/mobile/lib/extensions/string_extensions.dart
@@ -7,11 +7,16 @@ extension StringExtension on String {
}
extension DurationExtension on String {
- /// Parses and returns the string of format HH:MM:SS as a duration object else null
+ /// Parses and returns the string of format HH:MM:SS.ffffff as a duration object else null
Duration? toDuration() {
try {
- final parts = split(':').map((e) => double.parse(e).toInt()).toList(growable: false);
- return Duration(hours: parts[0], minutes: parts[1], seconds: parts[2]);
+ final parts = split(':');
+ final hours = double.parse(parts[0]).toInt();
+ final minutes = double.parse(parts[1]).toInt();
+ final secondsParts = parts[2].split('.');
+ final seconds = int.parse(secondsParts[0]);
+ final milliseconds = secondsParts.length > 1 ? (double.parse('0.${secondsParts[1]}') * 1000).round() : 0;
+ return Duration(hours: hours, minutes: minutes, seconds: seconds, milliseconds: milliseconds);
} catch (e) {
return null;
}
diff --git a/mobile/lib/infrastructure/entities/local_asset.entity.dart b/mobile/lib/infrastructure/entities/local_asset.entity.dart
index e1cb5f5597..5a14a44fb7 100644
--- a/mobile/lib/infrastructure/entities/local_asset.entity.dart
+++ b/mobile/lib/infrastructure/entities/local_asset.entity.dart
@@ -39,7 +39,7 @@ extension LocalAssetEntityDataDomainExtension on LocalAssetEntityData {
type: type,
createdAt: createdAt,
updatedAt: updatedAt,
- durationInSeconds: durationInSeconds,
+ durationMs: durationMs,
isFavorite: isFavorite,
height: height,
width: width,
diff --git a/mobile/lib/infrastructure/entities/local_asset.entity.drift.dart b/mobile/lib/infrastructure/entities/local_asset.entity.drift.dart
index 92ac3d2e35..e01e6ce745 100644
--- a/mobile/lib/infrastructure/entities/local_asset.entity.drift.dart
+++ b/mobile/lib/infrastructure/entities/local_asset.entity.drift.dart
@@ -16,7 +16,7 @@ typedef $$LocalAssetEntityTableCreateCompanionBuilder =
i0.Value updatedAt,
i0.Value width,
i0.Value height,
- i0.Value durationInSeconds,
+ i0.Value durationMs,
required String id,
i0.Value checksum,
i0.Value isFavorite,
@@ -35,7 +35,7 @@ typedef $$LocalAssetEntityTableUpdateCompanionBuilder =
i0.Value updatedAt,
i0.Value width,
i0.Value height,
- i0.Value durationInSeconds,
+ i0.Value durationMs,
i0.Value id,
i0.Value checksum,
i0.Value isFavorite,
@@ -87,8 +87,8 @@ class $$LocalAssetEntityTableFilterComposer
builder: (column) => i0.ColumnFilters(column),
);
- i0.ColumnFilters get durationInSeconds => $composableBuilder(
- column: $table.durationInSeconds,
+ i0.ColumnFilters get durationMs => $composableBuilder(
+ column: $table.durationMs,
builder: (column) => i0.ColumnFilters(column),
);
@@ -182,8 +182,8 @@ class $$LocalAssetEntityTableOrderingComposer
builder: (column) => i0.ColumnOrderings(column),
);
- i0.ColumnOrderings get durationInSeconds => $composableBuilder(
- column: $table.durationInSeconds,
+ i0.ColumnOrderings get durationMs => $composableBuilder(
+ column: $table.durationMs,
builder: (column) => i0.ColumnOrderings(column),
);
@@ -260,8 +260,8 @@ class $$LocalAssetEntityTableAnnotationComposer
i0.GeneratedColumn get height =>
$composableBuilder(column: $table.height, builder: (column) => column);
- i0.GeneratedColumn get durationInSeconds => $composableBuilder(
- column: $table.durationInSeconds,
+ i0.GeneratedColumn get durationMs => $composableBuilder(
+ column: $table.durationMs,
builder: (column) => column,
);
@@ -348,7 +348,7 @@ class $$LocalAssetEntityTableTableManager
i0.Value updatedAt = const i0.Value.absent(),
i0.Value width = const i0.Value.absent(),
i0.Value height = const i0.Value.absent(),
- i0.Value durationInSeconds = const i0.Value.absent(),
+ i0.Value durationMs = const i0.Value.absent(),
i0.Value id = const i0.Value.absent(),
i0.Value checksum = const i0.Value.absent(),
i0.Value isFavorite = const i0.Value.absent(),
@@ -366,7 +366,7 @@ class $$LocalAssetEntityTableTableManager
updatedAt: updatedAt,
width: width,
height: height,
- durationInSeconds: durationInSeconds,
+ durationMs: durationMs,
id: id,
checksum: checksum,
isFavorite: isFavorite,
@@ -385,7 +385,7 @@ class $$LocalAssetEntityTableTableManager
i0.Value updatedAt = const i0.Value.absent(),
i0.Value width = const i0.Value.absent(),
i0.Value height = const i0.Value.absent(),
- i0.Value durationInSeconds = const i0.Value.absent(),
+ i0.Value durationMs = const i0.Value.absent(),
required String id,
i0.Value checksum = const i0.Value.absent(),
i0.Value isFavorite = const i0.Value.absent(),
@@ -403,7 +403,7 @@ class $$LocalAssetEntityTableTableManager
updatedAt: updatedAt,
width: width,
height: height,
- durationInSeconds: durationInSeconds,
+ durationMs: durationMs,
id: id,
checksum: checksum,
isFavorite: isFavorite,
@@ -522,17 +522,17 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity
type: i0.DriftSqlType.int,
requiredDuringInsert: false,
);
- static const i0.VerificationMeta _durationInSecondsMeta =
- const i0.VerificationMeta('durationInSeconds');
+ static const i0.VerificationMeta _durationMsMeta = const i0.VerificationMeta(
+ 'durationMs',
+ );
@override
- late final i0.GeneratedColumn durationInSeconds =
- i0.GeneratedColumn(
- 'duration_in_seconds',
- aliasedName,
- true,
- type: i0.DriftSqlType.int,
- requiredDuringInsert: false,
- );
+ late final i0.GeneratedColumn durationMs = i0.GeneratedColumn(
+ 'duration_ms',
+ aliasedName,
+ true,
+ type: i0.DriftSqlType.int,
+ requiredDuringInsert: false,
+ );
static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id');
@override
late final i0.GeneratedColumn id = i0.GeneratedColumn(
@@ -645,7 +645,7 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity
updatedAt,
width,
height,
- durationInSeconds,
+ durationMs,
id,
checksum,
isFavorite,
@@ -700,13 +700,10 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity
height.isAcceptableOrUnknown(data['height']!, _heightMeta),
);
}
- if (data.containsKey('duration_in_seconds')) {
+ if (data.containsKey('duration_ms')) {
context.handle(
- _durationInSecondsMeta,
- durationInSeconds.isAcceptableOrUnknown(
- data['duration_in_seconds']!,
- _durationInSecondsMeta,
- ),
+ _durationMsMeta,
+ durationMs.isAcceptableOrUnknown(data['duration_ms']!, _durationMsMeta),
);
}
if (data.containsKey('id')) {
@@ -800,9 +797,9 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity
i0.DriftSqlType.int,
data['${effectivePrefix}height'],
),
- durationInSeconds: attachedDatabase.typeMapping.read(
+ durationMs: attachedDatabase.typeMapping.read(
i0.DriftSqlType.int,
- data['${effectivePrefix}duration_in_seconds'],
+ data['${effectivePrefix}duration_ms'],
),
id: attachedDatabase.typeMapping.read(
i0.DriftSqlType.string,
@@ -870,7 +867,7 @@ class LocalAssetEntityData extends i0.DataClass
final DateTime updatedAt;
final int? width;
final int? height;
- final int? durationInSeconds;
+ final int? durationMs;
final String id;
final String? checksum;
final bool isFavorite;
@@ -887,7 +884,7 @@ class LocalAssetEntityData extends i0.DataClass
required this.updatedAt,
this.width,
this.height,
- this.durationInSeconds,
+ this.durationMs,
required this.id,
this.checksum,
required this.isFavorite,
@@ -915,8 +912,8 @@ class LocalAssetEntityData extends i0.DataClass
if (!nullToAbsent || height != null) {
map['height'] = i0.Variable(height);
}
- if (!nullToAbsent || durationInSeconds != null) {
- map['duration_in_seconds'] = i0.Variable(durationInSeconds);
+ if (!nullToAbsent || durationMs != null) {
+ map['duration_ms'] = i0.Variable(durationMs);
}
map['id'] = i0.Variable(id);
if (!nullToAbsent || checksum != null) {
@@ -958,7 +955,7 @@ class LocalAssetEntityData extends i0.DataClass
updatedAt: serializer.fromJson(json['updatedAt']),
width: serializer.fromJson(json['width']),
height: serializer.fromJson(json['height']),
- durationInSeconds: serializer.fromJson(json['durationInSeconds']),
+ durationMs: serializer.fromJson(json['durationMs']),
id: serializer.fromJson(json['id']),
checksum: serializer.fromJson(json['checksum']),
isFavorite: serializer.fromJson(json['isFavorite']),
@@ -984,7 +981,7 @@ class LocalAssetEntityData extends i0.DataClass
'updatedAt': serializer.toJson(updatedAt),
'width': serializer.toJson(width),
'height': serializer.toJson(height),
- 'durationInSeconds': serializer.toJson(durationInSeconds),
+ 'durationMs': serializer.toJson(durationMs),
'id': serializer.toJson(id),
'checksum': serializer.toJson(checksum),
'isFavorite': serializer.toJson(isFavorite),
@@ -1006,7 +1003,7 @@ class LocalAssetEntityData extends i0.DataClass
DateTime? updatedAt,
i0.Value width = const i0.Value.absent(),
i0.Value height = const i0.Value.absent(),
- i0.Value durationInSeconds = const i0.Value.absent(),
+ i0.Value durationMs = const i0.Value.absent(),
String? id,
i0.Value checksum = const i0.Value.absent(),
bool? isFavorite,
@@ -1023,9 +1020,7 @@ class LocalAssetEntityData extends i0.DataClass
updatedAt: updatedAt ?? this.updatedAt,
width: width.present ? width.value : this.width,
height: height.present ? height.value : this.height,
- durationInSeconds: durationInSeconds.present
- ? durationInSeconds.value
- : this.durationInSeconds,
+ durationMs: durationMs.present ? durationMs.value : this.durationMs,
id: id ?? this.id,
checksum: checksum.present ? checksum.value : this.checksum,
isFavorite: isFavorite ?? this.isFavorite,
@@ -1046,9 +1041,9 @@ class LocalAssetEntityData extends i0.DataClass
updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt,
width: data.width.present ? data.width.value : this.width,
height: data.height.present ? data.height.value : this.height,
- durationInSeconds: data.durationInSeconds.present
- ? data.durationInSeconds.value
- : this.durationInSeconds,
+ 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
@@ -1078,7 +1073,7 @@ class LocalAssetEntityData extends i0.DataClass
..write('updatedAt: $updatedAt, ')
..write('width: $width, ')
..write('height: $height, ')
- ..write('durationInSeconds: $durationInSeconds, ')
+ ..write('durationMs: $durationMs, ')
..write('id: $id, ')
..write('checksum: $checksum, ')
..write('isFavorite: $isFavorite, ')
@@ -1100,7 +1095,7 @@ class LocalAssetEntityData extends i0.DataClass
updatedAt,
width,
height,
- durationInSeconds,
+ durationMs,
id,
checksum,
isFavorite,
@@ -1121,7 +1116,7 @@ class LocalAssetEntityData extends i0.DataClass
other.updatedAt == this.updatedAt &&
other.width == this.width &&
other.height == this.height &&
- other.durationInSeconds == this.durationInSeconds &&
+ other.durationMs == this.durationMs &&
other.id == this.id &&
other.checksum == this.checksum &&
other.isFavorite == this.isFavorite &&
@@ -1141,7 +1136,7 @@ class LocalAssetEntityCompanion
final i0.Value updatedAt;
final i0.Value width;
final i0.Value height;
- final i0.Value durationInSeconds;
+ final i0.Value durationMs;
final i0.Value id;
final i0.Value checksum;
final i0.Value isFavorite;
@@ -1158,7 +1153,7 @@ class LocalAssetEntityCompanion
this.updatedAt = const i0.Value.absent(),
this.width = const i0.Value.absent(),
this.height = const i0.Value.absent(),
- this.durationInSeconds = const i0.Value.absent(),
+ this.durationMs = const i0.Value.absent(),
this.id = const i0.Value.absent(),
this.checksum = const i0.Value.absent(),
this.isFavorite = const i0.Value.absent(),
@@ -1176,7 +1171,7 @@ class LocalAssetEntityCompanion
this.updatedAt = const i0.Value.absent(),
this.width = const i0.Value.absent(),
this.height = const i0.Value.absent(),
- this.durationInSeconds = const i0.Value.absent(),
+ this.durationMs = const i0.Value.absent(),
required String id,
this.checksum = const i0.Value.absent(),
this.isFavorite = const i0.Value.absent(),
@@ -1196,7 +1191,7 @@ class LocalAssetEntityCompanion
i0.Expression? updatedAt,
i0.Expression? width,
i0.Expression? height,
- i0.Expression? durationInSeconds,
+ i0.Expression? durationMs,
i0.Expression? id,
i0.Expression? checksum,
i0.Expression? isFavorite,
@@ -1214,7 +1209,7 @@ class LocalAssetEntityCompanion
if (updatedAt != null) 'updated_at': updatedAt,
if (width != null) 'width': width,
if (height != null) 'height': height,
- if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds,
+ if (durationMs != null) 'duration_ms': durationMs,
if (id != null) 'id': id,
if (checksum != null) 'checksum': checksum,
if (isFavorite != null) 'is_favorite': isFavorite,
@@ -1234,7 +1229,7 @@ class LocalAssetEntityCompanion
i0.Value? updatedAt,
i0.Value? width,
i0.Value? height,
- i0.Value? durationInSeconds,
+ i0.Value? durationMs,
i0.Value? id,
i0.Value? checksum,
i0.Value? isFavorite,
@@ -1252,7 +1247,7 @@ class LocalAssetEntityCompanion
updatedAt: updatedAt ?? this.updatedAt,
width: width ?? this.width,
height: height ?? this.height,
- durationInSeconds: durationInSeconds ?? this.durationInSeconds,
+ durationMs: durationMs ?? this.durationMs,
id: id ?? this.id,
checksum: checksum ?? this.checksum,
isFavorite: isFavorite ?? this.isFavorite,
@@ -1288,8 +1283,8 @@ class LocalAssetEntityCompanion
if (height.present) {
map['height'] = i0.Variable(height.value);
}
- if (durationInSeconds.present) {
- map['duration_in_seconds'] = i0.Variable(durationInSeconds.value);
+ if (durationMs.present) {
+ map['duration_ms'] = i0.Variable(durationMs.value);
}
if (id.present) {
map['id'] = i0.Variable(id.value);
@@ -1334,7 +1329,7 @@ class LocalAssetEntityCompanion
..write('updatedAt: $updatedAt, ')
..write('width: $width, ')
..write('height: $height, ')
- ..write('durationInSeconds: $durationInSeconds, ')
+ ..write('durationMs: $durationMs, ')
..write('id: $id, ')
..write('checksum: $checksum, ')
..write('isFavorite: $isFavorite, ')
diff --git a/mobile/lib/infrastructure/entities/merged_asset.drift b/mobile/lib/infrastructure/entities/merged_asset.drift
index 73276d1756..daad02e2b3 100644
--- a/mobile/lib/infrastructure/entities/merged_asset.drift
+++ b/mobile/lib/infrastructure/entities/merged_asset.drift
@@ -14,7 +14,7 @@ SELECT
rae.updated_at,
rae.width,
rae.height,
- rae.duration_in_seconds,
+ rae.duration_ms,
rae.is_favorite,
rae.thumb_hash,
rae.checksum,
@@ -52,7 +52,7 @@ SELECT
lae.updated_at,
lae.width,
lae.height,
- lae.duration_in_seconds,
+ lae.duration_ms,
lae.is_favorite,
NULL as thumb_hash,
lae.checksum,
diff --git a/mobile/lib/infrastructure/entities/merged_asset.drift.dart b/mobile/lib/infrastructure/entities/merged_asset.drift.dart
index c6004eb10d..1e501d4028 100644
--- a/mobile/lib/infrastructure/entities/merged_asset.drift.dart
+++ b/mobile/lib/infrastructure/entities/merged_asset.drift.dart
@@ -29,7 +29,7 @@ class MergedAssetDrift extends i1.ModularAccessor {
);
$arrayStartIndex += generatedlimit.amountOfVariables;
return customSelect(
- 'SELECT rae.id AS remote_id, (SELECT lae.id FROM local_asset_entity AS lae WHERE lae.checksum = rae.checksum LIMIT 1) AS local_id, rae.name, rae.type, rae.created_at AS created_at, rae.updated_at, rae.width, rae.height, rae.duration_in_seconds, rae.is_favorite, rae.thumb_hash, rae.checksum, rae.owner_id, rae.live_photo_video_id, 0 AS orientation, rae.stack_id, NULL AS i_cloud_id, NULL AS latitude, NULL AS longitude, NULL AS adjustmentTime, rae.is_edited, 0 AS playback_style FROM remote_asset_entity AS rae LEFT JOIN stack_entity AS se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandeduserIds) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT NULL AS remote_id, lae.id AS local_id, lae.name, lae.type, lae.created_at AS created_at, lae.updated_at, lae.width, lae.height, lae.duration_in_seconds, lae.is_favorite, NULL AS thumb_hash, lae.checksum, NULL AS owner_id, NULL AS live_photo_video_id, lae.orientation, NULL AS stack_id, lae.i_cloud_id, lae.latitude, lae.longitude, lae.adjustment_time, 0 AS is_edited, lae.playback_style FROM local_asset_entity AS lae WHERE NOT EXISTS (SELECT 1 FROM remote_asset_entity AS rae WHERE rae.checksum = lae.checksum AND rae.owner_id IN ($expandeduserIds)) AND EXISTS (SELECT 1 FROM local_album_asset_entity AS laa INNER JOIN local_album_entity AS la ON laa.album_id = la.id WHERE laa.asset_id = lae.id AND la.backup_selection = 0) AND NOT EXISTS (SELECT 1 FROM local_album_asset_entity AS laa INNER JOIN local_album_entity AS la ON laa.album_id = la.id WHERE laa.asset_id = lae.id AND la.backup_selection = 2) ORDER BY created_at DESC ${generatedlimit.sql}',
+ 'SELECT rae.id AS remote_id, (SELECT lae.id FROM local_asset_entity AS lae WHERE lae.checksum = rae.checksum LIMIT 1) AS local_id, rae.name, rae.type, rae.created_at AS created_at, rae.updated_at, rae.width, rae.height, rae.duration_ms, rae.is_favorite, rae.thumb_hash, rae.checksum, rae.owner_id, rae.live_photo_video_id, 0 AS orientation, rae.stack_id, NULL AS i_cloud_id, NULL AS latitude, NULL AS longitude, NULL AS adjustmentTime, rae.is_edited, 0 AS playback_style FROM remote_asset_entity AS rae LEFT JOIN stack_entity AS se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandeduserIds) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT NULL AS remote_id, lae.id AS local_id, lae.name, lae.type, lae.created_at AS created_at, lae.updated_at, lae.width, lae.height, lae.duration_ms, lae.is_favorite, NULL AS thumb_hash, lae.checksum, NULL AS owner_id, NULL AS live_photo_video_id, lae.orientation, NULL AS stack_id, lae.i_cloud_id, lae.latitude, lae.longitude, lae.adjustment_time, 0 AS is_edited, lae.playback_style FROM local_asset_entity AS lae WHERE NOT EXISTS (SELECT 1 FROM remote_asset_entity AS rae WHERE rae.checksum = lae.checksum AND rae.owner_id IN ($expandeduserIds)) AND EXISTS (SELECT 1 FROM local_album_asset_entity AS laa INNER JOIN local_album_entity AS la ON laa.album_id = la.id WHERE laa.asset_id = lae.id AND la.backup_selection = 0) AND NOT EXISTS (SELECT 1 FROM local_album_asset_entity AS laa INNER JOIN local_album_entity AS la ON laa.album_id = la.id WHERE laa.asset_id = lae.id AND la.backup_selection = 2) ORDER BY created_at DESC ${generatedlimit.sql}',
variables: [
for (var $ in userIds) i0.Variable($),
...generatedlimit.introducedVariables,
@@ -54,7 +54,7 @@ class MergedAssetDrift extends i1.ModularAccessor {
updatedAt: row.read('updated_at'),
width: row.readNullable('width'),
height: row.readNullable('height'),
- durationInSeconds: row.readNullable('duration_in_seconds'),
+ durationMs: row.readNullable('duration_ms'),
isFavorite: row.read('is_favorite'),
thumbHash: row.readNullable('thumb_hash'),
checksum: row.readNullable('checksum'),
@@ -127,7 +127,7 @@ class MergedAssetResult {
final DateTime updatedAt;
final int? width;
final int? height;
- final int? durationInSeconds;
+ final int? durationMs;
final bool isFavorite;
final String? thumbHash;
final String? checksum;
@@ -150,7 +150,7 @@ class MergedAssetResult {
required this.updatedAt,
this.width,
this.height,
- this.durationInSeconds,
+ this.durationMs,
required this.isFavorite,
this.thumbHash,
this.checksum,
diff --git a/mobile/lib/infrastructure/entities/remote_album.entity.dart b/mobile/lib/infrastructure/entities/remote_album.entity.dart
index 30e13853d8..d12d511148 100644
--- a/mobile/lib/infrastructure/entities/remote_album.entity.dart
+++ b/mobile/lib/infrastructure/entities/remote_album.entity.dart
@@ -1,10 +1,8 @@
import 'package:drift/drift.dart';
import 'package:immich_mobile/domain/models/album/album.model.dart';
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart';
-import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
-@TableIndex.sql('CREATE INDEX IF NOT EXISTS idx_remote_album_owner_id ON remote_album_entity (owner_id)')
class RemoteAlbumEntity extends Table with DriftDefaultsMixin {
const RemoteAlbumEntity();
@@ -18,8 +16,6 @@ class RemoteAlbumEntity extends Table with DriftDefaultsMixin {
DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)();
- TextColumn get ownerId => text().references(UserEntity, #id, onDelete: KeyAction.cascade)();
-
TextColumn get thumbnailAssetId =>
text().references(RemoteAssetEntity, #id, onDelete: KeyAction.setNull).nullable()();
diff --git a/mobile/lib/infrastructure/entities/remote_album.entity.drift.dart b/mobile/lib/infrastructure/entities/remote_album.entity.drift.dart
index 7dc864b978..8fdabe0b15 100644
--- a/mobile/lib/infrastructure/entities/remote_album.entity.drift.dart
+++ b/mobile/lib/infrastructure/entities/remote_album.entity.drift.dart
@@ -7,11 +7,9 @@ import 'package:immich_mobile/domain/models/album/album.model.dart' as i2;
import 'package:immich_mobile/infrastructure/entities/remote_album.entity.dart'
as i3;
import 'package:drift/src/runtime/query_builder/query_builder.dart' as i4;
-import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart'
+import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart'
as i5;
import 'package:drift/internal/modular.dart' as i6;
-import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart'
- as i7;
typedef $$RemoteAlbumEntityTableCreateCompanionBuilder =
i1.RemoteAlbumEntityCompanion Function({
@@ -20,7 +18,6 @@ typedef $$RemoteAlbumEntityTableCreateCompanionBuilder =
i0.Value description,
i0.Value createdAt,
i0.Value updatedAt,
- required String ownerId,
i0.Value thumbnailAssetId,
i0.Value isActivityEnabled,
required i2.AlbumAssetOrder order,
@@ -32,7 +29,6 @@ typedef $$RemoteAlbumEntityTableUpdateCompanionBuilder =
i0.Value description,
i0.Value createdAt,
i0.Value updatedAt,
- i0.Value ownerId,
i0.Value thumbnailAssetId,
i0.Value isActivityEnabled,
i0.Value order,
@@ -51,42 +47,10 @@ final class $$RemoteAlbumEntityTableReferences
super.$_typedResult,
);
- static i5.$UserEntityTable _ownerIdTable(i0.GeneratedDatabase db) =>
- i6.ReadDatabaseContainer(db)
- .resultSet('user_entity')
- .createAlias(
- i0.$_aliasNameGenerator(
- i6.ReadDatabaseContainer(db)
- .resultSet('remote_album_entity')
- .ownerId,
- i6.ReadDatabaseContainer(
- db,
- ).resultSet('user_entity').id,
- ),
- );
-
- i5.$$UserEntityTableProcessedTableManager get ownerId {
- final $_column = $_itemColumn('owner_id')!;
-
- final manager = i5
- .$$UserEntityTableTableManager(
- $_db,
- i6.ReadDatabaseContainer(
- $_db,
- ).resultSet('user_entity'),
- )
- .filter((f) => f.id.sqlEquals($_column));
- final item = $_typedResult.readTableOrNull(_ownerIdTable($_db));
- if (item == null) return manager;
- return i0.ProcessedTableManager(
- manager.$state.copyWith(prefetchedData: [item]),
- );
- }
-
- static i7.$RemoteAssetEntityTable _thumbnailAssetIdTable(
+ static i5.$RemoteAssetEntityTable _thumbnailAssetIdTable(
i0.GeneratedDatabase db,
) => i6.ReadDatabaseContainer(db)
- .resultSet('remote_asset_entity')
+ .resultSet('remote_asset_entity')
.createAlias(
i0.$_aliasNameGenerator(
i6.ReadDatabaseContainer(db)
@@ -94,19 +58,19 @@ final class $$RemoteAlbumEntityTableReferences
.thumbnailAssetId,
i6.ReadDatabaseContainer(
db,
- ).resultSet('remote_asset_entity').id,
+ ).resultSet('remote_asset_entity').id,
),
);
- i7.$$RemoteAssetEntityTableProcessedTableManager? get thumbnailAssetId {
+ i5.$$RemoteAssetEntityTableProcessedTableManager? get thumbnailAssetId {
final $_column = $_itemColumn('thumbnail_asset_id');
if ($_column == null) return null;
- final manager = i7
+ final manager = i5
.$$RemoteAssetEntityTableTableManager(
$_db,
i6.ReadDatabaseContainer(
$_db,
- ).resultSet('remote_asset_entity'),
+ ).resultSet('remote_asset_entity'),
)
.filter((f) => f.id.sqlEquals($_column));
final item = $_typedResult.readTableOrNull(_thumbnailAssetIdTable($_db));
@@ -162,51 +126,24 @@ class $$RemoteAlbumEntityTableFilterComposer
builder: (column) => i0.ColumnWithTypeConverterFilters(column),
);
- i5.$$UserEntityTableFilterComposer get ownerId {
- final i5.$$UserEntityTableFilterComposer composer = $composerBuilder(
- composer: this,
- getCurrentColumn: (t) => t.ownerId,
- referencedTable: i6.ReadDatabaseContainer(
- $db,
- ).resultSet('user_entity'),
- getReferencedColumn: (t) => t.id,
- builder:
- (
- joinBuilder, {
- $addJoinBuilderToRootComposer,
- $removeJoinBuilderFromRootComposer,
- }) => i5.$$UserEntityTableFilterComposer(
- $db: $db,
- $table: i6.ReadDatabaseContainer(
- $db,
- ).resultSet('user_entity'),
- $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
- joinBuilder: joinBuilder,
- $removeJoinBuilderFromRootComposer:
- $removeJoinBuilderFromRootComposer,
- ),
- );
- return composer;
- }
-
- i7.$$RemoteAssetEntityTableFilterComposer get thumbnailAssetId {
- final i7.$$RemoteAssetEntityTableFilterComposer composer = $composerBuilder(
+ i5.$$RemoteAssetEntityTableFilterComposer get thumbnailAssetId {
+ final i5.$$RemoteAssetEntityTableFilterComposer composer = $composerBuilder(
composer: this,
getCurrentColumn: (t) => t.thumbnailAssetId,
referencedTable: i6.ReadDatabaseContainer(
$db,
- ).resultSet('remote_asset_entity'),
+ ).resultSet('remote_asset_entity'),
getReferencedColumn: (t) => t.id,
builder:
(
joinBuilder, {
$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer,
- }) => i7.$$RemoteAssetEntityTableFilterComposer(
+ }) => i5.$$RemoteAssetEntityTableFilterComposer(
$db: $db,
$table: i6.ReadDatabaseContainer(
$db,
- ).resultSet('remote_asset_entity'),
+ ).resultSet('remote_asset_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
@@ -261,52 +198,25 @@ class $$RemoteAlbumEntityTableOrderingComposer
builder: (column) => i0.ColumnOrderings(column),
);
- i5.$$UserEntityTableOrderingComposer get ownerId {
- final i5.$$UserEntityTableOrderingComposer composer = $composerBuilder(
- composer: this,
- getCurrentColumn: (t) => t.ownerId,
- referencedTable: i6.ReadDatabaseContainer(
- $db,
- ).resultSet('user_entity'),
- getReferencedColumn: (t) => t.id,
- builder:
- (
- joinBuilder, {
- $addJoinBuilderToRootComposer,
- $removeJoinBuilderFromRootComposer,
- }) => i5.$$UserEntityTableOrderingComposer(
- $db: $db,
- $table: i6.ReadDatabaseContainer(
- $db,
- ).resultSet('user_entity'),
- $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
- joinBuilder: joinBuilder,
- $removeJoinBuilderFromRootComposer:
- $removeJoinBuilderFromRootComposer,
- ),
- );
- return composer;
- }
-
- i7.$$RemoteAssetEntityTableOrderingComposer get thumbnailAssetId {
- final i7.$$RemoteAssetEntityTableOrderingComposer composer =
+ i5.$$RemoteAssetEntityTableOrderingComposer get thumbnailAssetId {
+ final i5.$$RemoteAssetEntityTableOrderingComposer composer =
$composerBuilder(
composer: this,
getCurrentColumn: (t) => t.thumbnailAssetId,
referencedTable: i6.ReadDatabaseContainer(
$db,
- ).resultSet('remote_asset_entity'),
+ ).resultSet('remote_asset_entity'),
getReferencedColumn: (t) => t.id,
builder:
(
joinBuilder, {
$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer,
- }) => i7.$$RemoteAssetEntityTableOrderingComposer(
+ }) => i5.$$RemoteAssetEntityTableOrderingComposer(
$db: $db,
$table: i6.ReadDatabaseContainer(
$db,
- ).resultSet('remote_asset_entity'),
+ ).resultSet('remote_asset_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
@@ -351,52 +261,25 @@ class $$RemoteAlbumEntityTableAnnotationComposer
i0.GeneratedColumnWithTypeConverter get order =>
$composableBuilder(column: $table.order, builder: (column) => column);
- i5.$$UserEntityTableAnnotationComposer get ownerId {
- final i5.$$UserEntityTableAnnotationComposer composer = $composerBuilder(
- composer: this,
- getCurrentColumn: (t) => t.ownerId,
- referencedTable: i6.ReadDatabaseContainer(
- $db,
- ).resultSet('user_entity'),
- getReferencedColumn: (t) => t.id,
- builder:
- (
- joinBuilder, {
- $addJoinBuilderToRootComposer,
- $removeJoinBuilderFromRootComposer,
- }) => i5.$$UserEntityTableAnnotationComposer(
- $db: $db,
- $table: i6.ReadDatabaseContainer(
- $db,
- ).resultSet('user_entity'),
- $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
- joinBuilder: joinBuilder,
- $removeJoinBuilderFromRootComposer:
- $removeJoinBuilderFromRootComposer,
- ),
- );
- return composer;
- }
-
- i7.$$RemoteAssetEntityTableAnnotationComposer get thumbnailAssetId {
- final i7.$$RemoteAssetEntityTableAnnotationComposer composer =
+ i5.$$RemoteAssetEntityTableAnnotationComposer get thumbnailAssetId {
+ final i5.$$RemoteAssetEntityTableAnnotationComposer composer =
$composerBuilder(
composer: this,
getCurrentColumn: (t) => t.thumbnailAssetId,
referencedTable: i6.ReadDatabaseContainer(
$db,
- ).resultSet('remote_asset_entity'),
+ ).resultSet('remote_asset_entity'),
getReferencedColumn: (t) => t.id,
builder:
(
joinBuilder, {
$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer,
- }) => i7.$$RemoteAssetEntityTableAnnotationComposer(
+ }) => i5.$$RemoteAssetEntityTableAnnotationComposer(
$db: $db,
$table: i6.ReadDatabaseContainer(
$db,
- ).resultSet('remote_asset_entity'),
+ ).resultSet('remote_asset_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
@@ -420,7 +303,7 @@ class $$RemoteAlbumEntityTableTableManager
$$RemoteAlbumEntityTableUpdateCompanionBuilder,
(i1.RemoteAlbumEntityData, i1.$$RemoteAlbumEntityTableReferences),
i1.RemoteAlbumEntityData,
- i0.PrefetchHooks Function({bool ownerId, bool thumbnailAssetId})
+ i0.PrefetchHooks Function({bool thumbnailAssetId})
> {
$$RemoteAlbumEntityTableTableManager(
i0.GeneratedDatabase db,
@@ -445,7 +328,6 @@ class $$RemoteAlbumEntityTableTableManager
i0.Value description = const i0.Value.absent(),
i0.Value createdAt = const i0.Value.absent(),
i0.Value updatedAt = const i0.Value.absent(),
- i0.Value ownerId = const i0.Value.absent(),
i0.Value thumbnailAssetId = const i0.Value.absent(),
i0.Value isActivityEnabled = const i0.Value.absent(),
i0.Value