diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index a107c1ac3a..9dd57a5ec8 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -4,7 +4,7 @@ FROM ${BASEIMAGE} # Flutter SDK # https://flutter.dev/docs/development/tools/sdk/releases?tab=linux ENV FLUTTER_CHANNEL="stable" -ENV FLUTTER_VERSION="3.24.5" +ENV FLUTTER_VERSION="3.29.1" ENV FLUTTER_HOME=/flutter ENV PATH=${PATH}:${FLUTTER_HOME}/bin diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 5d4290fd7b..aa756a7d08 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -32,5 +32,5 @@ The `/api/something` endpoint is now `/api/something-else` - [ ] I have confirmed that any new dependencies are strictly necessary. - [ ] I have written tests for new code (if applicable) - [ ] I have followed naming conventions/patterns in the surrounding code -- [ ] All code in `src/services` uses repositories implementations for database calls, filesystem operations, etc. -- [ ] All code in `src/repositories/` is pretty basic/simple and does not have any immich specific logic (that belongs in `src/services`) +- [ ] All code in `src/services/` uses repositories implementations for database calls, filesystem operations, etc. +- [ ] All code in `src/repositories/` is pretty basic/simple and does not have any immich specific logic (that belongs in `src/services/`) diff --git a/.github/workflows/cli.yml b/.github/workflows/cli.yml index 7cfd75c61b..1243a81105 100644 --- a/.github/workflows/cli.yml +++ b/.github/workflows/cli.yml @@ -56,7 +56,7 @@ jobs: uses: actions/checkout@v4 - name: Set up QEMU - uses: docker/setup-qemu-action@v3.5.0 + uses: docker/setup-qemu-action@v3.6.0 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3.10.0 diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 96970fa460..12f3410310 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -255,19 +255,20 @@ jobs: flavor: | # Disable latest tag latest=false + suffix=${{ matrix.suffix }} images: | name=${{ env.GHCR_REPO }} name=${{ env.DOCKER_REPO }},enable=${{ github.event_name == 'release' }} tags: | # Tag with branch name - type=ref,event=branch,suffix=${{ matrix.suffix }} + type=ref,event=branch # Tag with pr-number - type=ref,event=pr,suffix=${{ matrix.suffix }} + type=ref,event=pr # Tag with long commit sha hash - type=sha,format=long,prefix=commit-,suffix=${{ matrix.suffix }} + type=sha,format=long,prefix=commit- # Tag with git tag on release - type=ref,event=tag,suffix=${{ matrix.suffix }} - type=raw,value=release,enable=${{ github.event_name == 'release' }},suffix=${{ matrix.suffix }} + type=ref,event=tag + type=raw,value=release,enable=${{ github.event_name == 'release' }} - name: Create manifest list and push working-directory: ${{ runner.temp }}/digests @@ -408,19 +409,20 @@ jobs: flavor: | # Disable latest tag latest=false + suffix=${{ matrix.suffix }} images: | name=${{ env.GHCR_REPO }} name=${{ env.DOCKER_REPO }},enable=${{ github.event_name == 'release' }} tags: | # Tag with branch name - type=ref,event=branch,suffix=${{ matrix.suffix }} + type=ref,event=branch # Tag with pr-number - type=ref,event=pr,suffix=${{ matrix.suffix }} + type=ref,event=pr # Tag with long commit sha hash - type=sha,format=long,prefix=commit-,suffix=${{ matrix.suffix }} + type=sha,format=long,prefix=commit- # Tag with git tag on release - type=ref,event=tag,suffix=${{ matrix.suffix }} - type=raw,value=release,enable=${{ github.event_name == 'release' }},suffix=${{ matrix.suffix }} + type=ref,event=tag + type=raw,value=release,enable=${{ github.event_name == 'release' }} - name: Create manifest list and push working-directory: ${{ runner.temp }}/digests diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml index 9be52f90f0..df4856b1a1 100644 --- a/.github/workflows/prepare-release.yml +++ b/.github/workflows/prepare-release.yml @@ -41,8 +41,8 @@ jobs: with: token: ${{ steps.generate-token.outputs.token }} - - name: Install Poetry - run: pipx install poetry + - name: Install uv + uses: astral-sh/setup-uv@v5 - name: Bump version run: misc/release/pump-version.sh -s "${{ inputs.serverBump }}" -m "${{ inputs.mobileBump }}" @@ -74,7 +74,7 @@ jobs: with: app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} - + - name: Checkout uses: actions/checkout@v4 with: diff --git a/.github/workflows/preview-label.yaml b/.github/workflows/preview-label.yaml index 6468d05e80..1c324ab49f 100644 --- a/.github/workflows/preview-label.yaml +++ b/.github/workflows/preview-label.yaml @@ -2,7 +2,7 @@ name: Preview label on: pull_request: - types: [labeled] + types: [labeled, closed] jobs: comment-status: diff --git a/.github/workflows/static_analysis.yml b/.github/workflows/static_analysis.yml index 4d8d40a47b..1e2020a19d 100644 --- a/.github/workflows/static_analysis.yml +++ b/.github/workflows/static_analysis.yml @@ -50,6 +50,26 @@ jobs: run: dart pub get working-directory: ./mobile + - name: Run Build Runner + run: make build + working-directory: ./mobile + + - name: Find file changes + uses: tj-actions/verify-changed-files@v20 + id: verify-changed-files + with: + files: | + mobile/**/*.g.dart + mobile/**/*.gr.dart + mobile/**/*.drift.dart + + - name: Verify files have not changed + if: steps.verify-changed-files.outputs.files_changed == 'true' + run: | + echo "ERROR: Generated files not up to date! Run make_build inside the mobile directory" + echo "Changed files: ${{ steps.verify-changed-files.outputs.changed_files }}" + exit 1 + - name: Run dart analyze run: dart analyze --fatal-infos working-directory: ./mobile @@ -61,8 +81,3 @@ jobs: - name: Run dart custom_lint run: dart run custom_lint working-directory: ./mobile - - # Enable after riverpod generator migration is completed - # - name: Run dart custom lint - # run: dart run custom_lint - # working-directory: ./mobile diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 476ef9f354..99f41697d4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -380,27 +380,28 @@ jobs: working-directory: ./machine-learning steps: - uses: actions/checkout@v4 - - name: Install poetry - run: pipx install poetry + - name: Install uv + uses: astral-sh/setup-uv@v5 - uses: actions/setup-python@v5 - with: - python-version: 3.11 - cache: 'poetry' + # TODO: add caching when supported (https://github.com/actions/setup-python/pull/818) + # with: + # python-version: 3.11 + # cache: 'uv' - name: Install dependencies run: | - poetry install --with dev --with cpu + uv sync --extra cpu - name: Lint with ruff run: | - poetry run ruff check --output-format=github app export + uv run ruff check --output-format=github app export - name: Check black formatting run: | - poetry run black --check app export + uv run black --check app export - name: Run mypy type checking run: | - poetry run mypy --install-types --non-interactive --strict app/ + uv run mypy --strict app/ - name: Run tests and coverage run: | - poetry run pytest app --cov=app --cov-report term-missing + uv run pytest app --cov=app --cov-report term-missing shellcheck: name: ShellCheck diff --git a/.github/workflows/weblate-lock.yml b/.github/workflows/weblate-lock.yml index 29a30640bd..4189e51919 100644 --- a/.github/workflows/weblate-lock.yml +++ b/.github/workflows/weblate-lock.yml @@ -18,8 +18,15 @@ jobs: filters: | i18n: - 'i18n/!(en)**\.json' + - name: Debug + run: | + echo "Should run: ${{ steps.found_paths.outputs.i18n == 'true' && github.head_ref != 'chore/translations'}}" + echo "Found i18n paths: ${{ steps.found_paths.outputs.i18n }}" + echo "Head ref: ${{ github.head_ref }}" + enforce-lock: name: Check Weblate Lock + needs: [ pre-job ] runs-on: ubuntu-latest if: ${{ needs.pre-job.outputs.should_run == 'true' }} steps: diff --git a/README.md b/README.md index 7b037ba1e7..fea9801d41 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@
@@ -63,7 +63,7 @@
Access the demo [here](https://demo.immich.app). The demo is running on a Free-tier Oracle VM in Amsterdam with a 2.4Ghz quad-core ARM64 CPU and 24GB RAM.
-For the mobile app, you can use `https://demo.immich.app/api` for the `Server Endpoint URL`
+For the mobile app, you can use `https://demo.immich.app` for the `Server Endpoint URL`
### Login credentials
diff --git a/cli/package-lock.json b/cli/package-lock.json
index 8d8c19cd35..3b16a3bf18 100644
--- a/cli/package-lock.json
+++ b/cli/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "@immich/cli",
- "version": "2.2.52",
+ "version": "2.2.53",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@immich/cli",
- "version": "2.2.52",
+ "version": "2.2.53",
"license": "GNU Affero General Public License version 3",
"dependencies": {
"chokidar": "^4.0.3",
@@ -27,7 +27,7 @@
"@types/lodash-es": "^4.17.12",
"@types/micromatch": "^4.0.9",
"@types/mock-fs": "^4.13.1",
- "@types/node": "^22.13.5",
+ "@types/node": "^22.13.9",
"@typescript-eslint/eslint-plugin": "^8.15.0",
"@typescript-eslint/parser": "^8.15.0",
"@vitest/coverage-v8": "^3.0.0",
@@ -55,14 +55,14 @@
},
"../open-api/typescript-sdk": {
"name": "@immich/sdk",
- "version": "1.128.0",
+ "version": "1.129.0",
"dev": true,
"license": "GNU Affero General Public License version 3",
"dependencies": {
"@oazapfts/runtime": "^1.0.2"
},
"devDependencies": {
- "@types/node": "^22.13.5",
+ "@types/node": "^22.13.9",
"typescript": "^5.3.3"
}
},
@@ -1502,9 +1502,9 @@
}
},
"node_modules/@types/node": {
- "version": "22.13.8",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.8.tgz",
- "integrity": "sha512-G3EfaZS+iOGYWLLRCEAXdWK9my08oHNZ+FHluRiggIYJPOXzhOiDgpVCUHaUvyIC5/fj7C/p637jdzC666AOKQ==",
+ "version": "22.13.10",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.10.tgz",
+ "integrity": "sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1518,17 +1518,17 @@
"dev": true
},
"node_modules/@typescript-eslint/eslint-plugin": {
- "version": "8.25.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.25.0.tgz",
- "integrity": "sha512-VM7bpzAe7JO/BFf40pIT1lJqS/z1F8OaSsUB3rpFJucQA4cOSuH2RVVVkFULN+En0Djgr29/jb4EQnedUo95KA==",
+ "version": "8.26.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.26.0.tgz",
+ "integrity": "sha512-cLr1J6pe56zjKYajK6SSSre6nl1Gj6xDp1TY0trpgPzjVbgDwd09v2Ws37LABxzkicmUjhEeg/fAUjPJJB1v5Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/regexpp": "^4.10.0",
- "@typescript-eslint/scope-manager": "8.25.0",
- "@typescript-eslint/type-utils": "8.25.0",
- "@typescript-eslint/utils": "8.25.0",
- "@typescript-eslint/visitor-keys": "8.25.0",
+ "@typescript-eslint/scope-manager": "8.26.0",
+ "@typescript-eslint/type-utils": "8.26.0",
+ "@typescript-eslint/utils": "8.26.0",
+ "@typescript-eslint/visitor-keys": "8.26.0",
"graphemer": "^1.4.0",
"ignore": "^5.3.1",
"natural-compare": "^1.4.0",
@@ -1544,20 +1544,20 @@
"peerDependencies": {
"@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0",
"eslint": "^8.57.0 || ^9.0.0",
- "typescript": ">=4.8.4 <5.8.0"
+ "typescript": ">=4.8.4 <5.9.0"
}
},
"node_modules/@typescript-eslint/parser": {
- "version": "8.25.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.25.0.tgz",
- "integrity": "sha512-4gbs64bnbSzu4FpgMiQ1A+D+urxkoJk/kqlDJ2W//5SygaEiAP2B4GoS7TEdxgwol2el03gckFV9lJ4QOMiiHg==",
+ "version": "8.26.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.26.0.tgz",
+ "integrity": "sha512-mNtXP9LTVBy14ZF3o7JG69gRPBK/2QWtQd0j0oH26HcY/foyJJau6pNUez7QrM5UHnSvwlQcJXKsk0I99B9pOA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/scope-manager": "8.25.0",
- "@typescript-eslint/types": "8.25.0",
- "@typescript-eslint/typescript-estree": "8.25.0",
- "@typescript-eslint/visitor-keys": "8.25.0",
+ "@typescript-eslint/scope-manager": "8.26.0",
+ "@typescript-eslint/types": "8.26.0",
+ "@typescript-eslint/typescript-estree": "8.26.0",
+ "@typescript-eslint/visitor-keys": "8.26.0",
"debug": "^4.3.4"
},
"engines": {
@@ -1569,18 +1569,18 @@
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0",
- "typescript": ">=4.8.4 <5.8.0"
+ "typescript": ">=4.8.4 <5.9.0"
}
},
"node_modules/@typescript-eslint/scope-manager": {
- "version": "8.25.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.25.0.tgz",
- "integrity": "sha512-6PPeiKIGbgStEyt4NNXa2ru5pMzQ8OYKO1hX1z53HMomrmiSB+R5FmChgQAP1ro8jMtNawz+TRQo/cSXrauTpg==",
+ "version": "8.26.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.26.0.tgz",
+ "integrity": "sha512-E0ntLvsfPqnPwng8b8y4OGuzh/iIOm2z8U3S9zic2TeMLW61u5IH2Q1wu0oSTkfrSzwbDJIB/Lm8O3//8BWMPA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.25.0",
- "@typescript-eslint/visitor-keys": "8.25.0"
+ "@typescript-eslint/types": "8.26.0",
+ "@typescript-eslint/visitor-keys": "8.26.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1591,14 +1591,14 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
- "version": "8.25.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.25.0.tgz",
- "integrity": "sha512-d77dHgHWnxmXOPJuDWO4FDWADmGQkN5+tt6SFRZz/RtCWl4pHgFl3+WdYCn16+3teG09DY6XtEpf3gGD0a186g==",
+ "version": "8.26.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.26.0.tgz",
+ "integrity": "sha512-ruk0RNChLKz3zKGn2LwXuVoeBcUMh+jaqzN461uMMdxy5H9epZqIBtYj7UiPXRuOpaALXGbmRuZQhmwHhaS04Q==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/typescript-estree": "8.25.0",
- "@typescript-eslint/utils": "8.25.0",
+ "@typescript-eslint/typescript-estree": "8.26.0",
+ "@typescript-eslint/utils": "8.26.0",
"debug": "^4.3.4",
"ts-api-utils": "^2.0.1"
},
@@ -1611,13 +1611,13 @@
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0",
- "typescript": ">=4.8.4 <5.8.0"
+ "typescript": ">=4.8.4 <5.9.0"
}
},
"node_modules/@typescript-eslint/types": {
- "version": "8.25.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.25.0.tgz",
- "integrity": "sha512-+vUe0Zb4tkNgznQwicsvLUJgZIRs6ITeWSCclX1q85pR1iOiaj+4uZJIUp//Z27QWu5Cseiw3O3AR8hVpax7Aw==",
+ "version": "8.26.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.26.0.tgz",
+ "integrity": "sha512-89B1eP3tnpr9A8L6PZlSjBvnJhWXtYfZhECqlBl1D9Lme9mHO6iWlsprBtVenQvY1HMhax1mWOjhtL3fh/u+pA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -1629,14 +1629,14 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
- "version": "8.25.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.25.0.tgz",
- "integrity": "sha512-ZPaiAKEZ6Blt/TPAx5Ot0EIB/yGtLI2EsGoY6F7XKklfMxYQyvtL+gT/UCqkMzO0BVFHLDlzvFqQzurYahxv9Q==",
+ "version": "8.26.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.26.0.tgz",
+ "integrity": "sha512-tiJ1Hvy/V/oMVRTbEOIeemA2XoylimlDQ03CgPPNaHYZbpsc78Hmngnt+WXZfJX1pjQ711V7g0H7cSJThGYfPQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.25.0",
- "@typescript-eslint/visitor-keys": "8.25.0",
+ "@typescript-eslint/types": "8.26.0",
+ "@typescript-eslint/visitor-keys": "8.26.0",
"debug": "^4.3.4",
"fast-glob": "^3.3.2",
"is-glob": "^4.0.3",
@@ -1652,20 +1652,20 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "typescript": ">=4.8.4 <5.8.0"
+ "typescript": ">=4.8.4 <5.9.0"
}
},
"node_modules/@typescript-eslint/utils": {
- "version": "8.25.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.25.0.tgz",
- "integrity": "sha512-syqRbrEv0J1wywiLsK60XzHnQe/kRViI3zwFALrNEgnntn1l24Ra2KvOAWwWbWZ1lBZxZljPDGOq967dsl6fkA==",
+ "version": "8.26.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.26.0.tgz",
+ "integrity": "sha512-2L2tU3FVwhvU14LndnQCA2frYC8JnPDVKyQtWFPf8IYFMt/ykEN1bPolNhNbCVgOmdzTlWdusCTKA/9nKrf8Ig==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.4.0",
- "@typescript-eslint/scope-manager": "8.25.0",
- "@typescript-eslint/types": "8.25.0",
- "@typescript-eslint/typescript-estree": "8.25.0"
+ "@typescript-eslint/scope-manager": "8.26.0",
+ "@typescript-eslint/types": "8.26.0",
+ "@typescript-eslint/typescript-estree": "8.26.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1676,17 +1676,17 @@
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0",
- "typescript": ">=4.8.4 <5.8.0"
+ "typescript": ">=4.8.4 <5.9.0"
}
},
"node_modules/@typescript-eslint/visitor-keys": {
- "version": "8.25.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.25.0.tgz",
- "integrity": "sha512-kCYXKAum9CecGVHGij7muybDfTS2sD3t0L4bJsEZLkyrXUImiCTq1M3LG2SRtOhiHFwMR9wAFplpT6XHYjTkwQ==",
+ "version": "8.26.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.26.0.tgz",
+ "integrity": "sha512-2z8JQJWAzPdDd51dRQ/oqIJxe99/hoLIqmf8RMCAJQtYDc535W/Jt2+RTP4bP0aKeBG1F65yjIZuczOXCmbWwg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.25.0",
+ "@typescript-eslint/types": "8.26.0",
"eslint-visitor-keys": "^4.2.0"
},
"engines": {
@@ -1711,9 +1711,9 @@
}
},
"node_modules/@vitest/coverage-v8": {
- "version": "3.0.7",
- "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.0.7.tgz",
- "integrity": "sha512-Av8WgBJLTrfLOer0uy3CxjlVuWK4CzcLBndW1Nm2vI+3hZ2ozHututkfc7Blu1u6waeQ7J8gzPK/AsBRnWA5mQ==",
+ "version": "3.0.8",
+ "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.0.8.tgz",
+ "integrity": "sha512-y7SAKsQirsEJ2F8bulBck4DoluhI2EEgTimHd6EEUgJBGKy9tC25cpywh1MH4FvDGoG2Unt7+asVd1kj4qOSAw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1734,8 +1734,8 @@
"url": "https://opencollective.com/vitest"
},
"peerDependencies": {
- "@vitest/browser": "3.0.7",
- "vitest": "3.0.7"
+ "@vitest/browser": "3.0.8",
+ "vitest": "3.0.8"
},
"peerDependenciesMeta": {
"@vitest/browser": {
@@ -1744,14 +1744,14 @@
}
},
"node_modules/@vitest/expect": {
- "version": "3.0.7",
- "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.0.7.tgz",
- "integrity": "sha512-QP25f+YJhzPfHrHfYHtvRn+uvkCFCqFtW9CktfBxmB+25QqWsx7VB2As6f4GmwllHLDhXNHvqedwhvMmSnNmjw==",
+ "version": "3.0.8",
+ "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.0.8.tgz",
+ "integrity": "sha512-Xu6TTIavTvSSS6LZaA3EebWFr6tsoXPetOWNMOlc7LO88QVVBwq2oQWBoDiLCN6YTvNYsGSjqOO8CAdjom5DCQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@vitest/spy": "3.0.7",
- "@vitest/utils": "3.0.7",
+ "@vitest/spy": "3.0.8",
+ "@vitest/utils": "3.0.8",
"chai": "^5.2.0",
"tinyrainbow": "^2.0.0"
},
@@ -1760,13 +1760,13 @@
}
},
"node_modules/@vitest/mocker": {
- "version": "3.0.7",
- "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.0.7.tgz",
- "integrity": "sha512-qui+3BLz9Eonx4EAuR/i+QlCX6AUZ35taDQgwGkK/Tw6/WgwodSrjN1X2xf69IA/643ZX5zNKIn2svvtZDrs4w==",
+ "version": "3.0.8",
+ "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.0.8.tgz",
+ "integrity": "sha512-n3LjS7fcW1BCoF+zWZxG7/5XvuYH+lsFg+BDwwAz0arIwHQJFUEsKBQ0BLU49fCxuM/2HSeBPHQD8WjgrxMfow==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@vitest/spy": "3.0.7",
+ "@vitest/spy": "3.0.8",
"estree-walker": "^3.0.3",
"magic-string": "^0.30.17"
},
@@ -1787,9 +1787,9 @@
}
},
"node_modules/@vitest/pretty-format": {
- "version": "3.0.7",
- "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.7.tgz",
- "integrity": "sha512-CiRY0BViD/V8uwuEzz9Yapyao+M9M008/9oMOSQydwbwb+CMokEq3XVaF3XK/VWaOK0Jm9z7ENhybg70Gtxsmg==",
+ "version": "3.0.8",
+ "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.8.tgz",
+ "integrity": "sha512-BNqwbEyitFhzYMYHUVbIvepOyeQOSFA/NeJMIP9enMntkkxLgOcgABH6fjyXG85ipTgvero6noreavGIqfJcIg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1800,13 +1800,13 @@
}
},
"node_modules/@vitest/runner": {
- "version": "3.0.7",
- "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.0.7.tgz",
- "integrity": "sha512-WeEl38Z0S2ZcuRTeyYqaZtm4e26tq6ZFqh5y8YD9YxfWuu0OFiGFUbnxNynwLjNRHPsXyee2M9tV7YxOTPZl2g==",
+ "version": "3.0.8",
+ "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.0.8.tgz",
+ "integrity": "sha512-c7UUw6gEcOzI8fih+uaAXS5DwjlBaCJUo7KJ4VvJcjL95+DSR1kova2hFuRt3w41KZEFcOEiq098KkyrjXeM5w==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@vitest/utils": "3.0.7",
+ "@vitest/utils": "3.0.8",
"pathe": "^2.0.3"
},
"funding": {
@@ -1814,13 +1814,13 @@
}
},
"node_modules/@vitest/snapshot": {
- "version": "3.0.7",
- "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.0.7.tgz",
- "integrity": "sha512-eqTUryJWQN0Rtf5yqCGTQWsCFOQe4eNz5Twsu21xYEcnFJtMU5XvmG0vgebhdLlrHQTSq5p8vWHJIeJQV8ovsA==",
+ "version": "3.0.8",
+ "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.0.8.tgz",
+ "integrity": "sha512-x8IlMGSEMugakInj44nUrLSILh/zy1f2/BgH0UeHpNyOocG18M9CWVIFBaXPt8TrqVZWmcPjwfG/ht5tnpba8A==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@vitest/pretty-format": "3.0.7",
+ "@vitest/pretty-format": "3.0.8",
"magic-string": "^0.30.17",
"pathe": "^2.0.3"
},
@@ -1829,9 +1829,9 @@
}
},
"node_modules/@vitest/spy": {
- "version": "3.0.7",
- "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.7.tgz",
- "integrity": "sha512-4T4WcsibB0B6hrKdAZTM37ekuyFZt2cGbEGd2+L0P8ov15J1/HUsUaqkXEQPNAWr4BtPPe1gI+FYfMHhEKfR8w==",
+ "version": "3.0.8",
+ "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.8.tgz",
+ "integrity": "sha512-MR+PzJa+22vFKYb934CejhR4BeRpMSoxkvNoDit68GQxRLSf11aT6CTj3XaqUU9rxgWJFnqicN/wxw6yBRkI1Q==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1842,13 +1842,13 @@
}
},
"node_modules/@vitest/utils": {
- "version": "3.0.7",
- "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.7.tgz",
- "integrity": "sha512-xePVpCRfooFX3rANQjwoditoXgWb1MaFbzmGuPP59MK6i13mrnDw/yEIyJudLeW6/38mCNcwCiJIGmpDPibAIg==",
+ "version": "3.0.8",
+ "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.8.tgz",
+ "integrity": "sha512-nkBC3aEhfX2PdtQI/QwAWp8qZWwzASsU4Npbcd5RdMPBSSLCpkZp52P3xku3s3uA0HIEhGvEcF8rNkBsz9dQ4Q==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@vitest/pretty-format": "3.0.7",
+ "@vitest/pretty-format": "3.0.8",
"loupe": "^3.1.3",
"tinyrainbow": "^2.0.0"
},
@@ -2429,13 +2429,13 @@
}
},
"node_modules/eslint-config-prettier": {
- "version": "10.0.2",
- "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.0.2.tgz",
- "integrity": "sha512-1105/17ZIMjmCOJOPNfVdbXafLCLj3hPmkmB7dLgt7XsQ/zkxSuDerE/xgO3RxoHysR1N1whmquY0lSn2O0VLg==",
+ "version": "10.1.1",
+ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.1.tgz",
+ "integrity": "sha512-4EQQr6wXwS+ZJSzaR5ZCrYgLxqvUjdXctaEtBqHcbkW944B1NQyO4qpdHQbXBONfwxXdkAY81HH4+LUfrg+zPw==",
"dev": true,
"license": "MIT",
"bin": {
- "eslint-config-prettier": "build/bin/cli.js"
+ "eslint-config-prettier": "bin/cli.js"
},
"peerDependencies": {
"eslint": ">=7.0.0"
@@ -3617,9 +3617,9 @@
}
},
"node_modules/prettier": {
- "version": "3.5.2",
- "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.2.tgz",
- "integrity": "sha512-lc6npv5PH7hVqozBR7lkBNOGXV9vMwROAPlumdBkX0wTbbzPu/U1hk5yL8p2pt4Xoc+2mkT8t/sow2YrV/M5qg==",
+ "version": "3.5.3",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz",
+ "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==",
"dev": true,
"license": "MIT",
"bin": {
@@ -4278,9 +4278,9 @@
}
},
"node_modules/typescript": {
- "version": "5.7.3",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz",
- "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==",
+ "version": "5.8.2",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz",
+ "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==",
"dev": true,
"license": "Apache-2.0",
"bin": {
@@ -4349,9 +4349,9 @@
}
},
"node_modules/vite": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.0.tgz",
- "integrity": "sha512-7dPxoo+WsT/64rDcwoOjk76XHj+TqNTIvHKcuMQ1k4/SeHDaQt5GFAeLYzrimZrMpn/O6DtdI03WUjdxuPM0oQ==",
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.1.tgz",
+ "integrity": "sha512-n2GnqDb6XPhlt9B8olZPrgMD/es/Nd1RdChF6CBD/fHW6pUyUTt2sQW2fPRX5GiD9XEa6+8A6A4f2vT6pSsE7Q==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4421,9 +4421,9 @@
}
},
"node_modules/vite-node": {
- "version": "3.0.7",
- "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.0.7.tgz",
- "integrity": "sha512-2fX0QwX4GkkkpULXdT1Pf4q0tC1i1lFOyseKoonavXUNlQ77KpW2XqBGGNIm/J4Ows4KxgGJzDguYVPKwG/n5A==",
+ "version": "3.0.8",
+ "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.0.8.tgz",
+ "integrity": "sha512-6PhR4H9VGlcwXZ+KWCdMqbtG649xCPZqfI9j2PsK1FcXgEzro5bGHcVKFCTqPLaNKZES8Evqv4LwvZARsq5qlg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4464,19 +4464,19 @@
}
},
"node_modules/vitest": {
- "version": "3.0.7",
- "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.7.tgz",
- "integrity": "sha512-IP7gPK3LS3Fvn44x30X1dM9vtawm0aesAa2yBIZ9vQf+qB69NXC5776+Qmcr7ohUXIQuLhk7xQR0aSUIDPqavg==",
+ "version": "3.0.8",
+ "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.8.tgz",
+ "integrity": "sha512-dfqAsNqRGUc8hB9OVR2P0w8PZPEckti2+5rdZip0WIz9WW0MnImJ8XiR61QhqLa92EQzKP2uPkzenKOAHyEIbA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@vitest/expect": "3.0.7",
- "@vitest/mocker": "3.0.7",
- "@vitest/pretty-format": "^3.0.7",
- "@vitest/runner": "3.0.7",
- "@vitest/snapshot": "3.0.7",
- "@vitest/spy": "3.0.7",
- "@vitest/utils": "3.0.7",
+ "@vitest/expect": "3.0.8",
+ "@vitest/mocker": "3.0.8",
+ "@vitest/pretty-format": "^3.0.8",
+ "@vitest/runner": "3.0.8",
+ "@vitest/snapshot": "3.0.8",
+ "@vitest/spy": "3.0.8",
+ "@vitest/utils": "3.0.8",
"chai": "^5.2.0",
"debug": "^4.4.0",
"expect-type": "^1.1.0",
@@ -4488,7 +4488,7 @@
"tinypool": "^1.0.2",
"tinyrainbow": "^2.0.0",
"vite": "^5.0.0 || ^6.0.0",
- "vite-node": "3.0.7",
+ "vite-node": "3.0.8",
"why-is-node-running": "^2.3.0"
},
"bin": {
@@ -4504,8 +4504,8 @@
"@edge-runtime/vm": "*",
"@types/debug": "^4.1.12",
"@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
- "@vitest/browser": "3.0.7",
- "@vitest/ui": "3.0.7",
+ "@vitest/browser": "3.0.8",
+ "@vitest/ui": "3.0.8",
"happy-dom": "*",
"jsdom": "*"
},
@@ -4534,9 +4534,9 @@
}
},
"node_modules/vitest-fetch-mock": {
- "version": "0.4.4",
- "resolved": "https://registry.npmjs.org/vitest-fetch-mock/-/vitest-fetch-mock-0.4.4.tgz",
- "integrity": "sha512-i2RNEAKBgnLWwj5DVz8ouzaHaPVg1xaYgAUmU5p+baJ149upnO+yJLPchAiY9ij8hf0PDkJVVke1pftBxmT05g==",
+ "version": "0.4.5",
+ "resolved": "https://registry.npmjs.org/vitest-fetch-mock/-/vitest-fetch-mock-0.4.5.tgz",
+ "integrity": "sha512-nhWdCQIGtaSEUVl96pMm0WggyDGPDv5FUy/Q9Hx3cs2RGmh3Q/uRsLClGbdG3kXBkJ3br5yTUjB2MeW25TwdOA==",
"dev": true,
"license": "MIT",
"engines": {
diff --git a/cli/package.json b/cli/package.json
index 193b678338..334bcc0b0c 100644
--- a/cli/package.json
+++ b/cli/package.json
@@ -1,6 +1,6 @@
{
"name": "@immich/cli",
- "version": "2.2.52",
+ "version": "2.2.53",
"description": "Command Line Interface (CLI) for Immich",
"type": "module",
"exports": "./dist/index.js",
@@ -21,7 +21,7 @@
"@types/lodash-es": "^4.17.12",
"@types/micromatch": "^4.0.9",
"@types/mock-fs": "^4.13.1",
- "@types/node": "^22.13.5",
+ "@types/node": "^22.13.9",
"@typescript-eslint/eslint-plugin": "^8.15.0",
"@typescript-eslint/parser": "^8.15.0",
"@vitest/coverage-v8": "^3.0.0",
@@ -72,4 +72,4 @@
"volta": {
"node": "22.14.0"
}
-}
\ No newline at end of file
+}
diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.dev.yml
index 9df5be3901..f2f814fbd0 100644
--- a/docker/docker-compose.dev.yml
+++ b/docker/docker-compose.dev.yml
@@ -25,7 +25,7 @@ services:
context: ../
dockerfile: server/Dockerfile
target: dev
- restart: always
+ restart: unless-stopped
volumes:
- ../server:/usr/src/app
- ../open-api:/usr/src/open-api
diff --git a/docs/docs/FAQ.mdx b/docs/docs/FAQ.mdx
index 23b2b9b30f..96ac03c9dc 100644
--- a/docs/docs/FAQ.mdx
+++ b/docs/docs/FAQ.mdx
@@ -117,7 +117,7 @@ See [Backup and Restore](/docs/administration/backup-and-restore.md).
### Does Immich support reading existing face tag metadata?
-No, it currently does not. There is an [open feature request on GitHub](https://github.com/immich-app/immich/discussions/4348).
+Yes, it creates new faces and persons from the imported asset metadata. For details see the [feature request #4348](https://github.com/immich-app/immich/discussions/4348) and [PR #6455](https://github.com/immich-app/immich/pull/6455).
### Does Immich support the filtering of NSFW images?
diff --git a/docs/docs/administration/backup-and-restore.md b/docs/docs/administration/backup-and-restore.md
index 4eb4d3a8bb..817a7dca6d 100644
--- a/docs/docs/administration/backup-and-restore.md
+++ b/docs/docs/administration/backup-and-restore.md
@@ -30,6 +30,13 @@ As mentioned above, you should make your own backup of these together with the a
You can adjust the schedule and amount of kept backups in the [admin settings](http://my.immich.app/admin/system-settings?isOpen=backup).
By default, Immich will keep the last 14 backups and create a new backup every day at 2:00 AM.
+#### Trigger Backup
+
+You are able to trigger a backup in the [admin job status page](http://my.immich.app/admin/jobs-status).
+Visit the page, open the "Create job" modal from the top right, select "Backup Database" and click "Confirm".
+A job will run and trigger a backup, you can verify this worked correctly by checking the logs or the backup folder.
+This backup will count towards the last X backups that will be kept based on your settings.
+
#### Restoring
We hope to make restoring simpler in future versions, for now you can find the backups in the `UPLOAD_LOCATION/backups` folder on your host.
@@ -53,7 +60,7 @@ docker compose create # Create Docker containers for Immich apps witho
docker start immich_postgres # Start Postgres server
sleep 10 # Wait for Postgres server to start up
# Check the database user if you deviated from the default
-gunzip < "/path/to/backup/dump.sql.gz" \
+gunzip --stdout "/path/to/backup/dump.sql.gz" \
| sed "s/SELECT pg_catalog.set_config('search_path', '', false);/SELECT pg_catalog.set_config('search_path', 'public, pg_catalog', true);/g" \
| docker exec -i immich_postgres psql --dbname=postgres --username= 0 && (
@@ -35,7 +33,6 @@ function HomepageHeader() {
sacrificing your privacy.
Download the mobile app
@@ -97,9 +94,8 @@ function HomepageHeader() {
docker-compose.yml
/.env
siano aggiornati per evitare problemi e incongruenze, soprattutto se utilizzi WatchTower o altri strumenti per aggiornare Immich in automatico.",
+ "version_announcement_message": "Ehilà! È stata rilasciata una nuova versione di Immich. Leggi le release notes e assicurati che i tuoi file di configurazione siano aggiornati per evitare problemi e incongruenze, soprattutto se utilizzi WatchTower o altri strumenti per aggiornare Immich in automatico.",
"version_history": "Storico delle Versioni",
"version_history_item": "Versione installata {version} il {date}",
"video": "Video",
@@ -1343,6 +1357,7 @@
"view_all": "Vedi tutto",
"view_all_users": "Visualizza tutti gli utenti",
"view_in_timeline": "Visualizza in timeline",
+ "view_link": "Visualizza link",
"view_links": "Visualizza i link",
"view_name": "Visualizza",
"view_next_asset": "Visualizza risorsa successiva",
@@ -1359,4 +1374,4 @@
"yes": "Si",
"you_dont_have_any_shared_links": "Non è presente alcun link condiviso",
"zoom_image": "Ingrandisci immagine"
-}
+}
\ No newline at end of file
diff --git a/i18n/ja.json b/i18n/ja.json
index 98054f14ca..154e6be42e 100644
--- a/i18n/ja.json
+++ b/i18n/ja.json
@@ -256,7 +256,7 @@
"transcoding_max_b_frames": "最大Bフレーム",
"transcoding_max_b_frames_description": "値を高くすると圧縮効率が向上しますが、エンコード速度が遅くなります。古いデバイスのハードウェアアクセラレーションでは対応していない場合があります。\"0\" はBフレームを無効にし、\"-1\" はこの値を自動的に設定します。",
"transcoding_max_bitrate": "最大ビットレート",
- "transcoding_max_bitrate_description": "最大ビットレートを設定すると、品質にわずかな影響を与えながらも、ファイルサイズを予測しやすくなります。720pの場合、一般的な値は VP9 や HEVC で \"2600k\"、H.264 で \"4500k\" です。\"0\" に設定すると無効になります。",
+ "transcoding_max_bitrate_description": "最大ビットレートを設定すると、品質にわずかな影響を与えながらも、ファイルサイズを予測しやすくなります。720pの場合、一般的な値は VP9 や HEVC で \"2600 kbit/s\"、H.264 で \"4500 kbit/s\" です。\"0\" に設定すると無効になります。",
"transcoding_max_keyframe_interval": "最大キーフレーム間隔",
"transcoding_max_keyframe_interval_description": "キーフレーム間の最大フレーム間隔を設定します。値を低くすると圧縮効率が悪化しますが、シーク時間が改善され、動きの速いシーンの品質が向上する場合があります。\"0\" に設定すると、この値が自動的に設定されます。",
"transcoding_optimal_description": "設定解像度を超える動画、または容認されていない形式の動画",
@@ -1257,4 +1257,4 @@
"yes": "はい",
"you_dont_have_any_shared_links": "共有リンクはありません",
"zoom_image": "画像を拡大"
-}
+}
\ No newline at end of file
diff --git a/i18n/ko.json b/i18n/ko.json
index ef8227c3f2..29971e5e30 100644
--- a/i18n/ko.json
+++ b/i18n/ko.json
@@ -68,7 +68,7 @@
"image_prefer_embedded_preview": "포함된 미리 보기 선호",
"image_prefer_embedded_preview_setting_description": "가능한 경우 이미지 처리 시 RAW 사진에 포함된 미리 보기를 사용합니다. 포함된 미리 보기는 카메라에서 생성된 것으로 카메라마다 품질이 다릅니다. 일부 이미지의 경우 더 정확한 색상이 표현될 수 있지만 반대로 더 많은 아티팩트가 있을 수도 있습니다.",
"image_prefer_wide_gamut": "넓은 색 영역 선호",
- "image_prefer_wide_gamut_setting_description": "섬네일 이미지에 Display P3을 사용합니다. 많은 색상을 표현할 수 있어 더 정확한 표현이 가능하지만, 오래된 브라우저를 사용하는 경우 이미지가 다르게 보일 수 있습니다. 색상 왜곡을 방지하기 위해 sRGB 이미지는 이 설정이 적용되지 않습니다.",
+ "image_prefer_wide_gamut_setting_description": "섬네일 이미지에 Display P3를 사용합니다. 많은 색상을 표현할 수 있어 더 정확한 표현이 가능하지만, 오래된 브라우저를 사용하는 경우 이미지가 다르게 보일 수 있습니다. 색상 왜곡을 방지하기 위해 sRGB 이미지는 이 설정이 적용되지 않습니다.",
"image_preview_description": "메타데이터를 제거한 중간 크기의 이미지, 단일 항목을 보는 경우 및 기계 학습에 사용됨",
"image_preview_quality_description": "1부터 100 사이의 미리보기 품질. 값이 높을수록 좋지만 파일 크기가 커져 앱의 반응성이 떨어질 수 있으며, 값이 낮으면 기계 학습의 품질이 떨어질 수 있습니다.",
"image_preview_title": "미리보기 설정",
@@ -131,7 +131,7 @@
"machine_learning_smart_search_description": "CLIP 임베딩으로 자연어를 사용하여 이미지 검색",
"machine_learning_smart_search_enabled": "스마트 검색 활성화",
"machine_learning_smart_search_enabled_description": "비활성화된 경우 스마트 검색을 위한 이미지 처리를 진행하지 않습니다.",
- "machine_learning_url_description": "기계 학습 서버의 URL을 입럭합니다. 여러 개의 URL이 입력된 경우 모든 서버에 응답을 보낸 뒤 응답에 성공한 서버가 사용됩니다.",
+ "machine_learning_url_description": "기계 학습 서버의 URL을 입력합니다. URL이 여러 개인 경우 첫 번째 서버부터 마지막까지 성공적으로 응답할 때까지 한 번에 하나씩 순서대로 요청을 시도합니다. 응답하지 않는 서버는 다시 사용 가능할 때까지 일시적으로 제외됩니다.",
"manage_concurrency": "동시성 관리",
"manage_log_settings": "로그 설정 관리",
"map_dark_style": "다크 스타일",
@@ -291,7 +291,7 @@
"transcoding_max_b_frames": "최대 B 프레임",
"transcoding_max_b_frames_description": "값이 높으면 압축 효율이 향상되지만 인코딩 속도가 저하됩니다. 오래된 기기의 하드웨어 가속과 호환되지 않을 수 있습니다. 0을 입력한 경우 B 프레임을 비활성화하며, -1을 입력한 경우 자동으로 설정합니다.",
"transcoding_max_bitrate": "최대 비트레이트",
- "transcoding_max_bitrate_description": "최대 비트레이트를 지정하면 품질이 일부 저하되지만 파일 크기가 예측 가능한 수준으로 일정하게 유지됩니다. 일반적으로 720p 기준 VP9 및 HEVC는 2600k, H.264는 4500k를 사용합니다. 0을 입력한 경우 비활성화됩니다.",
+ "transcoding_max_bitrate_description": "최대 비트레이트를 지정하면 품질이 일부 저하되지만 파일 크기가 예측 가능한 수준으로 일정하게 유지됩니다. 일반적으로 720p 기준 VP9 및 HEVC는 2600 kbit/s, H.264는 4500 kbit/s를 사용합니다. 0을 입력한 경우 비활성화됩니다.",
"transcoding_max_keyframe_interval": "최대 키프레임 간격",
"transcoding_max_keyframe_interval_description": "키프레임 사이 최대 프레임 거리를 설정합니다. 값이 낮으면 압축 효율이 저하되지만 검색 시간이 개선되고 빠른 움직임이 있는 장면에서 품질이 향상됩니다. 0을 입력한 경우 자동으로 설정합니다.",
"transcoding_optimal_description": "목표 해상도보다 높은 동영상 또는 허용되지 않는 형식의 동영상",
@@ -1323,4 +1323,4 @@
"yes": "네",
"you_dont_have_any_shared_links": "생성한 공유 링크가 없습니다.",
"zoom_image": "이미지 확대"
-}
+}
\ No newline at end of file
diff --git a/i18n/lt.json b/i18n/lt.json
index 5e478a37a9..738dc7bc7f 100644
--- a/i18n/lt.json
+++ b/i18n/lt.json
@@ -20,7 +20,7 @@
"add_partner": "Pridėti partnerį",
"add_path": "Pridėti kelią",
"add_photos": "Pridėti nuotraukų",
- "add_to": "Pridėti į ...",
+ "add_to": "Pridėti į…",
"add_to_album": "Pridėti į albumą",
"add_to_shared_album": "Pridėti į bendrinamą albumą",
"add_url": "Pridėti URL",
@@ -50,6 +50,7 @@
"create_job": "Sukurti darbą",
"cron_expression": "Cron išraiška",
"cron_expression_description": "Nustatyti skanavimo intervalą naudojant cron formatą. Norėdami gauti daugiau informacijos žiūrėkite Crontab Guru",
+ "cron_expression_presets": "Išankstiniai Cron nustatymai",
"disable_login": "Išjungti prisijungimą",
"duplicate_detection_job_description": "Vykdykite mašininį mokymąsi panašių vaizdų aptikimui. Priklauso nuo išmaniosios paieškos",
"exclusion_pattern_description": "Išimčių šablonai leidžia nepaisyti failų ir aplankų skenuojant jūsų biblioteką. Tai yra naudinga, jei turite aplankų su failais, kurių nenorite importuoti, pavyzdžiui, RAW failai.",
@@ -142,6 +143,8 @@
"map_settings": "Žemėlapis",
"map_settings_description": "Tvarkyti žemėlapio parametrus",
"map_style_description": "URL į style.json žemėlapio temą",
+ "memory_cleanup_job": "Atsiminimų valymas",
+ "memory_generate_job": "Atsiminimų generavimas",
"metadata_extraction_job": "Metaduomenų nuskaitymas",
"metadata_extraction_job_description": "Kiekvieno bibliotekos elemento metaduomenų nuskaitymas, tokių kaip GPS koordinatės, veidai ar rezoliucija",
"metadata_faces_import_setting": "Įjungti veidų importą",
@@ -904,6 +907,8 @@
"removed_from_archive": "Pašalinta iš archyvo",
"removed_from_favorites": "Pašalinta iš mėgstamiausių",
"removed_from_favorites_count": "{count, plural, one {# pašalintas} few {# pašalinti} other {# pašalinta}} iš mėgstamiausių",
+ "removed_memory": "Atsiminimas pašalintas",
+ "removed_photo_from_memory": "Nuotrauka ištrinta iš atsiminimų",
"removed_tagged_assets": "Žyma pašalinta iš {count, plural, one {# elemento} other {# elementų}}",
"rename": "Pervadinti",
"repair": "Pataisyti",
diff --git a/i18n/lv.json b/i18n/lv.json
index 38b0bf6426..d962ae1bd1 100644
--- a/i18n/lv.json
+++ b/i18n/lv.json
@@ -20,7 +20,7 @@
"add_partner": "Pievienot partneri",
"add_path": "Pievienot ceļu",
"add_photos": "Pievienot fotoattēlus",
- "add_to": "Pievienot ..",
+ "add_to": "Pievienot…",
"add_to_album": "Pievienot albumam",
"add_to_shared_album": "Pievienot koplietotam albumam",
"add_url": "Pievienot URL",
@@ -49,7 +49,7 @@
"external_library_management": "Ārējo bibliotēku pārvaldība",
"face_detection": "Seju noteikšana",
"image_format": "Formāts",
- "image_format_description": "",
+ "image_format_description": "WebP veido mazākus failus nekā JPEG, taču to kodēšana ir lēnāka.",
"image_prefer_embedded_preview": "",
"image_prefer_embedded_preview_setting_description": "",
"image_prefer_wide_gamut": "",
@@ -299,12 +299,13 @@
"asset_offline": "",
"asset_uploading": "Augšupielādē...",
"assets": "aktīvi",
- "authorized_devices": "",
+ "authorized_devices": "Autorizētās ierīces",
"back": "Atpakaļ",
"backward": "",
"birthdate_saved": "Dzimšanas datums veiksmīgi saglabāts",
"birthdate_set_description": "Dzimšanas datums tiek izmantots, lai aprēķinātu šīs personas vecumu fotogrāfijas uzņemšanas brīdī.",
"blurred_background": "",
+ "bugs_and_feature_requests": "Kļūdas un funkciju pieprasījumi",
"camera": "",
"camera_brand": "",
"camera_model": "",
@@ -326,12 +327,13 @@
"clear": "Notīrīt",
"clear_all": "Notīrīt visu",
"clear_message": "",
- "clear_value": "",
+ "clear_value": "Notīrīt vērtību",
+ "clockwise": "Pulksteņrādītāja virzienā",
"close": "Aizvērt",
"collapse": "Sakļaut",
"collapse_all": "Sakļaut visu",
"color": "Krāsa",
- "color_theme": "",
+ "color_theme": "Krāsu tēma",
"comment_deleted": "Komentārs dzēsts",
"comment_options": "",
"comments_are_disabled": "",
diff --git a/i18n/mr.json b/i18n/mr.json
index 0967ef424b..ec05d6c702 100644
--- a/i18n/mr.json
+++ b/i18n/mr.json
@@ -1 +1,62 @@
-{}
+{
+ "about": "विषयी",
+ "account": "खाते",
+ "account_settings": "खाते व्यवस्था",
+ "acknowledge": "मान्यता",
+ "action": "कृती",
+ "actions": "कृत्ये",
+ "active": "सक्रिय",
+ "activity": "गतिविधि",
+ "activity_changed": "गतिविधि {enabled, select, true {enabled} other {disabled}}",
+ "add": "जोडा",
+ "add_a_description": "वर्णन करा",
+ "add_a_location": "एक स्थळ टाका",
+ "add_a_name": "नाव टाका",
+ "add_a_title": "शीर्षक टाका",
+ "add_exclusion_pattern": "अपवाद नमुना जोडा",
+ "add_import_path": "आयात मार्ग टाका",
+ "add_location": "स्थळ टाका",
+ "add_more_users": "अधिक वापरकर्ते जोडा",
+ "add_partner": "भागीदार जोडा",
+ "add_path": "मार्ग टाका",
+ "add_photos": "छायाचित्रे जोडा",
+ "add_to": "त्या मध्ये जोडा…",
+ "add_to_album": "संग्रहात टाका",
+ "add_to_shared_album": "सामायिक संग्रहात टाका",
+ "add_url": "URL जोडा",
+ "added_to_archive": "संग्रहालयात जोडले",
+ "added_to_favorites": "आवडत्यात टाकले",
+ "added_to_favorites_count": "आवडत्यात {count, number} टाकले",
+ "admin": {
+ "add_exclusion_pattern_description": "अपवाद अनुकूलन जोडा. ** आणि ? या उपयोगात ग्लोबिंग समर्थित आहे. कोणत्याही \"Raw\" नावाच्या निर्देशिकेमधील सर्व खतावण्या दुर्लक्षीत करण्यासाठी \"/Raw/\" वापरा. \".tif\" या सामान्य पथावर समाप्त असलेल्या सर्व खतावण्या दुर्लक्षीत करण्यासाठी \"**/.tif\" वापरा. विशिष्ट पथ दुर्लक्ष करण्यासाठी \"/path/to/ignore/**\" वापरा.",
+ "asset_offline_description": "ही बाह्य संग्रहालय संसाधने डिस्कवर नाहीत आणि ट्रॅशमध्ये विस्थापित केली गेली आहेत. जर फाइल संग्रहालयामध्ये विस्थापित केली गेली आहे, तर नवीन संगत संसाधन किंव्हा रोजीनिशी मध्ये तपासा. हा संसाधन वापर करण्यासाठी कृपया निम्नलिखित खतावणी पथाला इम्मीच द्वारा वापरू शकतो याची तपासणी करा आणि तो संग्रहालय चाळा.",
+ "authentication_settings": "प्रमाणीकरण साधक",
+ "authentication_settings_description": "परवलीचा शब्द, OAuth आणि अन्य प्रमाणीकरण प्रबंधन करा",
+ "authentication_settings_disable_all": "तुम्हाला खात्री आहे की तुम्ही सर्व प्रवेश पद्धती बंद करू इच्छिता? प्रवेश पूर्णपणे बंद होइल!.",
+ "authentication_settings_reenable": "परत चालू करण्यासाठी Server Command वापरा.",
+ "background_task_job": "पृष्ठभूमि कार्य",
+ "backup_database": "माहिती संचयाची सुरक्षित प्रत करा",
+ "backup_database_enable_description": "माहिती संचयाच्या प्रतिलिपी चालू करा",
+ "backup_keep_last_amount": "पूर्वीच्या किती प्रतिलिपी ठेवायच्या",
+ "backup_settings": "प्रतिलिपी व्यवस्था",
+ "backup_settings_description": "माहिती संचय प्रतिलिपी व्यवस्थापन",
+ "check_all": "सर्व तपासा",
+ "cleared_jobs": "{job}: च्या कार्यवाह्या काढल्या",
+ "config_set_by_file": "संरचना सध्या संरचना खतावणीद्वारे निश्चित केली आहे",
+ "confirm_delete_library": "तुम्हाला नक्की हे {library} संग्रहालय हटवायचे आहे का?",
+ "confirm_delete_library_assets": "तुम्हाला नक्की हे संग्रहालय हटवायचे आहे का? इम्मीच मधून {count, plural, one {# contained asset} other {all # contained assets}} काढले जातील, आणि पूर्ववत करता येणार नाहीत. छायाचित्रे डिस्क वर राहतील.",
+ "confirm_email_below": "पुष्टी करण्या साठी, खाली \"{email}\" टंकलिखित करा",
+ "confirm_reprocess_all_faces": "तुम्हाला खात्री आहे का की तुम्हाला सर्व चेहऱ्यांवर पुन्हा प्रक्रिया करायची आहे? यामुळे नाव दिलेले लोकही साफ होतील.",
+ "confirm_user_password_reset": "तुम्हाला नक्की {user} चा परवलीचा शब्द बदलायचा आहे का?",
+ "create_job": "कार्य बनवा",
+ "cron_expression": "वेळापत्रक सूत्र",
+ "cron_expression_description": "चाळन्याचे वेळापत्रक क्रॉन पद्धती ने करा. अधिक माहिती साठी पहा: क्रॉन गुरु",
+ "cron_expression_presets": "पूर्वनिर्धारित वेळापत्रक सूत्रे",
+ "disable_login": "प्रवेशाधिकर वर्ज्य करा",
+ "duplicate_detection_job_description": "सारख्या छायाचित्रांचा शोध घेण्यासाठी यांत्रिकी प्रशिक्षण द्या. ही कार्यक्षमता चतुर शोधप्रणालीवर अवलंबून आहे",
+ "exclusion_pattern_description": "आपले संग्रहालय चाळताना अपवाद नमुने आपल्याला खतावण्या आणि र्निर्देशिकेला दुर्लक्षीत करू देतात. आपल्याकडे कच्च्या खतावण्या सारख्या आयात करू इच्छित नसलेल्या असंपादित (RAW) खतावण्या असलेल्या निर्देशिका असल्यास हे उपयुक्त आहे.",
+ "external_library_created_at": "बाह्य संग्रहालय ({date} रोजी बनवले गेले)",
+ "external_library_management": "बाह्य संग्रहालय व्यवस्थापन",
+ "face_detection": "मुख संशोधन"
+ }
+}
diff --git a/i18n/ms.json b/i18n/ms.json
index 9916ca62a0..7da863750d 100644
--- a/i18n/ms.json
+++ b/i18n/ms.json
@@ -299,7 +299,7 @@
"transcoding_max_b_frames": "Bingkai-B maksimum",
"transcoding_max_b_frames_description": "Nilai yang lebih tinggi meningkatkan kecekapan mampatan, tetapi memperlahankan pengekodan. Mungkin tidak serasi dengan pecutan perkakasan pada peranti lama. 0 melumpuhkan bingkai B, manakala -1 menetapkan nilai ini secara automatik.",
"transcoding_max_bitrate": "Kadar bit maksimum",
- "transcoding_max_bitrate_description": "Menetapkan kadar bit maksima boleh menjadikan saiz fail lebih boleh diramal dengan kekurangan yang kecil kepada kualiti. Pada 720p, nilai biasa ialah 2600k untuk VP9 atau HEVC, atau 4500k untuk H.264. Dilumpuhkan jika ditetapkan kepada 0.",
+ "transcoding_max_bitrate_description": "Menetapkan kadar bit maksima boleh menjadikan saiz fail lebih boleh diramal dengan kekurangan yang kecil kepada kualiti. Pada 720p, nilai biasa ialah 2600 kbit/s untuk VP9 atau HEVC, atau 4500 kbit/s untuk H.264. Dilumpuhkan jika ditetapkan kepada 0.",
"transcoding_max_keyframe_interval": "Selangan keyframe maksimum",
"transcoding_max_keyframe_interval_description": "Menetapkan jarak bingkai maksimum antara keyframes. Nilai yang lebih rendah memburukkan kecekapan mampatan, tetapi menambah baik masa carian dan mungkin meningkatkan kualiti dalam adegan dengan pergerakan pantas. 0 menetapkan nilai ini secara automatik.",
"transcoding_optimal_description": "Video yang lebih tinggi daripada resolusi sasaran atau tidak dalam format yang diterima",
@@ -372,4 +372,4 @@
"yes": "Ya",
"you_dont_have_any_shared_links": "Anda tidak mempunyai apa-apa pautan yang dikongsi",
"zoom_image": "Zum Gambar"
-}
+}
\ No newline at end of file
diff --git a/i18n/nb_NO.json b/i18n/nb_NO.json
index b29c8c1db6..78f7941760 100644
--- a/i18n/nb_NO.json
+++ b/i18n/nb_NO.json
@@ -41,6 +41,7 @@
"backup_settings": "Backupinnstillinger",
"backup_settings_description": "Håndter innstillinger for databasebackup",
"check_all": "Merk Alle",
+ "cleanup": "Opprydding",
"cleared_jobs": "Ryddet opp jobber for: {job}",
"config_set_by_file": "Konfigurasjonen er for øyeblikket satt av en konfigurasjonsfil",
"confirm_delete_library": "Er du sikker på at du vil slette biblioteket {library}?",
@@ -96,7 +97,7 @@
"library_scanning_enable_description": "Aktiver periodisk skanning av bibliotek",
"library_settings": "Eksternt bibliotek",
"library_settings_description": "Administrer innstillinger for eksterne bibliotek",
- "library_tasks_description": "Utfør bibliotekoppgaver",
+ "library_tasks_description": "Skann eksterne biblioteker for nye og/eller endrede ressurser",
"library_watching_enable_description": "Overvåk eksterne bibliotek for filendringer",
"library_watching_settings": "Overvåkning av bibliotek (EKSPERIMENTELL)",
"library_watching_settings_description": "Se automatisk etter endrede filer",
@@ -131,7 +132,7 @@
"machine_learning_smart_search_description": "Søk etter bilder semantisk ved å bruke CLIP-embeddings",
"machine_learning_smart_search_enabled": "Aktiver smart søk",
"machine_learning_smart_search_enabled_description": "Hvis deaktivert, vil bilder ikke bli enkodet for smart søk.",
- "machine_learning_url_description": "URL til maskinlærings-serveren. Hvis mer enn en URL er lagt inn, hver server vill bli forsøkt en om gangen frem til en svarer suksessfullt, i rekkefølge fra først til sist.",
+ "machine_learning_url_description": "URL til maskinlærings-serveren. Hvis mer enn en URL er lagt inn, hver server vill bli forsøkt en om gangen frem til en svarer suksessfullt, i rekkefølge fra først til sist. Servere som ikke svarer vil midlertidig bli oversett frem til dem svarer igjen.",
"manage_concurrency": "Administrer samtidighet",
"manage_log_settings": "Administrer logginnstillinger",
"map_dark_style": "Mørk stil",
@@ -147,6 +148,8 @@
"map_settings": "Innstillinger for kart og GPS",
"map_settings_description": "Administrer kartinnstillinger",
"map_style_description": "URL til et style.json-karttema",
+ "memory_cleanup_job": "Minneopprydding",
+ "memory_generate_job": "Minnegenerering",
"metadata_extraction_job": "Hent metadata",
"metadata_extraction_job_description": "Hent metadatainformasjon fra hver fil, for eksempel GPS-posisjon og oppløsning",
"metadata_faces_import_setting": "Aktiver ansikts importering",
@@ -240,7 +243,7 @@
"storage_template_hash_verification_enabled_description": "Aktiver hasjverifisering. Ikke deaktiver dette med mindre du er sikker på konsekvensene",
"storage_template_migration": "Lagringsmal migrering",
"storage_template_migration_description": "Bruk gjeldende {template} på tidligere opplastede bilder",
- "storage_template_migration_info": "Malendringer vil kun gjelde nye ressurser. For å anvende malen på tidligere opplastede ressurser, kjør {job}.",
+ "storage_template_migration_info": "Lagringsmalen vil endre filtypen til små bokstaver. Malendringer vil kun gjelde nye ressurser. For å anvende malen på tidligere opplastede ressurser, kjør {job}.",
"storage_template_migration_job": "Migreringsjobb for lagringsmal",
"storage_template_more_details": "For mer informasjon om denne funksjonen, se (
switch (propertyId) {
case 0:
return (_UseravatarColorValueEnumMap[reader.readByteOrNull(offset)] ??
- AvatarColorEnum.primary) as P;
+ AvatarColor.primary) as P;
case 1:
return (reader.readString(offset)) as P;
case 2:
- return (reader.readBool(offset)) as P;
- case 3:
return (reader.readString(offset)) as P;
- case 4:
+ case 3:
return (reader.readBoolOrNull(offset) ?? false) as P;
- case 5:
+ case 4:
return (reader.readBool(offset)) as P;
+ case 5:
+ return (reader.readBoolOrNull(offset) ?? false) as P;
case 6:
return (reader.readBoolOrNull(offset) ?? false) as P;
case 7:
- return (reader.readBoolOrNull(offset) ?? false) as P;
- case 8:
return (reader.readBoolOrNull(offset) ?? true) as P;
- case 9:
+ case 8:
return (reader.readString(offset)) as P;
- case 10:
+ case 9:
return (reader.readStringOrNull(offset) ?? '') as P;
+ case 10:
+ return (reader.readLongOrNull(offset) ?? 0) as P;
case 11:
return (reader.readLongOrNull(offset) ?? 0) as P;
case 12:
- return (reader.readLongOrNull(offset) ?? 0) as P;
- case 13:
return (reader.readDateTime(offset)) as P;
default:
throw IsarError('Unknown property with id $propertyId');
@@ -247,16 +224,16 @@ const _UseravatarColorEnumValueMap = {
'amber': 9,
};
const _UseravatarColorValueEnumMap = {
- 0: AvatarColorEnum.primary,
- 1: AvatarColorEnum.pink,
- 2: AvatarColorEnum.red,
- 3: AvatarColorEnum.yellow,
- 4: AvatarColorEnum.blue,
- 5: AvatarColorEnum.green,
- 6: AvatarColorEnum.purple,
- 7: AvatarColorEnum.orange,
- 8: AvatarColorEnum.gray,
- 9: AvatarColorEnum.amber,
+ 0: AvatarColor.primary,
+ 1: AvatarColor.pink,
+ 2: AvatarColor.red,
+ 3: AvatarColor.yellow,
+ 4: AvatarColor.blue,
+ 5: AvatarColor.green,
+ 6: AvatarColor.purple,
+ 7: AvatarColor.orange,
+ 8: AvatarColor.gray,
+ 9: AvatarColor.amber,
};
Id _userGetId(User object) {
@@ -264,14 +241,10 @@ Id _userGetId(User object) {
}
List> updateAll(List
> watchUserSyncEvent() {
+ return _getSyncStream(
+ SyncStreamDto(types: [SyncRequestType.usersV1]),
+ );
+ }
+
+ @override
+ Future
> _getSyncStream(
+ SyncStreamDto dto, {
+ int batchSize = 5000,
+ }) async* {
+ final client = http.Client();
+ final endpoint = "${_api.apiClient.basePath}/sync/stream";
+
+ final headers =
> getAll({SortUserBy? sortBy}) async {
+ return (await _db.users
+ .where()
+ .optional(
+ sortBy != null,
+ (query) => switch (sortBy!) {
+ SortUserBy.id => query.sortById(),
+ },
+ )
+ .findAll())
+ .map((u) => u.toDto())
+ .toList();
+ }
+
+ @override
+ Future
> getByUserIds(List
> getAll() async {
+ final dto = await checkNull(_api.searchUsers());
+ return dto.map(UserConverter.fromSimpleUserDto).toList();
+ }
+}
diff --git a/mobile/lib/infrastructure/utils/exif.converter.dart b/mobile/lib/infrastructure/utils/exif.converter.dart
new file mode 100644
index 0000000000..0f6e2b0295
--- /dev/null
+++ b/mobile/lib/infrastructure/utils/exif.converter.dart
@@ -0,0 +1,56 @@
+import 'package:immich_mobile/domain/models/exif.model.dart';
+import 'package:openapi/api.dart';
+
+abstract final class ExifDtoConverter {
+ static ExifInfo fromDto(ExifResponseDto dto) {
+ return ExifInfo(
+ fileSize: dto.fileSizeInByte,
+ description: dto.description,
+ orientation: dto.orientation,
+ timeZone: dto.timeZone,
+ dateTimeOriginal: dto.dateTimeOriginal,
+ isFlipped: isOrientationFlipped(dto.orientation),
+ latitude: dto.latitude?.toDouble(),
+ longitude: dto.longitude?.toDouble(),
+ city: dto.city,
+ state: dto.state,
+ country: dto.country,
+ make: dto.make,
+ model: dto.model,
+ lens: dto.lensModel,
+ f: dto.fNumber?.toDouble(),
+ mm: dto.focalLength?.toDouble(),
+ iso: dto.iso?.toInt(),
+ exposureSeconds: _exposureTimeToSeconds(dto.exposureTime),
+ );
+ }
+
+ static bool isOrientationFlipped(String? orientation) {
+ final value = orientation == null ? null : int.tryParse(orientation);
+ if (value == null) {
+ return false;
+ }
+ final isRotated90CW = value == 5 || value == 6 || value == 90;
+ final isRotated270CW = value == 7 || value == 8 || value == -90;
+ return isRotated90CW || isRotated270CW;
+ }
+
+ static double? _exposureTimeToSeconds(String? s) {
+ if (s == null) {
+ return null;
+ }
+ double? value = double.tryParse(s);
+ if (value != null) {
+ return value;
+ }
+ final parts = s.split("/");
+ if (parts.length == 2) {
+ final numerator = double.tryParse(parts.firstOrNull ?? "-");
+ final denominator = double.tryParse(parts.lastOrNull ?? "-");
+ if (numerator != null && denominator != null) {
+ return numerator / denominator;
+ }
+ }
+ return null;
+ }
+}
diff --git a/mobile/lib/infrastructure/utils/user.converter.dart b/mobile/lib/infrastructure/utils/user.converter.dart
new file mode 100644
index 0000000000..11f9ddec33
--- /dev/null
+++ b/mobile/lib/infrastructure/utils/user.converter.dart
@@ -0,0 +1,66 @@
+import 'package:immich_mobile/domain/models/user.model.dart';
+import 'package:openapi/api.dart';
+
+abstract final class UserConverter {
+ /// Base user dto used where the complete user object is not required
+ static UserDto fromSimpleUserDto(UserResponseDto dto) => UserDto(
+ uid: dto.id,
+ email: dto.email,
+ name: dto.name,
+ isAdmin: false,
+ updatedAt: DateTime.now(),
+ profileImagePath: dto.profileImagePath,
+ avatarColor: dto.avatarColor.toAvatarColor(),
+ );
+
+ static UserDto fromAdminDto(
+ UserAdminResponseDto adminDto, [
+ UserPreferencesResponseDto? preferenceDto,
+ ]) =>
+ UserDto(
+ uid: adminDto.id,
+ email: adminDto.email,
+ name: adminDto.name,
+ isAdmin: adminDto.isAdmin,
+ updatedAt: adminDto.updatedAt,
+ profileImagePath: adminDto.profileImagePath,
+ avatarColor: adminDto.avatarColor.toAvatarColor(),
+ memoryEnabled: preferenceDto?.memories.enabled ?? true,
+ inTimeline: false,
+ isPartnerSharedBy: false,
+ isPartnerSharedWith: false,
+ quotaUsageInBytes: adminDto.quotaUsageInBytes ?? 0,
+ quotaSizeInBytes: adminDto.quotaSizeInBytes ?? 0,
+ );
+
+ static UserDto fromPartnerDto(PartnerResponseDto dto) => UserDto(
+ uid: dto.id,
+ email: dto.email,
+ name: dto.name,
+ isAdmin: false,
+ updatedAt: DateTime.now(),
+ profileImagePath: dto.profileImagePath,
+ avatarColor: dto.avatarColor.toAvatarColor(),
+ memoryEnabled: false,
+ inTimeline: dto.inTimeline ?? false,
+ isPartnerSharedBy: false,
+ isPartnerSharedWith: false,
+ quotaUsageInBytes: 0,
+ quotaSizeInBytes: 0,
+ );
+}
+
+extension on UserAvatarColor {
+ AvatarColor toAvatarColor() => switch (this) {
+ UserAvatarColor.red => AvatarColor.red,
+ UserAvatarColor.green => AvatarColor.green,
+ UserAvatarColor.blue => AvatarColor.blue,
+ UserAvatarColor.purple => AvatarColor.purple,
+ UserAvatarColor.orange => AvatarColor.orange,
+ UserAvatarColor.pink => AvatarColor.pink,
+ UserAvatarColor.amber => AvatarColor.amber,
+ UserAvatarColor.yellow => AvatarColor.yellow,
+ UserAvatarColor.gray => AvatarColor.gray,
+ UserAvatarColor.primary || _ => AvatarColor.primary,
+ };
+}
diff --git a/mobile/lib/interfaces/album.interface.dart b/mobile/lib/interfaces/album.interface.dart
index 3a83a8feb7..c1696eda80 100644
--- a/mobile/lib/interfaces/album.interface.dart
+++ b/mobile/lib/interfaces/album.interface.dart
@@ -1,6 +1,6 @@
+import 'package:immich_mobile/domain/models/user.model.dart';
import 'package:immich_mobile/entities/album.entity.dart';
import 'package:immich_mobile/entities/asset.entity.dart';
-import 'package:immich_mobile/entities/user.entity.dart';
import 'package:immich_mobile/interfaces/database.interface.dart';
import 'package:immich_mobile/models/albums/album_search.model.dart';
@@ -31,9 +31,9 @@ abstract interface class IAlbumRepository implements IDatabaseRepository {
Future
> updateAll(List
> getAllUniquePaths();
+ Future
> getAssetsForPath(String? path);
+}
diff --git a/mobile/lib/interfaces/partner.interface.dart b/mobile/lib/interfaces/partner.interface.dart
index 995e07c392..8e5fcb7a97 100644
--- a/mobile/lib/interfaces/partner.interface.dart
+++ b/mobile/lib/interfaces/partner.interface.dart
@@ -1,8 +1,8 @@
-import 'package:immich_mobile/entities/user.entity.dart';
+import 'package:immich_mobile/domain/models/user.model.dart';
abstract class IPartnerRepository {
- Future
> getSharedWith();
- Future
> getSharedBy();
- Stream
> watchSharedWith();
- Stream
> watchSharedBy();
+ Future
> getSharedWith();
+ Future
> getSharedBy();
+ Stream
> watchSharedWith();
+ Stream
> watchSharedBy();
}
diff --git a/mobile/lib/interfaces/partner_api.interface.dart b/mobile/lib/interfaces/partner_api.interface.dart
index bca1baf66d..01149f473c 100644
--- a/mobile/lib/interfaces/partner_api.interface.dart
+++ b/mobile/lib/interfaces/partner_api.interface.dart
@@ -1,9 +1,9 @@
-import 'package:immich_mobile/entities/user.entity.dart';
+import 'package:immich_mobile/domain/models/user.model.dart';
abstract interface class IPartnerApiRepository {
- Future
> getAll(Direction direction);
- Future
> getAll(Direction direction);
+ Future
> getByIds(List
> getAll({bool self = true, UserSort? sortBy});
-
- /// Returns all users whose assets can be accessed (self+partners)
- Future
> getAllAccessible();
-
- Future
> upsertAll(List
> getAll();
- Future<({String profileImagePath})> createProfileImage({
- required String name,
- required Uint8List data,
- });
-}
diff --git a/mobile/lib/mixins/error_logger.mixin.dart b/mobile/lib/mixins/error_logger.mixin.dart
index 466028c338..482420a978 100644
--- a/mobile/lib/mixins/error_logger.mixin.dart
+++ b/mobile/lib/mixins/error_logger.mixin.dart
@@ -6,6 +6,7 @@ typedef AsyncFuture
> suggestedShareUsers =
+ final AsyncValue
> suggestedShareUsers =
ref.watch(otherUsersProvider);
- final sharedUsersList = useState
>(const []);
+ final sharedUsers = useRef
>(const []);
sharedUsers.value = ref.watch(
currentAlbumProvider.select((album) {
if (album == null) {
@@ -23,7 +23,7 @@ class AlbumSharedUserIcons extends HookConsumerWidget {
return sharedUsers.value;
}
- return album.sharedUsers.toList(growable: false);
+ return album.sharedUsers.map((u) => u.toDto()).toList(growable: false);
}),
);
diff --git a/mobile/lib/pages/album/album_shared_user_selection.page.dart b/mobile/lib/pages/album/album_shared_user_selection.page.dart
index ed8a45194d..1c797aa449 100644
--- a/mobile/lib/pages/album/album_shared_user_selection.page.dart
+++ b/mobile/lib/pages/album/album_shared_user_selection.page.dart
@@ -3,14 +3,14 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:immich_mobile/domain/models/user.model.dart';
+import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:immich_mobile/extensions/asyncvalue_extensions.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/providers/album/album.provider.dart';
import 'package:immich_mobile/providers/album/album_title.provider.dart';
import 'package:immich_mobile/providers/album/suggested_shared_users.provider.dart';
import 'package:immich_mobile/routing/router.dart';
-import 'package:immich_mobile/entities/asset.entity.dart';
-import 'package:immich_mobile/entities/user.entity.dart';
import 'package:immich_mobile/widgets/common/user_circle_avatar.dart';
@RoutePage()
@@ -21,7 +21,7 @@ class AlbumSharedUserSelectionPage extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
- final sharedUsersList = useState