Compare commits
102 Commits
fix/map-pa
...
fix/folder
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4adf5b24f8 | ||
|
|
eca9b56847 | ||
|
|
5b0575b956 | ||
|
|
05064f87f0 | ||
|
|
522cdbac99 | ||
|
|
9240bbc6ff | ||
|
|
3751f8bc57 | ||
|
|
88b8afb8d6 | ||
|
|
2e13543d5d | ||
|
|
bcfc967d77 | ||
|
|
7d0e8f50f7 | ||
|
|
c759233d8c | ||
|
|
bfe32c2bb9 | ||
|
|
6c7b2e4b5c | ||
|
|
7edbeb2ed6 | ||
|
|
4e59a55c1d | ||
|
|
c2d7337d12 | ||
|
|
c1b82bed9b | ||
|
|
9ca31abae9 | ||
|
|
ebcf133bea | ||
|
|
1923f1a887 | ||
|
|
ce14324c97 | ||
|
|
6a309129b7 | ||
|
|
bcb1bf4692 | ||
|
|
7f89999abe | ||
|
|
813186e618 | ||
|
|
20d9204ada | ||
|
|
3a9e79a452 | ||
|
|
03966146fe | ||
|
|
ecc58a8971 | ||
|
|
c705a7b280 | ||
|
|
ef278b4fb0 | ||
|
|
4cd633dc68 | ||
|
|
a18c6fa910 | ||
|
|
90aa0dc14d | ||
|
|
ce8c80dad0 | ||
|
|
81eb98d4e5 | ||
|
|
2b03802e9c | ||
|
|
484311e9bb | ||
|
|
366539bc4c | ||
|
|
69b1331026 | ||
|
|
af30d97668 | ||
|
|
9b047d30e4 | ||
|
|
6a5597b36b | ||
|
|
c10b795e99 | ||
|
|
b606d4fe73 | ||
|
|
4c2ad44303 | ||
|
|
698d3004b4 | ||
|
|
fe4d6edbdc | ||
|
|
798debfde3 | ||
|
|
6563fa608a | ||
|
|
1a90fc8e58 | ||
|
|
c707f9cef4 | ||
|
|
6fda863c08 | ||
|
|
373b654156 | ||
|
|
a5d84ba552 | ||
|
|
1dc8fa2979 | ||
|
|
0426699f13 | ||
|
|
8154ec29df | ||
|
|
3024cd343b | ||
|
|
0b44d4b6f2 | ||
|
|
a04c6ed80d | ||
|
|
1c50e19894 | ||
|
|
e61d7f2616 | ||
|
|
a6b0869714 | ||
|
|
9c25b8ba7d | ||
|
|
3c72f489d8 | ||
|
|
1f2c779b36 | ||
|
|
5c74f634b7 | ||
|
|
ecc99bfd16 | ||
|
|
ff4d70e351 | ||
|
|
42c2389eb5 | ||
|
|
33c9f88ba4 | ||
|
|
11c469907f | ||
|
|
7c43e6c3c8 | ||
|
|
00aa385972 | ||
|
|
a5ed453929 | ||
|
|
dd8969cb7d | ||
|
|
bce4f93c90 | ||
|
|
a4c0dc5007 | ||
|
|
d233a7d97a | ||
|
|
5cdbb65d28 | ||
|
|
3434544864 | ||
|
|
269bf4b344 | ||
|
|
f9435a538b | ||
|
|
10e2ec2841 | ||
|
|
fe91b44ab9 | ||
|
|
747a72120e | ||
|
|
910661e75c | ||
|
|
c8a135a7ae | ||
|
|
08d1cf5bde | ||
|
|
3e62497fd0 | ||
|
|
a1bc862a32 | ||
|
|
75bf3aa1be | ||
|
|
38e68d16f9 | ||
|
|
caf11fbb96 | ||
|
|
f99c6feac5 | ||
|
|
5122512f19 | ||
|
|
49ed212af8 | ||
|
|
e29103b69f | ||
|
|
14b771d7c7 | ||
|
|
07aa51638c |
@@ -55,7 +55,7 @@
|
|||||||
"userEnvProbe": "loginInteractiveShell",
|
"userEnvProbe": "loginInteractiveShell",
|
||||||
"remoteEnv": {
|
"remoteEnv": {
|
||||||
// The location where your uploaded files are stored
|
// The location where your uploaded files are stored
|
||||||
"UPLOAD_LOCATION": "${localEnv:UPLOAD_LOCATION:upload-devcontainer-volume}",
|
"UPLOAD_LOCATION": "${localEnv:UPLOAD_LOCATION:./library}",
|
||||||
// Connection secret for postgres. You should change it to a random password
|
// Connection secret for postgres. You should change it to a random password
|
||||||
// Please use only the characters `A-Za-z0-9`, without special characters or spaces
|
// Please use only the characters `A-Za-z0-9`, without special characters or spaces
|
||||||
"DB_PASSWORD": "${localEnv:DB_PASSWORD:postgres}",
|
"DB_PASSWORD": "${localEnv:DB_PASSWORD:postgres}",
|
||||||
|
|||||||
@@ -51,14 +51,19 @@ fix_permissions() {
|
|||||||
|
|
||||||
run_cmd sudo find "${IMMICH_WORKSPACE}/server/upload" -not -path "${IMMICH_WORKSPACE}/server/upload/postgres/*" -not -path "${IMMICH_WORKSPACE}/server/upload/postgres" -exec chown node {} +
|
run_cmd sudo find "${IMMICH_WORKSPACE}/server/upload" -not -path "${IMMICH_WORKSPACE}/server/upload/postgres/*" -not -path "${IMMICH_WORKSPACE}/server/upload/postgres" -exec chown node {} +
|
||||||
|
|
||||||
run_cmd sudo chown node -R "${IMMICH_WORKSPACE}/.vscode" \
|
# Change ownership for directories that exist
|
||||||
|
for dir in "${IMMICH_WORKSPACE}/.vscode" \
|
||||||
"${IMMICH_WORKSPACE}/cli/node_modules" \
|
"${IMMICH_WORKSPACE}/cli/node_modules" \
|
||||||
"${IMMICH_WORKSPACE}/e2e/node_modules" \
|
"${IMMICH_WORKSPACE}/e2e/node_modules" \
|
||||||
"${IMMICH_WORKSPACE}/open-api/typescript-sdk/node_modules" \
|
"${IMMICH_WORKSPACE}/open-api/typescript-sdk/node_modules" \
|
||||||
"${IMMICH_WORKSPACE}/server/node_modules" \
|
"${IMMICH_WORKSPACE}/server/node_modules" \
|
||||||
"${IMMICH_WORKSPACE}/server/dist" \
|
"${IMMICH_WORKSPACE}/server/dist" \
|
||||||
"${IMMICH_WORKSPACE}/web/node_modules" \
|
"${IMMICH_WORKSPACE}/web/node_modules" \
|
||||||
"${IMMICH_WORKSPACE}/web/dist"
|
"${IMMICH_WORKSPACE}/web/dist"; do
|
||||||
|
if [ -d "$dir" ]; then
|
||||||
|
run_cmd sudo chown node -R "$dir"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
log ""
|
log ""
|
||||||
}
|
}
|
||||||
@@ -68,10 +73,10 @@ install_dependencies() {
|
|||||||
log "Installing dependencies"
|
log "Installing dependencies"
|
||||||
(
|
(
|
||||||
cd "${IMMICH_WORKSPACE}" || exit 1
|
cd "${IMMICH_WORKSPACE}" || exit 1
|
||||||
run_cmd make install-server
|
run_cmd make ci-server
|
||||||
run_cmd make install-sdk
|
run_cmd make ci-sdk
|
||||||
run_cmd make build-sdk
|
run_cmd make build-sdk
|
||||||
run_cmd make install-web
|
run_cmd make ci-web
|
||||||
)
|
)
|
||||||
log ""
|
log ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ services:
|
|||||||
build:
|
build:
|
||||||
target: dev-container-server
|
target: dev-container-server
|
||||||
env_file: !reset []
|
env_file: !reset []
|
||||||
|
hostname: immich-dev
|
||||||
environment:
|
environment:
|
||||||
- IMMICH_SERVER_URL=http://127.0.0.1:2283/
|
- IMMICH_SERVER_URL=http://127.0.0.1:2283/
|
||||||
volumes: !override
|
volumes: !override
|
||||||
@@ -12,8 +13,8 @@ services:
|
|||||||
- open_api_node_modules:/workspaces/immich/open-api/typescript-sdk/node_modules
|
- open_api_node_modules:/workspaces/immich/open-api/typescript-sdk/node_modules
|
||||||
- server_node_modules:/workspaces/immich/server/node_modules
|
- server_node_modules:/workspaces/immich/server/node_modules
|
||||||
- web_node_modules:/workspaces/immich/web/node_modules
|
- web_node_modules:/workspaces/immich/web/node_modules
|
||||||
- ${UPLOAD_LOCATION-./Library}/photos:/workspaces/immich/server/upload
|
- ${UPLOAD_LOCATION:-upload1-devcontainer-volume}${UPLOAD_LOCATION:+/photos}:/workspaces/immich/server/upload
|
||||||
- ${UPLOAD_LOCATION-./Library}/photos/upload:/workspaces/immich/server/upload/upload
|
- ${UPLOAD_LOCATION:-upload2-devcontainer-volume}${UPLOAD_LOCATION:+/photos/upload}:/workspaces/immich/server/upload/upload
|
||||||
- /etc/localtime:/etc/localtime:ro
|
- /etc/localtime:/etc/localtime:ro
|
||||||
|
|
||||||
immich-web:
|
immich-web:
|
||||||
@@ -29,8 +30,9 @@ services:
|
|||||||
POSTGRES_USER: ${DB_USERNAME-postgres}
|
POSTGRES_USER: ${DB_USERNAME-postgres}
|
||||||
POSTGRES_DB: ${DB_DATABASE_NAME-immich}
|
POSTGRES_DB: ${DB_DATABASE_NAME-immich}
|
||||||
POSTGRES_INITDB_ARGS: '--data-checksums'
|
POSTGRES_INITDB_ARGS: '--data-checksums'
|
||||||
volumes:
|
POSTGRES_HOST_AUTH_METHOD: md5
|
||||||
- ${UPLOAD_LOCATION-./Library}/postgres:/var/lib/postgresql/data
|
volumes:
|
||||||
|
- ${UPLOAD_LOCATION:-postgres-devcontainer-volume}${UPLOAD_LOCATION:+/postgres}:/var/lib/postgresql/data
|
||||||
|
|
||||||
redis:
|
redis:
|
||||||
env_file: !reset []
|
env_file: !reset []
|
||||||
@@ -42,4 +44,6 @@ volumes:
|
|||||||
open_api_node_modules:
|
open_api_node_modules:
|
||||||
server_node_modules:
|
server_node_modules:
|
||||||
web_node_modules:
|
web_node_modules:
|
||||||
upload-devcontainer-volume:
|
upload1-devcontainer-volume:
|
||||||
|
upload2-devcontainer-volume:
|
||||||
|
postgres-devcontainer-volume:
|
||||||
|
|||||||
@@ -6,7 +6,11 @@ design/
|
|||||||
docker/
|
docker/
|
||||||
!docker/scripts
|
!docker/scripts
|
||||||
docs/
|
docs/
|
||||||
|
!docs/package.json
|
||||||
|
!docs/package-lock.json
|
||||||
e2e/
|
e2e/
|
||||||
|
!e2e/package.json
|
||||||
|
!e2e/package-lock.json
|
||||||
fastlane/
|
fastlane/
|
||||||
machine-learning/
|
machine-learning/
|
||||||
misc/
|
misc/
|
||||||
|
|||||||
2
.github/workflows/build-mobile.yml
vendored
2
.github/workflows/build-mobile.yml
vendored
@@ -73,7 +73,7 @@ jobs:
|
|||||||
cache: 'gradle'
|
cache: 'gradle'
|
||||||
|
|
||||||
- name: Setup Flutter SDK
|
- name: Setup Flutter SDK
|
||||||
uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046 # v2.19.0
|
uses: subosito/flutter-action@395322a6cded4e9ed503aebd4cc1965625f8e59a # v2.20.0
|
||||||
with:
|
with:
|
||||||
channel: 'stable'
|
channel: 'stable'
|
||||||
flutter-version-file: ./mobile/pubspec.yaml
|
flutter-version-file: ./mobile/pubspec.yaml
|
||||||
|
|||||||
7
.github/workflows/cli.yml
vendored
7
.github/workflows/cli.yml
vendored
@@ -38,6 +38,9 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
node-version-file: './cli/.nvmrc'
|
node-version-file: './cli/.nvmrc'
|
||||||
registry-url: 'https://registry.npmjs.org'
|
registry-url: 'https://registry.npmjs.org'
|
||||||
|
cache: 'npm'
|
||||||
|
cache-dependency-path: '**/package-lock.json'
|
||||||
|
|
||||||
- name: Prepare SDK
|
- name: Prepare SDK
|
||||||
run: npm ci --prefix ../open-api/typescript-sdk/
|
run: npm ci --prefix ../open-api/typescript-sdk/
|
||||||
- name: Build SDK
|
- name: Build SDK
|
||||||
@@ -67,7 +70,7 @@ jobs:
|
|||||||
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
|
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
|
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
||||||
@@ -96,7 +99,7 @@ jobs:
|
|||||||
type=raw,value=latest,enable=${{ github.event_name == 'release' }}
|
type=raw,value=latest,enable=${{ github.event_name == 'release' }}
|
||||||
|
|
||||||
- name: Build and push image
|
- name: Build and push image
|
||||||
uses: docker/build-push-action@1dc73863535b631f98b2378be8619f83b136f4a0 # v6.17.0
|
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
|
||||||
with:
|
with:
|
||||||
file: cli/Dockerfile
|
file: cli/Dockerfile
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/amd64,linux/arm64
|
||||||
|
|||||||
6
.github/workflows/codeql-analysis.yml
vendored
6
.github/workflows/codeql-analysis.yml
vendored
@@ -50,7 +50,7 @@ jobs:
|
|||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
|
uses: github/codeql-action/init@ce28f5bb42b7a9f2c824e633a3f6ee835bab6858 # v3.29.0
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||||
@@ -63,7 +63,7 @@ jobs:
|
|||||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
# 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)
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
- name: Autobuild
|
- name: Autobuild
|
||||||
uses: github/codeql-action/autobuild@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
|
uses: github/codeql-action/autobuild@ce28f5bb42b7a9f2c824e633a3f6ee835bab6858 # v3.29.0
|
||||||
|
|
||||||
# ℹ️ Command-line programs to run using the OS shell.
|
# ℹ️ 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
|
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||||
@@ -76,6 +76,6 @@ jobs:
|
|||||||
# ./location_of_script_within_repo/buildscript.sh
|
# ./location_of_script_within_repo/buildscript.sh
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
|
uses: github/codeql-action/analyze@ce28f5bb42b7a9f2c824e633a3f6ee835bab6858 # v3.29.0
|
||||||
with:
|
with:
|
||||||
category: '/language:${{matrix.language}}'
|
category: '/language:${{matrix.language}}'
|
||||||
|
|||||||
4
.github/workflows/docker.yml
vendored
4
.github/workflows/docker.yml
vendored
@@ -177,7 +177,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: always()
|
if: always()
|
||||||
steps:
|
steps:
|
||||||
- uses: immich-app/devtools/actions/success-check@6b81b1572e466f7f48ba3c823159ce3f4a4d66a6 # success-check-action-0.0.3
|
- uses: immich-app/devtools/actions/success-check@68f10eb389bb02a3cf9d1156111964c549eb421b # 0.0.4
|
||||||
with:
|
with:
|
||||||
needs: ${{ toJSON(needs) }}
|
needs: ${{ toJSON(needs) }}
|
||||||
|
|
||||||
@@ -188,6 +188,6 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: always()
|
if: always()
|
||||||
steps:
|
steps:
|
||||||
- uses: immich-app/devtools/actions/success-check@6b81b1572e466f7f48ba3c823159ce3f4a4d66a6 # success-check-action-0.0.3
|
- uses: immich-app/devtools/actions/success-check@68f10eb389bb02a3cf9d1156111964c549eb421b # 0.0.4
|
||||||
with:
|
with:
|
||||||
needs: ${{ toJSON(needs) }}
|
needs: ${{ toJSON(needs) }}
|
||||||
|
|||||||
2
.github/workflows/docs-build.yml
vendored
2
.github/workflows/docs-build.yml
vendored
@@ -57,6 +57,8 @@ jobs:
|
|||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './docs/.nvmrc'
|
node-version-file: './docs/.nvmrc'
|
||||||
|
cache: 'npm'
|
||||||
|
cache-dependency-path: '**/package-lock.json'
|
||||||
|
|
||||||
- name: Run npm install
|
- name: Run npm install
|
||||||
run: npm ci
|
run: npm ci
|
||||||
|
|||||||
2
.github/workflows/fix-format.yml
vendored
2
.github/workflows/fix-format.yml
vendored
@@ -32,6 +32,8 @@ jobs:
|
|||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
|
cache: 'npm'
|
||||||
|
cache-dependency-path: '**/package-lock.json'
|
||||||
|
|
||||||
- name: Fix formatting
|
- name: Fix formatting
|
||||||
run: make install-all && make format-all
|
run: make install-all && make format-all
|
||||||
|
|||||||
2
.github/workflows/prepare-release.yml
vendored
2
.github/workflows/prepare-release.yml
vendored
@@ -100,7 +100,7 @@ jobs:
|
|||||||
name: release-apk-signed
|
name: release-apk-signed
|
||||||
|
|
||||||
- name: Create draft release
|
- name: Create draft release
|
||||||
uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631 # v2.2.2
|
uses: softprops/action-gh-release@72f2c25fcb47643c292f7107632f7a47c1df5cd8 # v2.3.2
|
||||||
with:
|
with:
|
||||||
draft: true
|
draft: true
|
||||||
tag_name: ${{ env.IMMICH_VERSION }}
|
tag_name: ${{ env.IMMICH_VERSION }}
|
||||||
|
|||||||
2
.github/workflows/sdk.yml
vendored
2
.github/workflows/sdk.yml
vendored
@@ -25,6 +25,8 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
node-version-file: './open-api/typescript-sdk/.nvmrc'
|
node-version-file: './open-api/typescript-sdk/.nvmrc'
|
||||||
registry-url: 'https://registry.npmjs.org'
|
registry-url: 'https://registry.npmjs.org'
|
||||||
|
cache: 'npm'
|
||||||
|
cache-dependency-path: '**/package-lock.json'
|
||||||
- name: Install deps
|
- name: Install deps
|
||||||
run: npm ci
|
run: npm ci
|
||||||
- name: Build
|
- name: Build
|
||||||
|
|||||||
6
.github/workflows/static_analysis.yml
vendored
6
.github/workflows/static_analysis.yml
vendored
@@ -49,7 +49,7 @@ jobs:
|
|||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup Flutter SDK
|
- name: Setup Flutter SDK
|
||||||
uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046 # v2.19.0
|
uses: subosito/flutter-action@395322a6cded4e9ed503aebd4cc1965625f8e59a # v2.20.0
|
||||||
with:
|
with:
|
||||||
channel: 'stable'
|
channel: 'stable'
|
||||||
flutter-version-file: ./mobile/pubspec.yaml
|
flutter-version-file: ./mobile/pubspec.yaml
|
||||||
@@ -109,7 +109,7 @@ jobs:
|
|||||||
working-directory: ./mobile
|
working-directory: ./mobile
|
||||||
|
|
||||||
- name: Run DCM
|
- name: Run DCM
|
||||||
run: dcm analyze lib
|
run: dcm analyze lib --fatal-style --fatal-warnings
|
||||||
working-directory: ./mobile
|
working-directory: ./mobile
|
||||||
|
|
||||||
zizmor:
|
zizmor:
|
||||||
@@ -134,7 +134,7 @@ jobs:
|
|||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Upload SARIF file
|
- name: Upload SARIF file
|
||||||
uses: github/codeql-action/upload-sarif@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
|
uses: github/codeql-action/upload-sarif@ce28f5bb42b7a9f2c824e633a3f6ee835bab6858 # v3.29.0
|
||||||
with:
|
with:
|
||||||
sarif_file: results.sarif
|
sarif_file: results.sarif
|
||||||
category: zizmor
|
category: zizmor
|
||||||
|
|||||||
34
.github/workflows/test.yml
vendored
34
.github/workflows/test.yml
vendored
@@ -84,6 +84,8 @@ jobs:
|
|||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
|
cache: 'npm'
|
||||||
|
cache-dependency-path: '**/package-lock.json'
|
||||||
|
|
||||||
- name: Run npm install
|
- name: Run npm install
|
||||||
run: npm ci
|
run: npm ci
|
||||||
@@ -125,6 +127,8 @@ jobs:
|
|||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './cli/.nvmrc'
|
node-version-file: './cli/.nvmrc'
|
||||||
|
cache: 'npm'
|
||||||
|
cache-dependency-path: '**/package-lock.json'
|
||||||
|
|
||||||
- name: Setup typescript-sdk
|
- name: Setup typescript-sdk
|
||||||
run: npm ci && npm run build
|
run: npm ci && npm run build
|
||||||
@@ -170,6 +174,8 @@ jobs:
|
|||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './cli/.nvmrc'
|
node-version-file: './cli/.nvmrc'
|
||||||
|
cache: 'npm'
|
||||||
|
cache-dependency-path: '**/package-lock.json'
|
||||||
|
|
||||||
- name: Setup typescript-sdk
|
- name: Setup typescript-sdk
|
||||||
run: npm ci && npm run build
|
run: npm ci && npm run build
|
||||||
@@ -208,6 +214,8 @@ jobs:
|
|||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './web/.nvmrc'
|
node-version-file: './web/.nvmrc'
|
||||||
|
cache: 'npm'
|
||||||
|
cache-dependency-path: '**/package-lock.json'
|
||||||
|
|
||||||
- name: Run setup typescript-sdk
|
- name: Run setup typescript-sdk
|
||||||
run: npm ci && npm run build
|
run: npm ci && npm run build
|
||||||
@@ -249,6 +257,8 @@ jobs:
|
|||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './web/.nvmrc'
|
node-version-file: './web/.nvmrc'
|
||||||
|
cache: 'npm'
|
||||||
|
cache-dependency-path: '**/package-lock.json'
|
||||||
|
|
||||||
- name: Run setup typescript-sdk
|
- name: Run setup typescript-sdk
|
||||||
run: npm ci && npm run build
|
run: npm ci && npm run build
|
||||||
@@ -282,6 +292,8 @@ jobs:
|
|||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './web/.nvmrc'
|
node-version-file: './web/.nvmrc'
|
||||||
|
cache: 'npm'
|
||||||
|
cache-dependency-path: '**/package-lock.json'
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm --prefix=web ci
|
run: npm --prefix=web ci
|
||||||
@@ -326,6 +338,8 @@ jobs:
|
|||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './e2e/.nvmrc'
|
node-version-file: './e2e/.nvmrc'
|
||||||
|
cache: 'npm'
|
||||||
|
cache-dependency-path: '**/package-lock.json'
|
||||||
|
|
||||||
- name: Run setup typescript-sdk
|
- name: Run setup typescript-sdk
|
||||||
run: npm ci && npm run build
|
run: npm ci && npm run build
|
||||||
@@ -369,6 +383,8 @@ jobs:
|
|||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
|
cache: 'npm'
|
||||||
|
cache-dependency-path: '**/package-lock.json'
|
||||||
|
|
||||||
- name: Run npm install
|
- name: Run npm install
|
||||||
run: npm ci
|
run: npm ci
|
||||||
@@ -402,6 +418,8 @@ jobs:
|
|||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './e2e/.nvmrc'
|
node-version-file: './e2e/.nvmrc'
|
||||||
|
cache: 'npm'
|
||||||
|
cache-dependency-path: '**/package-lock.json'
|
||||||
|
|
||||||
- name: Run setup typescript-sdk
|
- name: Run setup typescript-sdk
|
||||||
run: npm ci && npm run build
|
run: npm ci && npm run build
|
||||||
@@ -450,6 +468,8 @@ jobs:
|
|||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './e2e/.nvmrc'
|
node-version-file: './e2e/.nvmrc'
|
||||||
|
cache: 'npm'
|
||||||
|
cache-dependency-path: '**/package-lock.json'
|
||||||
|
|
||||||
- name: Run setup typescript-sdk
|
- name: Run setup typescript-sdk
|
||||||
run: npm ci && npm run build
|
run: npm ci && npm run build
|
||||||
@@ -461,7 +481,7 @@ jobs:
|
|||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
- name: Install Playwright Browsers
|
- name: Install Playwright Browsers
|
||||||
run: npx playwright install --with-deps chromium
|
run: npx playwright install chromium --only-shell
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
- name: Docker build
|
- name: Docker build
|
||||||
@@ -479,7 +499,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: always()
|
if: always()
|
||||||
steps:
|
steps:
|
||||||
- uses: immich-app/devtools/actions/success-check@6b81b1572e466f7f48ba3c823159ce3f4a4d66a6 # success-check-action-0.0.3
|
- uses: immich-app/devtools/actions/success-check@68f10eb389bb02a3cf9d1156111964c549eb421b # 0.0.4
|
||||||
with:
|
with:
|
||||||
needs: ${{ toJSON(needs) }}
|
needs: ${{ toJSON(needs) }}
|
||||||
|
|
||||||
@@ -496,7 +516,7 @@ jobs:
|
|||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup Flutter SDK
|
- name: Setup Flutter SDK
|
||||||
uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046 # v2.19.0
|
uses: subosito/flutter-action@395322a6cded4e9ed503aebd4cc1965625f8e59a # v2.20.0
|
||||||
with:
|
with:
|
||||||
channel: 'stable'
|
channel: 'stable'
|
||||||
flutter-version-file: ./mobile/pubspec.yaml
|
flutter-version-file: ./mobile/pubspec.yaml
|
||||||
@@ -568,6 +588,8 @@ jobs:
|
|||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './.github/.nvmrc'
|
node-version-file: './.github/.nvmrc'
|
||||||
|
cache: 'npm'
|
||||||
|
cache-dependency-path: '**/package-lock.json'
|
||||||
|
|
||||||
- name: Run npm install
|
- name: Run npm install
|
||||||
run: npm ci
|
run: npm ci
|
||||||
@@ -609,6 +631,8 @@ jobs:
|
|||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
|
cache: 'npm'
|
||||||
|
cache-dependency-path: '**/package-lock.json'
|
||||||
|
|
||||||
- name: Install server dependencies
|
- name: Install server dependencies
|
||||||
run: npm --prefix=server ci
|
run: npm --prefix=server ci
|
||||||
@@ -644,7 +668,7 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
services:
|
services:
|
||||||
postgres:
|
postgres:
|
||||||
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.1
|
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3@sha256:1f5583fe3397210a0fbc7f11b0cec18bacc4a99e3e8ea0548e9bd6bcf26ec37a
|
||||||
env:
|
env:
|
||||||
POSTGRES_PASSWORD: postgres
|
POSTGRES_PASSWORD: postgres
|
||||||
POSTGRES_USER: postgres
|
POSTGRES_USER: postgres
|
||||||
@@ -670,6 +694,8 @@ jobs:
|
|||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
|
cache: 'npm'
|
||||||
|
cache-dependency-path: '**/package-lock.json'
|
||||||
|
|
||||||
- name: Install server dependencies
|
- name: Install server dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
|
|||||||
2
.github/workflows/weblate-lock.yml
vendored
2
.github/workflows/weblate-lock.yml
vendored
@@ -52,6 +52,6 @@ jobs:
|
|||||||
permissions: {}
|
permissions: {}
|
||||||
if: always()
|
if: always()
|
||||||
steps:
|
steps:
|
||||||
- uses: immich-app/devtools/actions/success-check@6b81b1572e466f7f48ba3c823159ce3f4a4d66a6 # success-check-action-0.0.3
|
- uses: immich-app/devtools/actions/success-check@68f10eb389bb02a3cf9d1156111964c549eb421b # 0.0.4
|
||||||
with:
|
with:
|
||||||
needs: ${{ toJSON(needs) }}
|
needs: ${{ toJSON(needs) }}
|
||||||
|
|||||||
5
Makefile
5
Makefile
@@ -48,6 +48,8 @@ audit-%:
|
|||||||
npm --prefix $(subst sdk,open-api/typescript-sdk,$*) audit fix
|
npm --prefix $(subst sdk,open-api/typescript-sdk,$*) audit fix
|
||||||
install-%:
|
install-%:
|
||||||
npm --prefix $(subst sdk,open-api/typescript-sdk,$*) i
|
npm --prefix $(subst sdk,open-api/typescript-sdk,$*) i
|
||||||
|
ci-%:
|
||||||
|
npm --prefix $(subst sdk,open-api/typescript-sdk,$*) ci
|
||||||
build-cli: build-sdk
|
build-cli: build-sdk
|
||||||
build-web: build-sdk
|
build-web: build-sdk
|
||||||
build-%: install-%
|
build-%: install-%
|
||||||
@@ -81,7 +83,8 @@ test-medium-dev:
|
|||||||
docker exec -it immich_server /bin/sh -c "npm run test:medium"
|
docker exec -it immich_server /bin/sh -c "npm run test:medium"
|
||||||
|
|
||||||
build-all: $(foreach M,$(filter-out e2e .github,$(MODULES)),build-$M) ;
|
build-all: $(foreach M,$(filter-out e2e .github,$(MODULES)),build-$M) ;
|
||||||
install-all: $(foreach M,$(MODULES),install-$M) ;
|
install-all: $(foreach M,$(filter-out .github,$(MODULES)),install-$M) ;
|
||||||
|
ci-all: $(foreach M,$(filter-out .github,$(MODULES)),ci-$M) ;
|
||||||
check-all: $(foreach M,$(filter-out sdk cli docs .github,$(MODULES)),check-$M) ;
|
check-all: $(foreach M,$(filter-out sdk cli docs .github,$(MODULES)),check-$M) ;
|
||||||
lint-all: $(foreach M,$(filter-out sdk docs .github,$(MODULES)),lint-$M) ;
|
lint-all: $(foreach M,$(filter-out sdk docs .github,$(MODULES)),lint-$M) ;
|
||||||
format-all: $(foreach M,$(filter-out sdk,$(MODULES)),format-$M) ;
|
format-all: $(foreach M,$(filter-out sdk,$(MODULES)),format-$M) ;
|
||||||
|
|||||||
10
cli/package-lock.json
generated
10
cli/package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@immich/cli",
|
"name": "@immich/cli",
|
||||||
"version": "2.2.69",
|
"version": "2.2.72",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@immich/cli",
|
"name": "@immich/cli",
|
||||||
"version": "2.2.69",
|
"version": "2.2.72",
|
||||||
"license": "GNU Affero General Public License version 3",
|
"license": "GNU Affero General Public License version 3",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chokidar": "^4.0.3",
|
"chokidar": "^4.0.3",
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"@types/micromatch": "^4.0.9",
|
"@types/micromatch": "^4.0.9",
|
||||||
"@types/mock-fs": "^4.13.1",
|
"@types/mock-fs": "^4.13.1",
|
||||||
"@types/node": "^22.15.31",
|
"@types/node": "^22.15.32",
|
||||||
"@vitest/coverage-v8": "^3.0.0",
|
"@vitest/coverage-v8": "^3.0.0",
|
||||||
"byte-size": "^9.0.0",
|
"byte-size": "^9.0.0",
|
||||||
"cli-progress": "^3.12.0",
|
"cli-progress": "^3.12.0",
|
||||||
@@ -54,14 +54,14 @@
|
|||||||
},
|
},
|
||||||
"../open-api/typescript-sdk": {
|
"../open-api/typescript-sdk": {
|
||||||
"name": "@immich/sdk",
|
"name": "@immich/sdk",
|
||||||
"version": "1.135.0",
|
"version": "1.135.3",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "GNU Affero General Public License version 3",
|
"license": "GNU Affero General Public License version 3",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@oazapfts/runtime": "^1.0.2"
|
"@oazapfts/runtime": "^1.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^22.15.31",
|
"@types/node": "^22.15.32",
|
||||||
"typescript": "^5.3.3"
|
"typescript": "^5.3.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@immich/cli",
|
"name": "@immich/cli",
|
||||||
"version": "2.2.69",
|
"version": "2.2.72",
|
||||||
"description": "Command Line Interface (CLI) for Immich",
|
"description": "Command Line Interface (CLI) for Immich",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"exports": "./dist/index.js",
|
"exports": "./dist/index.js",
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"@types/micromatch": "^4.0.9",
|
"@types/micromatch": "^4.0.9",
|
||||||
"@types/mock-fs": "^4.13.1",
|
"@types/mock-fs": "^4.13.1",
|
||||||
"@types/node": "^22.15.31",
|
"@types/node": "^22.15.32",
|
||||||
"@vitest/coverage-v8": "^3.0.0",
|
"@vitest/coverage-v8": "^3.0.0",
|
||||||
"byte-size": "^9.0.0",
|
"byte-size": "^9.0.0",
|
||||||
"cli-progress": "^3.12.0",
|
"cli-progress": "^3.12.0",
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ name: immich-dev
|
|||||||
services:
|
services:
|
||||||
immich-server:
|
immich-server:
|
||||||
container_name: immich_server
|
container_name: immich_server
|
||||||
command: [ '/usr/src/app/bin/immich-dev' ]
|
command: ['/usr/src/app/bin/immich-dev']
|
||||||
image: immich-server-dev:latest
|
image: immich-server-dev:latest
|
||||||
# extends:
|
# extends:
|
||||||
# file: hwaccel.transcoding.yml
|
# file: hwaccel.transcoding.yml
|
||||||
@@ -70,7 +70,7 @@ services:
|
|||||||
# user: 0:0
|
# user: 0:0
|
||||||
build:
|
build:
|
||||||
context: ../web
|
context: ../web
|
||||||
command: [ '/usr/src/app/bin/immich-web' ]
|
command: ['/usr/src/app/bin/immich-web']
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
ports:
|
ports:
|
||||||
@@ -122,7 +122,7 @@ services:
|
|||||||
|
|
||||||
database:
|
database:
|
||||||
container_name: immich_postgres
|
container_name: immich_postgres
|
||||||
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.1-pgvectors0.2.0
|
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:5f6a838e4e44c8e0e019d0ebfe3ee8952b69afc2809b2c25f7b0119641978e91
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
environment:
|
environment:
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ services:
|
|||||||
|
|
||||||
database:
|
database:
|
||||||
container_name: immich_postgres
|
container_name: immich_postgres
|
||||||
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.1-pgvectors0.2.0
|
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:5f6a838e4e44c8e0e019d0ebfe3ee8952b69afc2809b2c25f7b0119641978e91
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
environment:
|
environment:
|
||||||
@@ -91,10 +91,10 @@ services:
|
|||||||
# add data source for http://immich-prometheus:9090 to get started
|
# add data source for http://immich-prometheus:9090 to get started
|
||||||
immich-grafana:
|
immich-grafana:
|
||||||
container_name: immich_grafana
|
container_name: immich_grafana
|
||||||
command: [ './run.sh', '-disable-reporting' ]
|
command: ['./run.sh', '-disable-reporting']
|
||||||
ports:
|
ports:
|
||||||
- 3000:3000
|
- 3000:3000
|
||||||
image: grafana/grafana:12.0.1-ubuntu@sha256:65575bb9c761335e2ff30e364f21d38632e3b2e75f5f81d83cc92f44b9bbc055
|
image: grafana/grafana:12.0.2-ubuntu@sha256:0512d81cdeaaff0e370a9aa66027b465d1f1f04379c3a9c801a905fabbdbc7a5
|
||||||
volumes:
|
volumes:
|
||||||
- grafana-data:/var/lib/grafana
|
- grafana-data:/var/lib/grafana
|
||||||
|
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ services:
|
|||||||
|
|
||||||
database:
|
database:
|
||||||
container_name: immich_postgres
|
container_name: immich_postgres
|
||||||
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.1-pgvectors0.2.0
|
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:5f6a838e4e44c8e0e019d0ebfe3ee8952b69afc2809b2c25f7b0119641978e91
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
||||||
POSTGRES_USER: ${DB_USERNAME}
|
POSTGRES_USER: ${DB_USERNAME}
|
||||||
|
|||||||
@@ -64,7 +64,13 @@ COMMIT;
|
|||||||
|
|
||||||
### Updating VectorChord
|
### Updating VectorChord
|
||||||
|
|
||||||
When installing a new version of VectorChord, you will need to manually update the extension by connecting to the Immich database and running `ALTER EXTENSION vchord UPDATE;`.
|
When installing a new version of VectorChord, you will need to manually update the extension and reindex by connecting to the Immich database and running:
|
||||||
|
|
||||||
|
```
|
||||||
|
ALTER EXTENSION vchord UPDATE;
|
||||||
|
REINDEX INDEX face_index;
|
||||||
|
REINDEX INDEX clip_index;
|
||||||
|
```
|
||||||
|
|
||||||
## Migrating to VectorChord
|
## Migrating to VectorChord
|
||||||
|
|
||||||
@@ -76,6 +82,8 @@ Support for pgvecto.rs will be dropped in a later release, hence we recommend al
|
|||||||
|
|
||||||
The easiest option is to have both extensions installed during the migration:
|
The easiest option is to have both extensions installed during the migration:
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Migration steps (automatic)</summary>
|
||||||
1. Ensure you still have pgvecto.rs installed
|
1. Ensure you still have pgvecto.rs installed
|
||||||
2. Install `pgvector` (`>= 0.7.0, < 1.0.0`). The easiest way to do this is on Debian/Ubuntu by adding the [PostgreSQL Apt repository][pg-apt] and then running `apt install postgresql-NN-pgvector`, where `NN` is your Postgres version (e.g., `16`)
|
2. Install `pgvector` (`>= 0.7.0, < 1.0.0`). The easiest way to do this is on Debian/Ubuntu by adding the [PostgreSQL Apt repository][pg-apt] and then running `apt install postgresql-NN-pgvector`, where `NN` is your Postgres version (e.g., `16`)
|
||||||
3. [Install VectorChord][vchord-install]
|
3. [Install VectorChord][vchord-install]
|
||||||
@@ -89,8 +97,12 @@ The easiest option is to have both extensions installed during the migration:
|
|||||||
11. Restart the Postgres database
|
11. Restart the Postgres database
|
||||||
12. Uninstall pgvecto.rs (e.g. `apt-get purge vectors-pg14` on Debian-based environments, replacing `pg14` as appropriate). `pgvector` must remain installed as it provides the data types used by `vchord`
|
12. Uninstall pgvecto.rs (e.g. `apt-get purge vectors-pg14` on Debian-based environments, replacing `pg14` as appropriate). `pgvector` must remain installed as it provides the data types used by `vchord`
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
If it is not possible to have both VectorChord and pgvecto.rs installed at the same time, you can perform the migration with more manual steps:
|
If it is not possible to have both VectorChord and pgvecto.rs installed at the same time, you can perform the migration with more manual steps:
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Migration steps (manual)</summary>
|
||||||
1. While pgvecto.rs is still installed, run the following SQL command using psql or your choice of database client. Take note of the number outputted by this command as you will need it later
|
1. While pgvecto.rs is still installed, run the following SQL command using psql or your choice of database client. Take note of the number outputted by this command as you will need it later
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
@@ -123,14 +135,20 @@ ALTER TABLE face_search ALTER COLUMN embedding SET DATA TYPE vector(512);
|
|||||||
|
|
||||||
5. Start Immich and let it create new indices using VectorChord
|
5. Start Immich and let it create new indices using VectorChord
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
### Migrating from pgvector
|
### Migrating from pgvector
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Migration steps</summary>
|
||||||
1. Ensure you have at least 0.7.0 of pgvector installed. If it is below that, please upgrade it and run the SQL command `ALTER EXTENSION vector UPDATE;` using psql or your choice of database client
|
1. Ensure you have at least 0.7.0 of pgvector installed. If it is below that, please upgrade it and run the SQL command `ALTER EXTENSION vector UPDATE;` using psql or your choice of database client
|
||||||
2. Follow the Prerequisites to install VectorChord
|
2. Follow the Prerequisites to install VectorChord
|
||||||
3. If Immich does not have superuser permissions, run the SQL command `CREATE EXTENSION vchord CASCADE;`
|
3. If Immich does not have superuser permissions, run the SQL command `CREATE EXTENSION vchord CASCADE;`
|
||||||
4. Remove the `DB_VECTOR_EXTENSION=pgvector` environmental variable as it will make Immich still use pgvector if set
|
4. Remove the `DB_VECTOR_EXTENSION=pgvector` environmental variable as it will make Immich still use pgvector if set
|
||||||
5. Start Immich and let it create new indices using VectorChord
|
5. Start Immich and let it create new indices using VectorChord
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
Note that VectorChord itself uses pgvector types, so you should not uninstall pgvector after following these steps.
|
Note that VectorChord itself uses pgvector types, so you should not uninstall pgvector after following these steps.
|
||||||
|
|
||||||
[vchord-install]: https://docs.vectorchord.ai/vectorchord/getting-started/installation.html
|
[vchord-install]: https://docs.vectorchord.ai/vectorchord/getting-started/installation.html
|
||||||
|
|||||||
@@ -39,8 +39,8 @@ alt="Dot Env Example"
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
- Change the default `DB_PASSWORD`, and add custom database connection information if necessary.
|
- Change the default `DB_PASSWORD`, and add custom database connection information if necessary.
|
||||||
- Change `DB_DATA_LOCATION` to a folder where the database will be saved to disk.
|
- Change `DB_DATA_LOCATION` to a folder (absolute path) where the database will be saved to disk.
|
||||||
- Change `UPLOAD_LOCATION` to a folder where media (uploaded and generated) will be stored.
|
- Change `UPLOAD_LOCATION` to a folder (absolute path) where media (uploaded and generated) will be stored.
|
||||||
|
|
||||||
11. Click on "**Deploy the stack**".
|
11. Click on "**Deploy the stack**".
|
||||||
|
|
||||||
|
|||||||
12
docs/static/archived-versions.json
vendored
12
docs/static/archived-versions.json
vendored
@@ -1,4 +1,16 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"label": "v1.135.3",
|
||||||
|
"url": "https://v1.135.3.archive.immich.app"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.135.2",
|
||||||
|
"url": "https://v1.135.2.archive.immich.app"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.135.1",
|
||||||
|
"url": "https://v1.135.1.archive.immich.app"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"label": "v1.135.0",
|
"label": "v1.135.0",
|
||||||
"url": "https://v1.135.0.archive.immich.app"
|
"url": "https://v1.135.0.archive.immich.app"
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ services:
|
|||||||
image: redis:6.2-alpine@sha256:3211c33a618c457e5d241922c975dbc4f446d0bdb2dc75694f5573ef8e2d01fa
|
image: redis:6.2-alpine@sha256:3211c33a618c457e5d241922c975dbc4f446d0bdb2dc75694f5573ef8e2d01fa
|
||||||
|
|
||||||
database:
|
database:
|
||||||
image: ghcr.io/immich-app/postgres:14-vectorchord0.3.0@sha256:9c704fb49ce27549df00f1b096cc93f8b0c959ef087507704d74954808f78a82
|
image: ghcr.io/immich-app/postgres:14-vectorchord0.3.0@sha256:3aef84a0a4fabbda17ef115c3019ba0c914ec73e9f6e59203674322d858b8eea
|
||||||
command: -c fsync=off -c shared_preload_libraries=vchord.so -c config_file=/var/lib/postgresql/data/postgresql.conf
|
command: -c fsync=off -c shared_preload_libraries=vchord.so -c config_file=/var/lib/postgresql/data/postgresql.conf
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_PASSWORD: postgres
|
POSTGRES_PASSWORD: postgres
|
||||||
|
|||||||
14
e2e/package-lock.json
generated
14
e2e/package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "immich-e2e",
|
"name": "immich-e2e",
|
||||||
"version": "1.135.0",
|
"version": "1.135.3",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "immich-e2e",
|
"name": "immich-e2e",
|
||||||
"version": "1.135.0",
|
"version": "1.135.3",
|
||||||
"license": "GNU Affero General Public License version 3",
|
"license": "GNU Affero General Public License version 3",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/eslintrc": "^3.1.0",
|
"@eslint/eslintrc": "^3.1.0",
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
"@immich/sdk": "file:../open-api/typescript-sdk",
|
"@immich/sdk": "file:../open-api/typescript-sdk",
|
||||||
"@playwright/test": "^1.44.1",
|
"@playwright/test": "^1.44.1",
|
||||||
"@types/luxon": "^3.4.2",
|
"@types/luxon": "^3.4.2",
|
||||||
"@types/node": "^22.15.31",
|
"@types/node": "^22.15.32",
|
||||||
"@types/oidc-provider": "^9.0.0",
|
"@types/oidc-provider": "^9.0.0",
|
||||||
"@types/pg": "^8.15.1",
|
"@types/pg": "^8.15.1",
|
||||||
"@types/pngjs": "^6.0.4",
|
"@types/pngjs": "^6.0.4",
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
},
|
},
|
||||||
"../cli": {
|
"../cli": {
|
||||||
"name": "@immich/cli",
|
"name": "@immich/cli",
|
||||||
"version": "2.2.69",
|
"version": "2.2.72",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "GNU Affero General Public License version 3",
|
"license": "GNU Affero General Public License version 3",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -66,7 +66,7 @@
|
|||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"@types/micromatch": "^4.0.9",
|
"@types/micromatch": "^4.0.9",
|
||||||
"@types/mock-fs": "^4.13.1",
|
"@types/mock-fs": "^4.13.1",
|
||||||
"@types/node": "^22.15.31",
|
"@types/node": "^22.15.32",
|
||||||
"@vitest/coverage-v8": "^3.0.0",
|
"@vitest/coverage-v8": "^3.0.0",
|
||||||
"byte-size": "^9.0.0",
|
"byte-size": "^9.0.0",
|
||||||
"cli-progress": "^3.12.0",
|
"cli-progress": "^3.12.0",
|
||||||
@@ -93,14 +93,14 @@
|
|||||||
},
|
},
|
||||||
"../open-api/typescript-sdk": {
|
"../open-api/typescript-sdk": {
|
||||||
"name": "@immich/sdk",
|
"name": "@immich/sdk",
|
||||||
"version": "1.135.0",
|
"version": "1.135.3",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "GNU Affero General Public License version 3",
|
"license": "GNU Affero General Public License version 3",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@oazapfts/runtime": "^1.0.2"
|
"@oazapfts/runtime": "^1.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^22.15.31",
|
"@types/node": "^22.15.32",
|
||||||
"typescript": "^5.3.3"
|
"typescript": "^5.3.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "immich-e2e",
|
"name": "immich-e2e",
|
||||||
"version": "1.135.0",
|
"version": "1.135.3",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
"@immich/sdk": "file:../open-api/typescript-sdk",
|
"@immich/sdk": "file:../open-api/typescript-sdk",
|
||||||
"@playwright/test": "^1.44.1",
|
"@playwright/test": "^1.44.1",
|
||||||
"@types/luxon": "^3.4.2",
|
"@types/luxon": "^3.4.2",
|
||||||
"@types/node": "^22.15.31",
|
"@types/node": "^22.15.32",
|
||||||
"@types/oidc-provider": "^9.0.0",
|
"@types/oidc-provider": "^9.0.0",
|
||||||
"@types/pg": "^8.15.1",
|
"@types/pg": "^8.15.1",
|
||||||
"@types/pngjs": "^6.0.4",
|
"@types/pngjs": "^6.0.4",
|
||||||
|
|||||||
@@ -14,7 +14,11 @@ describe('/people', () => {
|
|||||||
let nameAlicePerson: PersonResponseDto;
|
let nameAlicePerson: PersonResponseDto;
|
||||||
let nameBobPerson: PersonResponseDto;
|
let nameBobPerson: PersonResponseDto;
|
||||||
let nameCharliePerson: PersonResponseDto;
|
let nameCharliePerson: PersonResponseDto;
|
||||||
let nameNullPerson: PersonResponseDto;
|
let nameNullPerson4Assets: PersonResponseDto;
|
||||||
|
let nameNullPerson3Assets: PersonResponseDto;
|
||||||
|
let nameNullPerson1Asset: PersonResponseDto;
|
||||||
|
let nameBillPersonFavourite: PersonResponseDto;
|
||||||
|
let nameFreddyPersonFavourite: PersonResponseDto;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await utils.resetDatabase();
|
await utils.resetDatabase();
|
||||||
@@ -27,7 +31,11 @@ describe('/people', () => {
|
|||||||
nameCharliePerson,
|
nameCharliePerson,
|
||||||
nameBobPerson,
|
nameBobPerson,
|
||||||
nameAlicePerson,
|
nameAlicePerson,
|
||||||
nameNullPerson,
|
nameNullPerson4Assets,
|
||||||
|
nameNullPerson3Assets,
|
||||||
|
nameNullPerson1Asset,
|
||||||
|
nameBillPersonFavourite,
|
||||||
|
nameFreddyPersonFavourite,
|
||||||
] = await Promise.all([
|
] = await Promise.all([
|
||||||
utils.createPerson(admin.accessToken, {
|
utils.createPerson(admin.accessToken, {
|
||||||
name: 'visible_person',
|
name: 'visible_person',
|
||||||
@@ -52,11 +60,26 @@ describe('/people', () => {
|
|||||||
utils.createPerson(admin.accessToken, {
|
utils.createPerson(admin.accessToken, {
|
||||||
name: '',
|
name: '',
|
||||||
}),
|
}),
|
||||||
|
utils.createPerson(admin.accessToken, {
|
||||||
|
name: '',
|
||||||
|
}),
|
||||||
|
utils.createPerson(admin.accessToken, {
|
||||||
|
name: '',
|
||||||
|
}),
|
||||||
|
utils.createPerson(admin.accessToken, {
|
||||||
|
name: 'Bill',
|
||||||
|
isFavorite: true,
|
||||||
|
}),
|
||||||
|
utils.createPerson(admin.accessToken, {
|
||||||
|
name: 'Freddy',
|
||||||
|
isFavorite: true,
|
||||||
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const asset1 = await utils.createAsset(admin.accessToken);
|
const asset1 = await utils.createAsset(admin.accessToken);
|
||||||
const asset2 = await utils.createAsset(admin.accessToken);
|
const asset2 = await utils.createAsset(admin.accessToken);
|
||||||
const asset3 = await utils.createAsset(admin.accessToken);
|
const asset3 = await utils.createAsset(admin.accessToken);
|
||||||
|
const asset4 = await utils.createAsset(admin.accessToken);
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
utils.createFace({ assetId: asset1.id, personId: visiblePerson.id }),
|
utils.createFace({ assetId: asset1.id, personId: visiblePerson.id }),
|
||||||
@@ -64,15 +87,27 @@ describe('/people', () => {
|
|||||||
utils.createFace({ assetId: asset1.id, personId: multipleAssetsPerson.id }),
|
utils.createFace({ assetId: asset1.id, personId: multipleAssetsPerson.id }),
|
||||||
utils.createFace({ assetId: asset1.id, personId: multipleAssetsPerson.id }),
|
utils.createFace({ assetId: asset1.id, personId: multipleAssetsPerson.id }),
|
||||||
utils.createFace({ assetId: asset2.id, personId: multipleAssetsPerson.id }),
|
utils.createFace({ assetId: asset2.id, personId: multipleAssetsPerson.id }),
|
||||||
utils.createFace({ assetId: asset3.id, personId: multipleAssetsPerson.id }),
|
utils.createFace({ assetId: asset3.id, personId: multipleAssetsPerson.id }), // 4 assets
|
||||||
// Named persons
|
// Named persons
|
||||||
utils.createFace({ assetId: asset1.id, personId: nameCharliePerson.id }), // 1 asset
|
utils.createFace({ assetId: asset1.id, personId: nameCharliePerson.id }), // 1 asset
|
||||||
utils.createFace({ assetId: asset1.id, personId: nameBobPerson.id }),
|
utils.createFace({ assetId: asset1.id, personId: nameBobPerson.id }),
|
||||||
utils.createFace({ assetId: asset2.id, personId: nameBobPerson.id }), // 2 assets
|
utils.createFace({ assetId: asset2.id, personId: nameBobPerson.id }), // 2 assets
|
||||||
utils.createFace({ assetId: asset1.id, personId: nameAlicePerson.id }), // 1 asset
|
utils.createFace({ assetId: asset1.id, personId: nameAlicePerson.id }), // 1 asset
|
||||||
// Null-named person
|
// Null-named person 4 assets
|
||||||
utils.createFace({ assetId: asset1.id, personId: nameNullPerson.id }),
|
utils.createFace({ assetId: asset1.id, personId: nameNullPerson4Assets.id }),
|
||||||
utils.createFace({ assetId: asset2.id, personId: nameNullPerson.id }), // 2 assets
|
utils.createFace({ assetId: asset2.id, personId: nameNullPerson4Assets.id }),
|
||||||
|
utils.createFace({ assetId: asset3.id, personId: nameNullPerson4Assets.id }),
|
||||||
|
utils.createFace({ assetId: asset4.id, personId: nameNullPerson4Assets.id }), // 4 assets
|
||||||
|
// Null-named person 3 assets
|
||||||
|
utils.createFace({ assetId: asset1.id, personId: nameNullPerson3Assets.id }),
|
||||||
|
utils.createFace({ assetId: asset2.id, personId: nameNullPerson3Assets.id }),
|
||||||
|
utils.createFace({ assetId: asset3.id, personId: nameNullPerson3Assets.id }), // 3 assets
|
||||||
|
// Null-named person 1 asset
|
||||||
|
utils.createFace({ assetId: asset3.id, personId: nameNullPerson1Asset.id }),
|
||||||
|
// Favourite People
|
||||||
|
utils.createFace({ assetId: asset1.id, personId: nameFreddyPersonFavourite.id }),
|
||||||
|
utils.createFace({ assetId: asset2.id, personId: nameFreddyPersonFavourite.id }),
|
||||||
|
utils.createFace({ assetId: asset1.id, personId: nameBillPersonFavourite.id }),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -87,15 +122,19 @@ describe('/people', () => {
|
|||||||
expect(status).toBe(200);
|
expect(status).toBe(200);
|
||||||
expect(body).toEqual({
|
expect(body).toEqual({
|
||||||
hasNextPage: false,
|
hasNextPage: false,
|
||||||
total: 7,
|
total: 11,
|
||||||
hidden: 1,
|
hidden: 1,
|
||||||
people: [
|
people: [
|
||||||
|
expect.objectContaining({ name: 'Freddy' }),
|
||||||
|
expect.objectContaining({ name: 'Bill' }),
|
||||||
expect.objectContaining({ name: 'multiple_assets_person' }),
|
expect.objectContaining({ name: 'multiple_assets_person' }),
|
||||||
expect.objectContaining({ name: 'Bob' }),
|
expect.objectContaining({ name: 'Bob' }),
|
||||||
expect.objectContaining({ name: 'Alice' }),
|
expect.objectContaining({ name: 'Alice' }),
|
||||||
expect.objectContaining({ name: 'Charlie' }),
|
expect.objectContaining({ name: 'Charlie' }),
|
||||||
expect.objectContaining({ name: 'visible_person' }),
|
expect.objectContaining({ name: 'visible_person' }),
|
||||||
expect.objectContaining({ name: 'hidden_person' }),
|
expect.objectContaining({ id: nameNullPerson4Assets.id, name: '' }),
|
||||||
|
expect.objectContaining({ id: nameNullPerson3Assets.id, name: '' }),
|
||||||
|
expect.objectContaining({ name: 'hidden_person' }), // Should really be before the null names
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -105,17 +144,21 @@ describe('/people', () => {
|
|||||||
|
|
||||||
expect(status).toBe(200);
|
expect(status).toBe(200);
|
||||||
expect(body.hasNextPage).toBe(false);
|
expect(body.hasNextPage).toBe(false);
|
||||||
expect(body.total).toBe(7); // All persons
|
expect(body.total).toBe(11); // All persons
|
||||||
expect(body.hidden).toBe(1); // 'hidden_person'
|
expect(body.hidden).toBe(1); // 'hidden_person'
|
||||||
|
|
||||||
const people = body.people as PersonResponseDto[];
|
const people = body.people as PersonResponseDto[];
|
||||||
|
|
||||||
expect(people.map((p) => p.id)).toEqual([
|
expect(people.map((p) => p.id)).toEqual([
|
||||||
|
nameFreddyPersonFavourite.id, // name: 'Freddy', count: 2
|
||||||
|
nameBillPersonFavourite.id, // name: 'Bill', count: 1
|
||||||
multipleAssetsPerson.id, // name: 'multiple_assets_person', count: 3
|
multipleAssetsPerson.id, // name: 'multiple_assets_person', count: 3
|
||||||
nameBobPerson.id, // name: 'Bob', count: 2
|
nameBobPerson.id, // name: 'Bob', count: 2
|
||||||
nameAlicePerson.id, // name: 'Alice', count: 1
|
nameAlicePerson.id, // name: 'Alice', count: 1
|
||||||
nameCharliePerson.id, // name: 'Charlie', count: 1
|
nameCharliePerson.id, // name: 'Charlie', count: 1
|
||||||
visiblePerson.id, // name: 'visible_person', count: 1
|
visiblePerson.id, // name: 'visible_person', count: 1
|
||||||
|
nameNullPerson4Assets.id, // name: '', count: 4
|
||||||
|
nameNullPerson3Assets.id, // name: '', count: 3
|
||||||
]);
|
]);
|
||||||
|
|
||||||
expect(people.some((p) => p.id === hiddenPerson.id)).toBe(false);
|
expect(people.some((p) => p.id === hiddenPerson.id)).toBe(false);
|
||||||
@@ -127,14 +170,18 @@ describe('/people', () => {
|
|||||||
expect(status).toBe(200);
|
expect(status).toBe(200);
|
||||||
expect(body).toEqual({
|
expect(body).toEqual({
|
||||||
hasNextPage: false,
|
hasNextPage: false,
|
||||||
total: 7,
|
total: 11,
|
||||||
hidden: 1,
|
hidden: 1,
|
||||||
people: [
|
people: [
|
||||||
|
expect.objectContaining({ name: 'Freddy' }),
|
||||||
|
expect.objectContaining({ name: 'Bill' }),
|
||||||
expect.objectContaining({ name: 'multiple_assets_person' }),
|
expect.objectContaining({ name: 'multiple_assets_person' }),
|
||||||
expect.objectContaining({ name: 'Bob' }),
|
expect.objectContaining({ name: 'Bob' }),
|
||||||
expect.objectContaining({ name: 'Alice' }),
|
expect.objectContaining({ name: 'Alice' }),
|
||||||
expect.objectContaining({ name: 'Charlie' }),
|
expect.objectContaining({ name: 'Charlie' }),
|
||||||
expect.objectContaining({ name: 'visible_person' }),
|
expect.objectContaining({ name: 'visible_person' }),
|
||||||
|
expect.objectContaining({ id: nameNullPerson4Assets.id, name: '' }),
|
||||||
|
expect.objectContaining({ id: nameNullPerson3Assets.id, name: '' }),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -148,9 +195,9 @@ describe('/people', () => {
|
|||||||
expect(status).toBe(200);
|
expect(status).toBe(200);
|
||||||
expect(body).toEqual({
|
expect(body).toEqual({
|
||||||
hasNextPage: true,
|
hasNextPage: true,
|
||||||
total: 7,
|
total: 11,
|
||||||
hidden: 1,
|
hidden: 1,
|
||||||
people: [expect.objectContaining({ name: 'visible_person' })],
|
people: [expect.objectContaining({ name: 'Alice' })],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -119,6 +119,16 @@ describe('/shared-links', () => {
|
|||||||
expect(resp.header['content-type']).toContain('text/html');
|
expect(resp.header['content-type']).toContain('text/html');
|
||||||
expect(resp.text).toContain(`<meta property="og:image" content="https://my.immich.app`);
|
expect(resp.text).toContain(`<meta property="og:image" content="https://my.immich.app`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should return 404 for an invalid shared link', async () => {
|
||||||
|
const resp = await request(shareUrl).get(`/invalid-key`);
|
||||||
|
expect(resp.status).toBe(404);
|
||||||
|
expect(resp.header['content-type']).toContain('text/html');
|
||||||
|
expect(resp.text).not.toContain(`og:type`);
|
||||||
|
expect(resp.text).not.toContain(`og:title`);
|
||||||
|
expect(resp.text).not.toContain(`og:description`);
|
||||||
|
expect(resp.text).not.toContain(`og:image`);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /shared-links', () => {
|
describe('GET /shared-links', () => {
|
||||||
|
|||||||
@@ -464,7 +464,6 @@
|
|||||||
"assets": "Assets",
|
"assets": "Assets",
|
||||||
"assets_added_count": "Added {count, plural, one {# asset} other {# assets}}",
|
"assets_added_count": "Added {count, plural, one {# asset} other {# assets}}",
|
||||||
"assets_added_to_album_count": "Added {count, plural, one {# asset} other {# assets}} to the album",
|
"assets_added_to_album_count": "Added {count, plural, one {# asset} other {# assets}} to the album",
|
||||||
"assets_added_to_name_count": "Added {count, plural, one {# asset} other {# assets}} to {hasName, select, true {<b>{name}</b>} other {new album}}",
|
|
||||||
"assets_cannot_be_added_to_album_count": "{count, plural, one {Asset} other {Assets}} cannot be added to the album",
|
"assets_cannot_be_added_to_album_count": "{count, plural, one {Asset} other {Assets}} cannot be added to the album",
|
||||||
"assets_count": "{count, plural, one {# asset} other {# assets}}",
|
"assets_count": "{count, plural, one {# asset} other {# assets}}",
|
||||||
"assets_deleted_permanently": "{count} asset(s) deleted permanently",
|
"assets_deleted_permanently": "{count} asset(s) deleted permanently",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
ARG DEVICE=cpu
|
ARG DEVICE=cpu
|
||||||
|
|
||||||
FROM python:3.11-bookworm@sha256:d2621a9f74d31a8a60af19f97b09cc3ac54382c8680b6544018713a12ef6c048 AS builder-cpu
|
FROM python:3.11-bookworm@sha256:ce3b954c9285a7a145cba620bae03db836ab890b6b9e0d05a3ca522ea00dfbc9 AS builder-cpu
|
||||||
|
|
||||||
FROM builder-cpu AS builder-openvino
|
FROM builder-cpu AS builder-openvino
|
||||||
|
|
||||||
@@ -59,7 +59,7 @@ ENV PYTHONDONTWRITEBYTECODE=1 \
|
|||||||
|
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends g++
|
RUN apt-get update && apt-get install -y --no-install-recommends g++
|
||||||
|
|
||||||
COPY --from=ghcr.io/astral-sh/uv:latest@sha256:4faec156e35a5f345d57804d8858c6ba1cf6352ce5f4bffc11b7fdebdef46a38 /uv /uvx /bin/
|
COPY --from=ghcr.io/astral-sh/uv:latest@sha256:cda0fdc9b6066975ba4c791597870d18bc3a441dfc18ab24c5e888c16e15780c /uv /uvx /bin/
|
||||||
RUN --mount=type=cache,target=/root/.cache/uv \
|
RUN --mount=type=cache,target=/root/.cache/uv \
|
||||||
--mount=type=bind,source=uv.lock,target=uv.lock \
|
--mount=type=bind,source=uv.lock,target=uv.lock \
|
||||||
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
|
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
|
||||||
@@ -68,11 +68,11 @@ RUN if [ "$DEVICE" = "rocm" ]; then \
|
|||||||
uv pip install /opt/onnxruntime_rocm-*.whl; \
|
uv pip install /opt/onnxruntime_rocm-*.whl; \
|
||||||
fi
|
fi
|
||||||
|
|
||||||
FROM python:3.11-slim-bookworm@sha256:7a3ed1226224bcc1fe5443262363d42f48cf832a540c1836ba8ccbeaadf8637c AS prod-cpu
|
FROM python:3.11-slim-bookworm@sha256:9e1912aab0a30bbd9488eb79063f68f42a68ab0946cbe98fecf197fe5b085506 AS prod-cpu
|
||||||
|
|
||||||
ENV LD_PRELOAD=/usr/lib/libmimalloc.so.2
|
ENV LD_PRELOAD=/usr/lib/libmimalloc.so.2
|
||||||
|
|
||||||
FROM python:3.11-slim-bookworm@sha256:7a3ed1226224bcc1fe5443262363d42f48cf832a540c1836ba8ccbeaadf8637c AS prod-openvino
|
FROM python:3.11-slim-bookworm@sha256:9e1912aab0a30bbd9488eb79063f68f42a68ab0946cbe98fecf197fe5b085506 AS prod-openvino
|
||||||
|
|
||||||
RUN apt-get update && \
|
RUN apt-get update && \
|
||||||
apt-get install --no-install-recommends -yqq ocl-icd-libopencl1 wget && \
|
apt-get install --no-install-recommends -yqq ocl-icd-libopencl1 wget && \
|
||||||
|
|||||||
@@ -244,7 +244,6 @@ async def load(model: InferenceModel) -> InferenceModel:
|
|||||||
|
|
||||||
async def idle_shutdown_task() -> None:
|
async def idle_shutdown_task() -> None:
|
||||||
while True:
|
while True:
|
||||||
log.debug("Checking for inactivity...")
|
|
||||||
if (
|
if (
|
||||||
last_called is not None
|
last_called is not None
|
||||||
and not active_requests
|
and not active_requests
|
||||||
|
|||||||
141
machine-learning/uv.lock
generated
141
machine-learning/uv.lock
generated
@@ -517,16 +517,16 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fastapi"
|
name = "fastapi"
|
||||||
version = "0.115.12"
|
version = "0.115.13"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "pydantic" },
|
{ name = "pydantic" },
|
||||||
{ name = "starlette" },
|
{ name = "starlette" },
|
||||||
{ name = "typing-extensions" },
|
{ name = "typing-extensions" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/f4/55/ae499352d82338331ca1e28c7f4a63bfd09479b16395dce38cf50a39e2c2/fastapi-0.115.12.tar.gz", hash = "sha256:1e2c2a2646905f9e83d32f04a3f86aff4a286669c6c950ca95b5fd68c2602681", size = 295236, upload-time = "2025-03-23T22:55:43.822Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/20/64/ec0788201b5554e2a87c49af26b77a4d132f807a0fa9675257ac92c6aa0e/fastapi-0.115.13.tar.gz", hash = "sha256:55d1d25c2e1e0a0a50aceb1c8705cd932def273c102bff0b1c1da88b3c6eb307", size = 295680, upload-time = "2025-06-17T11:49:45.575Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/50/b3/b51f09c2ba432a576fe63758bddc81f78f0c6309d9e5c10d194313bf021e/fastapi-0.115.12-py3-none-any.whl", hash = "sha256:e94613d6c05e27be7ffebdd6ea5f388112e5e430c8f7d6494a9d1d88d43e814d", size = 95164, upload-time = "2025-03-23T22:55:42.101Z" },
|
{ url = "https://files.pythonhosted.org/packages/59/4a/e17764385382062b0edbb35a26b7cf76d71e27e456546277a42ba6545c6e/fastapi-0.115.13-py3-none-any.whl", hash = "sha256:0a0cab59afa7bab22f5eb347f8c9864b681558c278395e94035a741fc10cd865", size = 95315, upload-time = "2025-06-17T11:49:44.106Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -900,7 +900,7 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "huggingface-hub"
|
name = "huggingface-hub"
|
||||||
version = "0.32.4"
|
version = "0.33.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "filelock" },
|
{ name = "filelock" },
|
||||||
@@ -912,9 +912,9 @@ dependencies = [
|
|||||||
{ name = "tqdm" },
|
{ name = "tqdm" },
|
||||||
{ name = "typing-extensions" },
|
{ name = "typing-extensions" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/60/c8/4f7d270285c46324fd66f62159eb16739aa5696f422dba57678a8c6b78e9/huggingface_hub-0.32.4.tar.gz", hash = "sha256:f61d45cd338736f59fb0e97550b74c24ee771bcc92c05ae0766b9116abe720be", size = 424494, upload-time = "2025-06-03T09:59:46.105Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/91/8a/1362d565fefabaa4185cf3ae842a98dbc5b35146f5694f7080f043a6952f/huggingface_hub-0.33.0.tar.gz", hash = "sha256:aa31f70d29439d00ff7a33837c03f1f9dd83971ce4e29ad664d63ffb17d3bb97", size = 426179, upload-time = "2025-06-11T17:08:07.913Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/67/8b/222140f3cfb6f17b0dd8c4b9a0b36bd4ebefe9fb0098ba35d6960abcda0f/huggingface_hub-0.32.4-py3-none-any.whl", hash = "sha256:37abf8826b38d971f60d3625229221c36e53fe58060286db9baf619cfbf39767", size = 512101, upload-time = "2025-06-03T09:59:44.099Z" },
|
{ url = "https://files.pythonhosted.org/packages/33/fb/53587a89fbc00799e4179796f51b3ad713c5de6bb680b2becb6d37c94649/huggingface_hub-0.33.0-py3-none-any.whl", hash = "sha256:e8668875b40c68f9929150d99727d39e5ebb8a05a98e4191b908dc7ded9074b3", size = 514799, upload-time = "2025-06-11T17:08:05.757Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1225,7 +1225,7 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "locust"
|
name = "locust"
|
||||||
version = "2.37.9"
|
version = "2.37.11"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "configargparse" },
|
{ name = "configargparse" },
|
||||||
@@ -1245,14 +1245,14 @@ dependencies = [
|
|||||||
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
|
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
|
||||||
{ name = "werkzeug" },
|
{ name = "werkzeug" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/90/05/2bfdf19756c6a12f6f9513f75340ecf0595d83cab4d9fc91162225908e3d/locust-2.37.9.tar.gz", hash = "sha256:e43673b594ec5ecde4f9ba6e0d5c66c00d7c0ae93591951abe83e8d186c67175", size = 2252507, upload-time = "2025-06-05T09:26:58.186Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/4f/19/66cdab585f7d4385be615d3792402fc75a1bed7519e5283adbe7133dbc78/locust-2.37.11.tar.gz", hash = "sha256:89c79bc599aa57160bd41dd3876e35d8b9dee5abded78e35008d01fd8f1640ed", size = 2252602, upload-time = "2025-06-23T08:22:23.922Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/33/1c/0ece4176231c578e819d970ec08d124492833e50aafd171c582bcc414446/locust-2.37.9-py3-none-any.whl", hash = "sha256:e17da439f3a252d1fb6d4c34daf00d7e8b87e99d833a32e8a79f4f8ebb07767d", size = 2269084, upload-time = "2025-06-05T09:26:56.257Z" },
|
{ url = "https://files.pythonhosted.org/packages/3a/2d/e5ae05911521bf84113be349d51b16d54589e986837d2d518f63434ea3ec/locust-2.37.11-py3-none-any.whl", hash = "sha256:b826f95fbfd5d9a32df6ab1b74672b88e65bbc33ec99fdc10af98079952ad517", size = 2269179, upload-time = "2025-06-23T08:22:21.067Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "locust-cloud"
|
name = "locust-cloud"
|
||||||
version = "1.23.1"
|
version = "1.24.2"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "configargparse" },
|
{ name = "configargparse" },
|
||||||
@@ -1262,9 +1262,9 @@ dependencies = [
|
|||||||
{ name = "python-socketio", extra = ["client"] },
|
{ name = "python-socketio", extra = ["client"] },
|
||||||
{ name = "tomli", marker = "python_full_version < '3.11'" },
|
{ name = "tomli", marker = "python_full_version < '3.11'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/bd/7c/d9cbbd051490aeedfbd6ddda8ad48f77dd848ee490f6ebd166d20db5911e/locust_cloud-1.23.1.tar.gz", hash = "sha256:a09161752b8c9a9205e97cef5223ee3ad967bc2d91c52d61952aaa3da6802a55", size = 450937, upload-time = "2025-06-05T06:07:53.773Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/d9/77/bda24167a2b763ba5d3cad1f3fa2a938f5273e51a61bffdbc8dc2e3ba24d/locust_cloud-1.24.2.tar.gz", hash = "sha256:a2656537ff367e6d4d4673477ba9e81ed73a8423a71573cd2512248740eded77", size = 451122, upload-time = "2025-06-23T11:08:00.558Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/7e/01/5af43edee540e38ba0ee0a2e3beb72c50073e0f646bb543a8b34650315e3/locust_cloud-1.23.1-py3-none-any.whl", hash = "sha256:11677895c6ed6d0beef1b425a6f04f10ea2cfcaeaefbf00a97fb3c9134296e54", size = 408323, upload-time = "2025-06-05T06:07:51.947Z" },
|
{ url = "https://files.pythonhosted.org/packages/f4/38/8cda8aa1c6dfe5c5abbf69a9b10c03585c37eff64ca92733a291806052ac/locust_cloud-1.24.2-py3-none-any.whl", hash = "sha256:64a5e6f2bf0a1a012d9805291d44fb57e57535c2b5c0fa5bc87ba0d7cce9ef9c", size = 408594, upload-time = "2025-06-23T11:07:59.092Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1415,7 +1415,7 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mypy"
|
name = "mypy"
|
||||||
version = "1.16.0"
|
version = "1.16.1"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "mypy-extensions" },
|
{ name = "mypy-extensions" },
|
||||||
@@ -1423,33 +1423,33 @@ dependencies = [
|
|||||||
{ name = "tomli", marker = "python_full_version < '3.11'" },
|
{ name = "tomli", marker = "python_full_version < '3.11'" },
|
||||||
{ name = "typing-extensions" },
|
{ name = "typing-extensions" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/d4/38/13c2f1abae94d5ea0354e146b95a1be9b2137a0d506728e0da037c4276f6/mypy-1.16.0.tar.gz", hash = "sha256:84b94283f817e2aa6350a14b4a8fb2a35a53c286f97c9d30f53b63620e7af8ab", size = 3323139, upload-time = "2025-05-29T13:46:12.532Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/81/69/92c7fa98112e4d9eb075a239caa4ef4649ad7d441545ccffbd5e34607cbb/mypy-1.16.1.tar.gz", hash = "sha256:6bd00a0a2094841c5e47e7374bb42b83d64c527a502e3334e1173a0c24437bab", size = 3324747, upload-time = "2025-06-16T16:51:35.145Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/64/5e/a0485f0608a3d67029d3d73cec209278b025e3493a3acfda3ef3a88540fd/mypy-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7909541fef256527e5ee9c0a7e2aeed78b6cda72ba44298d1334fe7881b05c5c", size = 10967416, upload-time = "2025-05-29T13:34:17.783Z" },
|
{ url = "https://files.pythonhosted.org/packages/8e/12/2bf23a80fcef5edb75de9a1e295d778e0f46ea89eb8b115818b663eff42b/mypy-1.16.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b4f0fed1022a63c6fec38f28b7fc77fca47fd490445c69d0a66266c59dd0b88a", size = 10958644, upload-time = "2025-06-16T16:51:11.649Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/4b/53/5837c221f74c0d53a4bfc3003296f8179c3a2a7f336d7de7bbafbe96b688/mypy-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e71d6f0090c2256c713ed3d52711d01859c82608b5d68d4fa01a3fe30df95571", size = 10087654, upload-time = "2025-05-29T13:32:37.878Z" },
|
{ url = "https://files.pythonhosted.org/packages/08/50/bfe47b3b278eacf348291742fd5e6613bbc4b3434b72ce9361896417cfe5/mypy-1.16.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:86042bbf9f5a05ea000d3203cf87aa9d0ccf9a01f73f71c58979eb9249f46d72", size = 10087033, upload-time = "2025-06-16T16:35:30.089Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/29/59/5fd2400352c3093bed4c09017fe671d26bc5bb7e6ef2d4bf85f2a2488104/mypy-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:936ccfdd749af4766be824268bfe22d1db9eb2f34a3ea1d00ffbe5b5265f5491", size = 11875192, upload-time = "2025-05-29T13:34:54.281Z" },
|
{ url = "https://files.pythonhosted.org/packages/21/de/40307c12fe25675a0776aaa2cdd2879cf30d99eec91b898de00228dc3ab5/mypy-1.16.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ea7469ee5902c95542bea7ee545f7006508c65c8c54b06dc2c92676ce526f3ea", size = 11875645, upload-time = "2025-06-16T16:35:48.49Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/ad/3e/4bfec74663a64c2012f3e278dbc29ffe82b121bc551758590d1b6449ec0c/mypy-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4086883a73166631307fdd330c4a9080ce24913d4f4c5ec596c601b3a4bdd777", size = 12612939, upload-time = "2025-05-29T13:33:14.766Z" },
|
{ url = "https://files.pythonhosted.org/packages/a6/d8/85bdb59e4a98b7a31495bd8f1a4445d8ffc86cde4ab1f8c11d247c11aedc/mypy-1.16.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:352025753ef6a83cb9e7f2427319bb7875d1fdda8439d1e23de12ab164179574", size = 12616986, upload-time = "2025-06-16T16:48:39.526Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/88/1f/fecbe3dcba4bf2ca34c26ca016383a9676711907f8db4da8354925cbb08f/mypy-1.16.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:feec38097f71797da0231997e0de3a58108c51845399669ebc532c815f93866b", size = 12874719, upload-time = "2025-05-29T13:21:52.09Z" },
|
{ url = "https://files.pythonhosted.org/packages/0e/d0/bb25731158fa8f8ee9e068d3e94fcceb4971fedf1424248496292512afe9/mypy-1.16.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ff9fa5b16e4c1364eb89a4d16bcda9987f05d39604e1e6c35378a2987c1aac2d", size = 12878632, upload-time = "2025-06-16T16:36:08.195Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/f3/51/c2d280601cd816c43dfa512a759270d5a5ef638d7ac9bea9134c8305a12f/mypy-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:09a8da6a0ee9a9770b8ff61b39c0bb07971cda90e7297f4213741b48a0cc8d93", size = 9487053, upload-time = "2025-05-29T13:33:29.797Z" },
|
{ url = "https://files.pythonhosted.org/packages/2d/11/822a9beb7a2b825c0cb06132ca0a5183f8327a5e23ef89717c9474ba0bc6/mypy-1.16.1-cp310-cp310-win_amd64.whl", hash = "sha256:1256688e284632382f8f3b9e2123df7d279f603c561f099758e66dd6ed4e8bd6", size = 9484391, upload-time = "2025-06-16T16:37:56.151Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/24/c4/ff2f79db7075c274fe85b5fff8797d29c6b61b8854c39e3b7feb556aa377/mypy-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9f826aaa7ff8443bac6a494cf743f591488ea940dd360e7dd330e30dd772a5ab", size = 10884498, upload-time = "2025-05-29T13:18:54.066Z" },
|
{ url = "https://files.pythonhosted.org/packages/9a/61/ec1245aa1c325cb7a6c0f8570a2eee3bfc40fa90d19b1267f8e50b5c8645/mypy-1.16.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:472e4e4c100062488ec643f6162dd0d5208e33e2f34544e1fc931372e806c0cc", size = 10890557, upload-time = "2025-06-16T16:37:21.421Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/02/07/12198e83006235f10f6a7808917376b5d6240a2fd5dce740fe5d2ebf3247/mypy-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:82d056e6faa508501af333a6af192c700b33e15865bda49611e3d7d8358ebea2", size = 10011755, upload-time = "2025-05-29T13:34:00.851Z" },
|
{ url = "https://files.pythonhosted.org/packages/6b/bb/6eccc0ba0aa0c7a87df24e73f0ad34170514abd8162eb0c75fd7128171fb/mypy-1.16.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ea16e2a7d2714277e349e24d19a782a663a34ed60864006e8585db08f8ad1782", size = 10012921, upload-time = "2025-06-16T16:51:28.659Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/f1/9b/5fd5801a72b5d6fb6ec0105ea1d0e01ab2d4971893076e558d4b6d6b5f80/mypy-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:089bedc02307c2548eb51f426e085546db1fa7dd87fbb7c9fa561575cf6eb1ff", size = 11800138, upload-time = "2025-05-29T13:32:55.082Z" },
|
{ url = "https://files.pythonhosted.org/packages/5f/80/b337a12e2006715f99f529e732c5f6a8c143bb58c92bb142d5ab380963a5/mypy-1.16.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08e850ea22adc4d8a4014651575567b0318ede51e8e9fe7a68f25391af699507", size = 11802887, upload-time = "2025-06-16T16:50:53.627Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/2e/81/a117441ea5dfc3746431e51d78a4aca569c677aa225bca2cc05a7c239b61/mypy-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6a2322896003ba66bbd1318c10d3afdfe24e78ef12ea10e2acd985e9d684a666", size = 12533156, upload-time = "2025-05-29T13:19:12.963Z" },
|
{ url = "https://files.pythonhosted.org/packages/d9/59/f7af072d09793d581a745a25737c7c0a945760036b16aeb620f658a017af/mypy-1.16.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22d76a63a42619bfb90122889b903519149879ddbf2ba4251834727944c8baca", size = 12531658, upload-time = "2025-06-16T16:33:55.002Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/3f/38/88ec57c6c86014d3f06251e00f397b5a7daa6888884d0abf187e4f5f587f/mypy-1.16.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:021a68568082c5b36e977d54e8f1de978baf401a33884ffcea09bd8e88a98f4c", size = 12742426, upload-time = "2025-05-29T13:20:22.72Z" },
|
{ url = "https://files.pythonhosted.org/packages/82/c4/607672f2d6c0254b94a646cfc45ad589dd71b04aa1f3d642b840f7cce06c/mypy-1.16.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:2c7ce0662b6b9dc8f4ed86eb7a5d505ee3298c04b40ec13b30e572c0e5ae17c4", size = 12732486, upload-time = "2025-06-16T16:37:03.301Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/bd/53/7e9d528433d56e6f6f77ccf24af6ce570986c2d98a5839e4c2009ef47283/mypy-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:54066fed302d83bf5128632d05b4ec68412e1f03ef2c300434057d66866cea4b", size = 9478319, upload-time = "2025-05-29T13:21:17.582Z" },
|
{ url = "https://files.pythonhosted.org/packages/b6/5e/136555ec1d80df877a707cebf9081bd3a9f397dedc1ab9750518d87489ec/mypy-1.16.1-cp311-cp311-win_amd64.whl", hash = "sha256:211287e98e05352a2e1d4e8759c5490925a7c784ddc84207f4714822f8cf99b6", size = 9479482, upload-time = "2025-06-16T16:47:37.48Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/70/cf/158e5055e60ca2be23aec54a3010f89dcffd788732634b344fc9cb1e85a0/mypy-1.16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c5436d11e89a3ad16ce8afe752f0f373ae9620841c50883dc96f8b8805620b13", size = 11062927, upload-time = "2025-05-29T13:35:52.328Z" },
|
{ url = "https://files.pythonhosted.org/packages/b4/d6/39482e5fcc724c15bf6280ff5806548c7185e0c090712a3736ed4d07e8b7/mypy-1.16.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:af4792433f09575d9eeca5c63d7d90ca4aeceda9d8355e136f80f8967639183d", size = 11066493, upload-time = "2025-06-16T16:47:01.683Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/94/34/cfff7a56be1609f5d10ef386342ce3494158e4d506516890142007e6472c/mypy-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f2622af30bf01d8fc36466231bdd203d120d7a599a6d88fb22bdcb9dbff84090", size = 10083082, upload-time = "2025-05-29T13:35:33.378Z" },
|
{ url = "https://files.pythonhosted.org/packages/e6/e5/26c347890efc6b757f4d5bb83f4a0cf5958b8cf49c938ac99b8b72b420a6/mypy-1.16.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:66df38405fd8466ce3517eda1f6640611a0b8e70895e2a9462d1d4323c5eb4b9", size = 10081687, upload-time = "2025-06-16T16:48:19.367Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/b3/7f/7242062ec6288c33d8ad89574df87c3903d394870e5e6ba1699317a65075/mypy-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d045d33c284e10a038f5e29faca055b90eee87da3fc63b8889085744ebabb5a1", size = 11828306, upload-time = "2025-05-29T13:21:02.164Z" },
|
{ url = "https://files.pythonhosted.org/packages/44/c7/b5cb264c97b86914487d6a24bd8688c0172e37ec0f43e93b9691cae9468b/mypy-1.16.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:44e7acddb3c48bd2713994d098729494117803616e116032af192871aed80b79", size = 11839723, upload-time = "2025-06-16T16:49:20.912Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/6f/5f/b392f7b4f659f5b619ce5994c5c43caab3d80df2296ae54fa888b3d17f5a/mypy-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b4968f14f44c62e2ec4a038c8797a87315be8df7740dc3ee8d3bfe1c6bf5dba8", size = 12702764, upload-time = "2025-05-29T13:20:42.826Z" },
|
{ url = "https://files.pythonhosted.org/packages/15/f8/491997a9b8a554204f834ed4816bda813aefda31cf873bb099deee3c9a99/mypy-1.16.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0ab5eca37b50188163fa7c1b73c685ac66c4e9bdee4a85c9adac0e91d8895e15", size = 12722980, upload-time = "2025-06-16T16:37:40.929Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/9b/c0/7646ef3a00fa39ac9bc0938626d9ff29d19d733011be929cfea59d82d136/mypy-1.16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:eb14a4a871bb8efb1e4a50360d4e3c8d6c601e7a31028a2c79f9bb659b63d730", size = 12896233, upload-time = "2025-05-29T13:18:37.446Z" },
|
{ url = "https://files.pythonhosted.org/packages/df/f0/2bd41e174b5fd93bc9de9a28e4fb673113633b8a7f3a607fa4a73595e468/mypy-1.16.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb6229b2c9086247e21a83c309754b9058b438704ad2f6807f0d8227f6ebdd", size = 12903328, upload-time = "2025-06-16T16:34:35.099Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/6d/38/52f4b808b3fef7f0ef840ee8ff6ce5b5d77381e65425758d515cdd4f5bb5/mypy-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:bd4e1ebe126152a7bbaa4daedd781c90c8f9643c79b9748caa270ad542f12bec", size = 9565547, upload-time = "2025-05-29T13:20:02.836Z" },
|
{ url = "https://files.pythonhosted.org/packages/61/81/5572108a7bec2c46b8aff7e9b524f371fe6ab5efb534d38d6b37b5490da8/mypy-1.16.1-cp312-cp312-win_amd64.whl", hash = "sha256:1f0435cf920e287ff68af3d10a118a73f212deb2ce087619eb4e648116d1fe9b", size = 9562321, upload-time = "2025-06-16T16:48:58.823Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/97/9c/ca03bdbefbaa03b264b9318a98950a9c683e06472226b55472f96ebbc53d/mypy-1.16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a9e056237c89f1587a3be1a3a70a06a698d25e2479b9a2f57325ddaaffc3567b", size = 11059753, upload-time = "2025-05-29T13:18:18.167Z" },
|
{ url = "https://files.pythonhosted.org/packages/28/e3/96964af4a75a949e67df4b95318fe2b7427ac8189bbc3ef28f92a1c5bc56/mypy-1.16.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ddc91eb318c8751c69ddb200a5937f1232ee8efb4e64e9f4bc475a33719de438", size = 11063480, upload-time = "2025-06-16T16:47:56.205Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/36/92/79a969b8302cfe316027c88f7dc6fee70129490a370b3f6eb11d777749d0/mypy-1.16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0b07e107affb9ee6ce1f342c07f51552d126c32cd62955f59a7db94a51ad12c0", size = 10073338, upload-time = "2025-05-29T13:19:48.079Z" },
|
{ url = "https://files.pythonhosted.org/packages/f5/4d/cd1a42b8e5be278fab7010fb289d9307a63e07153f0ae1510a3d7b703193/mypy-1.16.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:87ff2c13d58bdc4bbe7dc0dedfe622c0f04e2cb2a492269f3b418df2de05c536", size = 10090538, upload-time = "2025-06-16T16:46:43.92Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/14/9b/a943f09319167da0552d5cd722104096a9c99270719b1afeea60d11610aa/mypy-1.16.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c6fb60cbd85dc65d4d63d37cb5c86f4e3a301ec605f606ae3a9173e5cf34997b", size = 11827764, upload-time = "2025-05-29T13:46:04.47Z" },
|
{ url = "https://files.pythonhosted.org/packages/c9/4f/c3c6b4b66374b5f68bab07c8cabd63a049ff69796b844bc759a0ca99bb2a/mypy-1.16.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a7cfb0fe29fe5a9841b7c8ee6dffb52382c45acdf68f032145b75620acfbd6f", size = 11836839, upload-time = "2025-06-16T16:36:28.039Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/ec/64/ff75e71c65a0cb6ee737287c7913ea155845a556c64144c65b811afdb9c7/mypy-1.16.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a7e32297a437cc915599e0578fa6bc68ae6a8dc059c9e009c628e1c47f91495d", size = 12701356, upload-time = "2025-05-29T13:35:13.553Z" },
|
{ url = "https://files.pythonhosted.org/packages/b4/7e/81ca3b074021ad9775e5cb97ebe0089c0f13684b066a750b7dc208438403/mypy-1.16.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:051e1677689c9d9578b9c7f4d206d763f9bbd95723cd1416fad50db49d52f359", size = 12715634, upload-time = "2025-06-16T16:50:34.441Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/0a/ad/0e93c18987a1182c350f7a5fab70550852f9fabe30ecb63bfbe51b602074/mypy-1.16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:afe420c9380ccec31e744e8baff0d406c846683681025db3531b32db56962d52", size = 12900745, upload-time = "2025-05-29T13:17:24.409Z" },
|
{ url = "https://files.pythonhosted.org/packages/e9/95/bdd40c8be346fa4c70edb4081d727a54d0a05382d84966869738cfa8a497/mypy-1.16.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d5d2309511cc56c021b4b4e462907c2b12f669b2dbeb68300110ec27723971be", size = 12895584, upload-time = "2025-06-16T16:34:54.857Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/28/5d/036c278d7a013e97e33f08c047fe5583ab4f1fc47c9a49f985f1cdd2a2d7/mypy-1.16.0-cp313-cp313-win_amd64.whl", hash = "sha256:55f9076c6ce55dd3f8cd0c6fff26a008ca8e5131b89d5ba6d86bd3f47e736eeb", size = 9572200, upload-time = "2025-05-29T13:33:44.92Z" },
|
{ url = "https://files.pythonhosted.org/packages/5a/fd/d486a0827a1c597b3b48b1bdef47228a6e9ee8102ab8c28f944cb83b65dc/mypy-1.16.1-cp313-cp313-win_amd64.whl", hash = "sha256:4f58ac32771341e38a853c5d0ec0dfe27e18e27da9cdb8bbc882d2249c71a3ee", size = 9573886, upload-time = "2025-06-16T16:36:43.589Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/99/a3/6ed10530dec8e0fdc890d81361260c9ef1f5e5c217ad8c9b21ecb2b8366b/mypy-1.16.0-py3-none-any.whl", hash = "sha256:29e1499864a3888bca5c1542f2d7232c6e586295183320caa95758fc84034031", size = 2265773, upload-time = "2025-05-29T13:35:18.762Z" },
|
{ url = "https://files.pythonhosted.org/packages/cf/d3/53e684e78e07c1a2bf7105715e5edd09ce951fc3f47cf9ed095ec1b7a037/mypy-1.16.1-py3-none-any.whl", hash = "sha256:5fc2ac4027d0ef28d6ba69a0343737a23c4d1b83672bf38d1fe237bdc0643b37", size = 2265923, upload-time = "2025-06-16T16:48:02.366Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1834,7 +1834,7 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pydantic"
|
name = "pydantic"
|
||||||
version = "2.11.5"
|
version = "2.11.7"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "annotated-types" },
|
{ name = "annotated-types" },
|
||||||
@@ -1842,9 +1842,9 @@ dependencies = [
|
|||||||
{ name = "typing-extensions" },
|
{ name = "typing-extensions" },
|
||||||
{ name = "typing-inspection" },
|
{ name = "typing-inspection" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/f0/86/8ce9040065e8f924d642c58e4a344e33163a07f6b57f836d0d734e0ad3fb/pydantic-2.11.5.tar.gz", hash = "sha256:7f853db3d0ce78ce8bbb148c401c2cdd6431b3473c0cdff2755c7690952a7b7a", size = 787102, upload-time = "2025-05-22T21:18:08.761Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/00/dd/4325abf92c39ba8623b5af936ddb36ffcfe0beae70405d456ab1fb2f5b8c/pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db", size = 788350, upload-time = "2025-06-14T08:33:17.137Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/b5/69/831ed22b38ff9b4b64b66569f0e5b7b97cf3638346eb95a2147fdb49ad5f/pydantic-2.11.5-py3-none-any.whl", hash = "sha256:f9c26ba06f9747749ca1e5c94d6a85cb84254577553c8785576fd38fa64dc0f7", size = 444229, upload-time = "2025-05-22T21:18:06.329Z" },
|
{ url = "https://files.pythonhosted.org/packages/6a/c0/ec2b1c8712ca690e5d61979dee872603e92b8a32f94cc1b72d53beab008a/pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b", size = 444782, upload-time = "2025-06-14T08:33:14.905Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1977,7 +1977,7 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pytest"
|
name = "pytest"
|
||||||
version = "8.4.0"
|
version = "8.4.1"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
||||||
@@ -1988,9 +1988,9 @@ dependencies = [
|
|||||||
{ name = "pygments" },
|
{ name = "pygments" },
|
||||||
{ name = "tomli", marker = "python_full_version < '3.11'" },
|
{ name = "tomli", marker = "python_full_version < '3.11'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/fb/aa/405082ce2749be5398045152251ac69c0f3578c7077efc53431303af97ce/pytest-8.4.0.tar.gz", hash = "sha256:14d920b48472ea0dbf68e45b96cd1ffda4705f33307dcc86c676c1b5104838a6", size = 1515232, upload-time = "2025-06-02T17:36:30.03Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/08/ba/45911d754e8eba3d5a841a5ce61a65a685ff1798421ac054f85aa8747dfb/pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c", size = 1517714, upload-time = "2025-06-18T05:48:06.109Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/2f/de/afa024cbe022b1b318a3d224125aa24939e99b4ff6f22e0ba639a2eaee47/pytest-8.4.0-py3-none-any.whl", hash = "sha256:f40f825768ad76c0977cbacdf1fd37c6f7a468e460ea6a0636078f8972d4517e", size = 363797, upload-time = "2025-06-02T17:36:27.859Z" },
|
{ url = "https://files.pythonhosted.org/packages/29/16/c8a903f4c4dffe7a12843191437d7cd8e32751d5de349d45d3fe69544e87/pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7", size = 365474, upload-time = "2025-06-18T05:48:03.955Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2007,15 +2007,16 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pytest-cov"
|
name = "pytest-cov"
|
||||||
version = "6.1.1"
|
version = "6.2.1"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "coverage", extra = ["toml"] },
|
{ name = "coverage", extra = ["toml"] },
|
||||||
|
{ name = "pluggy" },
|
||||||
{ name = "pytest" },
|
{ name = "pytest" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/25/69/5f1e57f6c5a39f81411b550027bf72842c4567ff5fd572bed1edc9e4b5d9/pytest_cov-6.1.1.tar.gz", hash = "sha256:46935f7aaefba760e716c2ebfbe1c216240b9592966e7da99ea8292d4d3e2a0a", size = 66857, upload-time = "2025-04-05T14:07:51.592Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/18/99/668cade231f434aaa59bbfbf49469068d2ddd945000621d3d165d2e7dd7b/pytest_cov-6.2.1.tar.gz", hash = "sha256:25cc6cc0a5358204b8108ecedc51a9b57b34cc6b8c967cc2c01a4e00d8a67da2", size = 69432, upload-time = "2025-06-12T10:47:47.684Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/28/d0/def53b4a790cfb21483016430ed828f64830dd981ebe1089971cd10cab25/pytest_cov-6.1.1-py3-none-any.whl", hash = "sha256:bddf29ed2d0ab6f4df17b4c55b0a657287db8684af9c42ea546b21b1041b3dde", size = 23841, upload-time = "2025-04-05T14:07:49.641Z" },
|
{ url = "https://files.pythonhosted.org/packages/bc/16/4ea354101abb1287856baa4af2732be351c7bee728065aed451b678153fd/pytest_cov-6.2.1-py3-none-any.whl", hash = "sha256:f5bc4c23f42f1cdd23c70b1dab1bbaef4fc505ba950d53e0081d0730dd7e86d5", size = 24644, upload-time = "2025-06-12T10:47:45.932Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2303,27 +2304,27 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruff"
|
name = "ruff"
|
||||||
version = "0.11.13"
|
version = "0.12.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/ed/da/9c6f995903b4d9474b39da91d2d626659af3ff1eeb43e9ae7c119349dba6/ruff-0.11.13.tar.gz", hash = "sha256:26fa247dc68d1d4e72c179e08889a25ac0c7ba4d78aecfc835d49cbfd60bf514", size = 4282054, upload-time = "2025-06-05T21:00:15.721Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/24/90/5255432602c0b196a0da6720f6f76b93eb50baef46d3c9b0025e2f9acbf3/ruff-0.12.0.tar.gz", hash = "sha256:4d047db3662418d4a848a3fdbfaf17488b34b62f527ed6f10cb8afd78135bc5c", size = 4376101, upload-time = "2025-06-17T15:19:26.217Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/7d/ce/a11d381192966e0b4290842cc8d4fac7dc9214ddf627c11c1afff87da29b/ruff-0.11.13-py3-none-linux_armv6l.whl", hash = "sha256:4bdfbf1240533f40042ec00c9e09a3aade6f8c10b6414cf11b519488d2635d46", size = 10292516, upload-time = "2025-06-05T20:59:32.944Z" },
|
{ url = "https://files.pythonhosted.org/packages/e6/fd/b46bb20e14b11ff49dbc74c61de352e0dc07fb650189513631f6fb5fc69f/ruff-0.12.0-py3-none-linux_armv6l.whl", hash = "sha256:5652a9ecdb308a1754d96a68827755f28d5dfb416b06f60fd9e13f26191a8848", size = 10311554, upload-time = "2025-06-17T15:18:45.792Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/78/db/87c3b59b0d4e753e40b6a3b4a2642dfd1dcaefbff121ddc64d6c8b47ba00/ruff-0.11.13-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:aef9c9ed1b5ca28bb15c7eac83b8670cf3b20b478195bd49c8d756ba0a36cf48", size = 11106083, upload-time = "2025-06-05T20:59:37.03Z" },
|
{ url = "https://files.pythonhosted.org/packages/e7/d3/021dde5a988fa3e25d2468d1dadeea0ae89dc4bc67d0140c6e68818a12a1/ruff-0.12.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:05ed0c914fabc602fc1f3b42c53aa219e5736cb030cdd85640c32dbc73da74a6", size = 11118435, upload-time = "2025-06-17T15:18:49.064Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/77/79/d8cec175856ff810a19825d09ce700265f905c643c69f45d2b737e4a470a/ruff-0.11.13-py3-none-macosx_11_0_arm64.whl", hash = "sha256:53b15a9dfdce029c842e9a5aebc3855e9ab7771395979ff85b7c1dedb53ddc2b", size = 10436024, upload-time = "2025-06-05T20:59:39.741Z" },
|
{ url = "https://files.pythonhosted.org/packages/07/a2/01a5acf495265c667686ec418f19fd5c32bcc326d4c79ac28824aecd6a32/ruff-0.12.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:07a7aa9b69ac3fcfda3c507916d5d1bca10821fe3797d46bad10f2c6de1edda0", size = 10466010, upload-time = "2025-06-17T15:18:51.341Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/8b/5b/f6d94f2980fa1ee854b41568368a2e1252681b9238ab2895e133d303538f/ruff-0.11.13-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab153241400789138d13f362c43f7edecc0edfffce2afa6a68434000ecd8f69a", size = 10646324, upload-time = "2025-06-05T20:59:42.185Z" },
|
{ url = "https://files.pythonhosted.org/packages/4c/57/7caf31dd947d72e7aa06c60ecb19c135cad871a0a8a251723088132ce801/ruff-0.12.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e7731c3eec50af71597243bace7ec6104616ca56dda2b99c89935fe926bdcd48", size = 10661366, upload-time = "2025-06-17T15:18:53.29Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/6c/9c/b4c2acf24ea4426016d511dfdc787f4ce1ceb835f3c5fbdbcb32b1c63bda/ruff-0.11.13-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6c51f93029d54a910d3d24f7dd0bb909e31b6cd989a5e4ac513f4eb41629f0dc", size = 10174416, upload-time = "2025-06-05T20:59:44.319Z" },
|
{ url = "https://files.pythonhosted.org/packages/e9/ba/aa393b972a782b4bc9ea121e0e358a18981980856190d7d2b6187f63e03a/ruff-0.12.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:952d0630eae628250ab1c70a7fffb641b03e6b4a2d3f3ec6c1d19b4ab6c6c807", size = 10173492, upload-time = "2025-06-17T15:18:55.262Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/f3/10/e2e62f77c65ede8cd032c2ca39c41f48feabedb6e282bfd6073d81bb671d/ruff-0.11.13-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1808b3ed53e1a777c2ef733aca9051dc9bf7c99b26ece15cb59a0320fbdbd629", size = 11724197, upload-time = "2025-06-05T20:59:46.935Z" },
|
{ url = "https://files.pythonhosted.org/packages/d7/50/9349ee777614bc3062fc6b038503a59b2034d09dd259daf8192f56c06720/ruff-0.12.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c021f04ea06966b02614d442e94071781c424ab8e02ec7af2f037b4c1e01cc82", size = 11761739, upload-time = "2025-06-17T15:18:58.906Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/bb/f0/466fe8469b85c561e081d798c45f8a1d21e0b4a5ef795a1d7f1a9a9ec182/ruff-0.11.13-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:d28ce58b5ecf0f43c1b71edffabe6ed7f245d5336b17805803312ec9bc665933", size = 12511615, upload-time = "2025-06-05T20:59:49.534Z" },
|
{ url = "https://files.pythonhosted.org/packages/04/8f/ad459de67c70ec112e2ba7206841c8f4eb340a03ee6a5cabc159fe558b8e/ruff-0.12.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:7d235618283718ee2fe14db07f954f9b2423700919dc688eacf3f8797a11315c", size = 12537098, upload-time = "2025-06-17T15:19:01.316Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/17/0e/cefe778b46dbd0cbcb03a839946c8f80a06f7968eb298aa4d1a4293f3448/ruff-0.11.13-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55e4bc3a77842da33c16d55b32c6cac1ec5fb0fbec9c8c513bdce76c4f922165", size = 12117080, upload-time = "2025-06-05T20:59:51.654Z" },
|
{ url = "https://files.pythonhosted.org/packages/ed/50/15ad9c80ebd3c4819f5bd8883e57329f538704ed57bac680d95cb6627527/ruff-0.12.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0c0758038f81beec8cc52ca22de9685b8ae7f7cc18c013ec2050012862cc9165", size = 12154122, upload-time = "2025-06-17T15:19:03.727Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/5d/2c/caaeda564cbe103bed145ea557cb86795b18651b0f6b3ff6a10e84e5a33f/ruff-0.11.13-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:633bf2c6f35678c56ec73189ba6fa19ff1c5e4807a78bf60ef487b9dd272cc71", size = 11326315, upload-time = "2025-06-05T20:59:54.469Z" },
|
{ url = "https://files.pythonhosted.org/packages/76/e6/79b91e41bc8cc3e78ee95c87093c6cacfa275c786e53c9b11b9358026b3d/ruff-0.12.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:139b3d28027987b78fc8d6cfb61165447bdf3740e650b7c480744873688808c2", size = 11363374, upload-time = "2025-06-17T15:19:05.875Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/75/f0/782e7d681d660eda8c536962920c41309e6dd4ebcea9a2714ed5127d44bd/ruff-0.11.13-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ffbc82d70424b275b089166310448051afdc6e914fdab90e08df66c43bb5ca9", size = 11555640, upload-time = "2025-06-05T20:59:56.986Z" },
|
{ url = "https://files.pythonhosted.org/packages/db/c3/82b292ff8a561850934549aa9dc39e2c4e783ab3c21debe55a495ddf7827/ruff-0.12.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68853e8517b17bba004152aebd9dd77d5213e503a5f2789395b25f26acac0da4", size = 11587647, upload-time = "2025-06-17T15:19:08.246Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/5d/d4/3d580c616316c7f07fb3c99dbecfe01fbaea7b6fd9a82b801e72e5de742a/ruff-0.11.13-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:4a9ddd3ec62a9a89578c85842b836e4ac832d4a2e0bfaad3b02243f930ceafcc", size = 10507364, upload-time = "2025-06-05T20:59:59.154Z" },
|
{ url = "https://files.pythonhosted.org/packages/2b/42/d5760d742669f285909de1bbf50289baccb647b53e99b8a3b4f7ce1b2001/ruff-0.12.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:3a9512af224b9ac4757f7010843771da6b2b0935a9e5e76bb407caa901a1a514", size = 10527284, upload-time = "2025-06-17T15:19:10.37Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/5a/dc/195e6f17d7b3ea6b12dc4f3e9de575db7983db187c378d44606e5d503319/ruff-0.11.13-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d237a496e0778d719efb05058c64d28b757c77824e04ffe8796c7436e26712b7", size = 10141462, upload-time = "2025-06-05T21:00:01.481Z" },
|
{ url = "https://files.pythonhosted.org/packages/19/f6/fcee9935f25a8a8bba4adbae62495c39ef281256693962c2159e8b284c5f/ruff-0.12.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:b08df3d96db798e5beb488d4df03011874aff919a97dcc2dd8539bb2be5d6a88", size = 10158609, upload-time = "2025-06-17T15:19:12.286Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/f4/8e/39a094af6967faa57ecdeacb91bedfb232474ff8c3d20f16a5514e6b3534/ruff-0.11.13-py3-none-musllinux_1_2_i686.whl", hash = "sha256:26816a218ca6ef02142343fd24c70f7cd8c5aa6c203bca284407adf675984432", size = 11121028, upload-time = "2025-06-05T21:00:04.06Z" },
|
{ url = "https://files.pythonhosted.org/packages/37/fb/057febf0eea07b9384787bfe197e8b3384aa05faa0d6bd844b94ceb29945/ruff-0.12.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:6a315992297a7435a66259073681bb0d8647a826b7a6de45c6934b2ca3a9ed51", size = 11141462, upload-time = "2025-06-17T15:19:15.195Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/5a/c0/b0b508193b0e8a1654ec683ebab18d309861f8bd64e3a2f9648b80d392cb/ruff-0.11.13-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:51c3f95abd9331dc5b87c47ac7f376db5616041173826dfd556cfe3d4977f492", size = 11602992, upload-time = "2025-06-05T21:00:06.249Z" },
|
{ url = "https://files.pythonhosted.org/packages/10/7c/1be8571011585914b9d23c95b15d07eec2d2303e94a03df58294bc9274d4/ruff-0.12.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:1e55e44e770e061f55a7dbc6e9aed47feea07731d809a3710feda2262d2d4d8a", size = 11641616, upload-time = "2025-06-17T15:19:17.6Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/7c/91/263e33ab93ab09ca06ce4f8f8547a858cc198072f873ebc9be7466790bae/ruff-0.11.13-py3-none-win32.whl", hash = "sha256:96c27935418e4e8e77a26bb05962817f28b8ef3843a6c6cc49d8783b5507f250", size = 10474944, upload-time = "2025-06-05T21:00:08.459Z" },
|
{ url = "https://files.pythonhosted.org/packages/6a/ef/b960ab4818f90ff59e571d03c3f992828d4683561095e80f9ef31f3d58b7/ruff-0.12.0-py3-none-win32.whl", hash = "sha256:7162a4c816f8d1555eb195c46ae0bd819834d2a3f18f98cc63819a7b46f474fb", size = 10525289, upload-time = "2025-06-17T15:19:19.688Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/46/f4/7c27734ac2073aae8efb0119cae6931b6fb48017adf048fdf85c19337afc/ruff-0.11.13-py3-none-win_amd64.whl", hash = "sha256:29c3189895a8a6a657b7af4e97d330c8a3afd2c9c8f46c81e2fc5a31866517e3", size = 11548669, upload-time = "2025-06-05T21:00:11.147Z" },
|
{ url = "https://files.pythonhosted.org/packages/34/93/8b16034d493ef958a500f17cda3496c63a537ce9d5a6479feec9558f1695/ruff-0.12.0-py3-none-win_amd64.whl", hash = "sha256:d00b7a157b8fb6d3827b49d3324da34a1e3f93492c1f97b08e222ad7e9b291e0", size = 11598311, upload-time = "2025-06-17T15:19:21.785Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/ec/bf/b273dd11673fed8a6bd46032c0ea2a04b2ac9bfa9c628756a5856ba113b0/ruff-0.11.13-py3-none-win_arm64.whl", hash = "sha256:b4385285e9179d608ff1d2fb9922062663c658605819a6876d8beef0c30b7f3b", size = 10683928, upload-time = "2025-06-05T21:00:13.758Z" },
|
{ url = "https://files.pythonhosted.org/packages/d0/33/4d3e79e4a84533d6cd526bfb42c020a23256ae5e4265d858bd1287831f7d/ruff-0.12.0-py3-none-win_arm64.whl", hash = "sha256:8cd24580405ad8c1cc64d61725bca091d6b6da7eb3d36f72cc605467069d7e8b", size = 10724946, upload-time = "2025-06-17T15:19:23.952Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2558,14 +2559,14 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "types-requests"
|
name = "types-requests"
|
||||||
version = "2.32.0.20250602"
|
version = "2.32.4.20250611"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "urllib3" },
|
{ name = "urllib3" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/48/b0/5321e6eeba5d59e4347fcf9bf06a5052f085c3aa0f4876230566d6a4dc97/types_requests-2.32.0.20250602.tar.gz", hash = "sha256:ee603aeefec42051195ae62ca7667cd909a2f8128fdf8aad9e8a5219ecfab3bf", size = 23042, upload-time = "2025-06-02T03:15:02.958Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/6d/7f/73b3a04a53b0fd2a911d4ec517940ecd6600630b559e4505cc7b68beb5a0/types_requests-2.32.4.20250611.tar.gz", hash = "sha256:741c8777ed6425830bf51e54d6abe245f79b4dcb9019f1622b773463946bf826", size = 23118, upload-time = "2025-06-11T03:11:41.272Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/da/18/9b782980e575c6581d5c0c1c99f4c6f89a1d7173dad072ee96b2756c02e6/types_requests-2.32.0.20250602-py3-none-any.whl", hash = "sha256:f4f335f87779b47ce10b8b8597b409130299f6971ead27fead4fe7ba6ea3e726", size = 20638, upload-time = "2025-06-02T03:15:01.959Z" },
|
{ url = "https://files.pythonhosted.org/packages/3d/ea/0be9258c5a4fa1ba2300111aa5a0767ee6d18eb3fd20e91616c12082284d/types_requests-2.32.4.20250611-py3-none-any.whl", hash = "sha256:ad2fe5d3b0cb3c2c902c8815a70e7fb2302c4b8c1f77bdcd738192cdb3878072", size = 20643, upload-time = "2025-06-11T03:11:40.186Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
@@ -146,6 +146,7 @@ dart_code_metrics:
|
|||||||
# - no-empty-block
|
# - no-empty-block
|
||||||
# - no-equal-then-else
|
# - no-equal-then-else
|
||||||
# - prefer-correct-test-file-name
|
# - prefer-correct-test-file-name
|
||||||
|
- prefer-const-border-radius
|
||||||
# - prefer-match-file-name
|
# - prefer-match-file-name
|
||||||
# - prefer-return-await
|
# - prefer-return-await
|
||||||
# - avoid-self-assignment
|
# - avoid-self-assignment
|
||||||
@@ -290,7 +291,8 @@ dart_code_metrics:
|
|||||||
# Style
|
# Style
|
||||||
# - prefer-trailing-comma
|
# - prefer-trailing-comma
|
||||||
# - unnecessary-trailing-comma
|
# - unnecessary-trailing-comma
|
||||||
# - prefer-declaring-const-constructor
|
- prefer-declaring-const-constructor
|
||||||
# - prefer-single-widget-per-file
|
# - prefer-single-widget-per-file
|
||||||
|
- prefer-switch-expression
|
||||||
# - prefer-prefixed-global-constants
|
# - prefer-prefixed-global-constants
|
||||||
# - prefer-correct-callback-field-name
|
# - prefer-correct-callback-field-name
|
||||||
|
|||||||
@@ -90,6 +90,35 @@
|
|||||||
<data android:mimeType="video/*" />
|
<data android:mimeType="video/*" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
|
<!-- immich:// URL scheme handling -->
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
<data android:scheme="immich" />
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
|
<!-- my.immich.app deep link -->
|
||||||
|
<intent-filter android:autoVerify="true">
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
|
<data android:scheme="https" />
|
||||||
|
|
||||||
|
<data
|
||||||
|
android:host="my.immich.app"
|
||||||
|
android:path="/" />
|
||||||
|
<data
|
||||||
|
android:host="my.immich.app"
|
||||||
|
android:pathPrefix="/albums/" />
|
||||||
|
<data
|
||||||
|
android:host="my.immich.app"
|
||||||
|
android:pathPrefix="/memories/" />
|
||||||
|
<data
|
||||||
|
android:host="my.immich.app"
|
||||||
|
android:pathPrefix="/photos/" />
|
||||||
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -35,8 +35,8 @@ platform :android do
|
|||||||
task: 'bundle',
|
task: 'bundle',
|
||||||
build_type: 'Release',
|
build_type: 'Release',
|
||||||
properties: {
|
properties: {
|
||||||
"android.injected.version.code" => 201,
|
"android.injected.version.code" => 204,
|
||||||
"android.injected.version.name" => "1.135.0",
|
"android.injected.version.name" => "1.135.3",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
upload_to_play_store(skip_upload_apk: true, skip_upload_images: true, skip_upload_screenshots: true, aab: '../build/app/outputs/bundle/release/app-release.aab')
|
upload_to_play_store(skip_upload_apk: true, skip_upload_images: true, skip_upload_screenshots: true, aab: '../build/app/outputs/bundle/release/app-release.aab')
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import 'general_helper.dart';
|
|||||||
class ImmichTestLoginHelper {
|
class ImmichTestLoginHelper {
|
||||||
final WidgetTester tester;
|
final WidgetTester tester;
|
||||||
|
|
||||||
ImmichTestLoginHelper(this.tester);
|
const ImmichTestLoginHelper(this.tester);
|
||||||
|
|
||||||
Future<void> waitForLoginScreen() async {
|
Future<void> waitForLoginScreen() async {
|
||||||
await pumpUntilFound(tester, find.text("Login"));
|
await pumpUntilFound(tester, find.text("Login"));
|
||||||
@@ -60,11 +60,11 @@ class ImmichTestLoginHelper {
|
|||||||
await tester.tap(button);
|
await tester.tap(button);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> assertLoginSuccess({int timeoutSeconds = 15}) async {
|
Future<void> assertLoginSuccess() async {
|
||||||
await pumpUntilFound(tester, find.text("home_page_building_timeline".tr()));
|
await pumpUntilFound(tester, find.text("home_page_building_timeline".tr()));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> assertLoginFailed({int timeoutSeconds = 15}) async {
|
Future<void> assertLoginFailed() async {
|
||||||
await pumpUntilFound(tester, find.text("login_form_failed_login".tr()));
|
await pumpUntilFound(tester, find.text("login_form_failed_login".tr()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -346,6 +346,7 @@
|
|||||||
};
|
};
|
||||||
F0B57D372DF764BD00DC5BCC = {
|
F0B57D372DF764BD00DC5BCC = {
|
||||||
CreatedOnToolsVersion = 16.4;
|
CreatedOnToolsVersion = 16.4;
|
||||||
|
ProvisioningStyle = Automatic;
|
||||||
};
|
};
|
||||||
FAC6F88F2D287C890078CB2F = {
|
FAC6F88F2D287C890078CB2F = {
|
||||||
CreatedOnToolsVersion = 16.0;
|
CreatedOnToolsVersion = 16.0;
|
||||||
@@ -648,7 +649,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = Runner/RunnerProfile.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Runner/RunnerProfile.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 208;
|
CURRENT_PROJECT_VERSION = 210;
|
||||||
CUSTOM_GROUP_ID = group.app.immich.share;
|
CUSTOM_GROUP_ID = group.app.immich.share;
|
||||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
@@ -792,7 +793,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 208;
|
CURRENT_PROJECT_VERSION = 210;
|
||||||
CUSTOM_GROUP_ID = group.app.immich.share;
|
CUSTOM_GROUP_ID = group.app.immich.share;
|
||||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
@@ -822,7 +823,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 208;
|
CURRENT_PROJECT_VERSION = 210;
|
||||||
CUSTOM_GROUP_ID = group.app.immich.share;
|
CUSTOM_GROUP_ID = group.app.immich.share;
|
||||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
@@ -856,7 +857,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements;
|
CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 210;
|
||||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||||
@@ -864,7 +865,7 @@
|
|||||||
INFOPLIST_FILE = WidgetExtension/Info.plist;
|
INFOPLIST_FILE = WidgetExtension/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = Widget;
|
INFOPLIST_KEY_CFBundleDisplayName = Widget;
|
||||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 18.5;
|
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
@@ -899,7 +900,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements;
|
CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 210;
|
||||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||||
@@ -907,7 +908,7 @@
|
|||||||
INFOPLIST_FILE = WidgetExtension/Info.plist;
|
INFOPLIST_FILE = WidgetExtension/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = Widget;
|
INFOPLIST_KEY_CFBundleDisplayName = Widget;
|
||||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 18.5;
|
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
@@ -939,7 +940,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements;
|
CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 210;
|
||||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||||
@@ -947,7 +948,7 @@
|
|||||||
INFOPLIST_FILE = WidgetExtension/Info.plist;
|
INFOPLIST_FILE = WidgetExtension/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = Widget;
|
INFOPLIST_KEY_CFBundleDisplayName = Widget;
|
||||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 18.5;
|
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
@@ -978,7 +979,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
|
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 208;
|
CURRENT_PROJECT_VERSION = 210;
|
||||||
CUSTOM_GROUP_ID = group.app.immich.share;
|
CUSTOM_GROUP_ID = group.app.immich.share;
|
||||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
@@ -1022,7 +1023,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
|
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 208;
|
CURRENT_PROJECT_VERSION = 210;
|
||||||
CUSTOM_GROUP_ID = group.app.immich.share;
|
CUSTOM_GROUP_ID = group.app.immich.share;
|
||||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
@@ -1063,7 +1064,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
|
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 208;
|
CURRENT_PROJECT_VERSION = 210;
|
||||||
CUSTOM_GROUP_ID = group.app.immich.share;
|
CUSTOM_GROUP_ID = group.app.immich.share;
|
||||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
|
|||||||
@@ -78,7 +78,7 @@
|
|||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>1.134.0</string>
|
<string>1.135.1</string>
|
||||||
<key>CFBundleSignature</key>
|
<key>CFBundleSignature</key>
|
||||||
<string>????</string>
|
<string>????</string>
|
||||||
<key>CFBundleURLTypes</key>
|
<key>CFBundleURLTypes</key>
|
||||||
@@ -86,14 +86,26 @@
|
|||||||
<dict>
|
<dict>
|
||||||
<key>CFBundleTypeRole</key>
|
<key>CFBundleTypeRole</key>
|
||||||
<string>Editor</string>
|
<string>Editor</string>
|
||||||
|
<key>CFBundleURLName</key>
|
||||||
|
<string>Share Extension</string>
|
||||||
<key>CFBundleURLSchemes</key>
|
<key>CFBundleURLSchemes</key>
|
||||||
<array>
|
<array>
|
||||||
<string>ShareMedia-$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
<string>ShareMedia-$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleTypeRole</key>
|
||||||
|
<string>Editor</string>
|
||||||
|
<key>CFBundleURLName</key>
|
||||||
|
<string>Deep Link</string>
|
||||||
|
<key>CFBundleURLSchemes</key>
|
||||||
|
<array>
|
||||||
|
<string>immich</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
</array>
|
</array>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>208</string>
|
<string>210</string>
|
||||||
<key>FLTEnableImpeller</key>
|
<key>FLTEnableImpeller</key>
|
||||||
<true />
|
<true />
|
||||||
<key>ITSAppUsesNonExemptEncryption</key>
|
<key>ITSAppUsesNonExemptEncryption</key>
|
||||||
@@ -115,11 +127,13 @@
|
|||||||
</dict>
|
</dict>
|
||||||
<key>NSBonjourServices</key>
|
<key>NSBonjourServices</key>
|
||||||
<array>
|
<array>
|
||||||
<string>_googlecast._tcp</string>
|
<string>_googlecast._tcp</string>
|
||||||
<string>_CC1AD845._googlecast._tcp</string>
|
<string>_CC1AD845._googlecast._tcp</string>
|
||||||
</array>
|
</array>
|
||||||
<key>NSCameraUsageDescription</key>
|
<key>NSCameraUsageDescription</key>
|
||||||
<string>We need to access the camera to let you take beautiful video using this app</string>
|
<string>We need to access the camera to let you take beautiful video using this app</string>
|
||||||
|
<key>NSFaceIDUsageDescription</key>
|
||||||
|
<string>We need to use FaceID to allow access to your locked folder</string>
|
||||||
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
|
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
|
||||||
<string>We require this permission to access the local WiFi name for background upload mechanism</string>
|
<string>We require this permission to access the local WiFi name for background upload mechanism</string>
|
||||||
<key>NSLocationUsageDescription</key>
|
<key>NSLocationUsageDescription</key>
|
||||||
@@ -166,7 +180,8 @@
|
|||||||
<true />
|
<true />
|
||||||
<key>io.flutter.embedded_views_preview</key>
|
<key>io.flutter.embedded_views_preview</key>
|
||||||
<true />
|
<true />
|
||||||
<key>NSFaceIDUsageDescription</key>
|
<key>NSLocalNetworkUsageDescription</key>
|
||||||
<string>We need to use FaceID to allow access to your locked folder</string>
|
<string>We need local network permission to connect to the local server using IP address and
|
||||||
|
allow the casting feature to work</string>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
@@ -2,6 +2,10 @@
|
|||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
|
<key>com.apple.developer.associated-domains</key>
|
||||||
|
<array>
|
||||||
|
<string>applinks:my.immich.app</string>
|
||||||
|
</array>
|
||||||
<key>com.apple.developer.networking.wifi-info</key>
|
<key>com.apple.developer.networking.wifi-info</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>com.apple.security.application-groups</key>
|
<key>com.apple.security.application-groups</key>
|
||||||
|
|||||||
@@ -4,6 +4,10 @@
|
|||||||
<dict>
|
<dict>
|
||||||
<key>aps-environment</key>
|
<key>aps-environment</key>
|
||||||
<string>development</string>
|
<string>development</string>
|
||||||
|
<key>com.apple.developer.associated-domains</key>
|
||||||
|
<array>
|
||||||
|
<string>applinks:my.immich.app</string>
|
||||||
|
</array>
|
||||||
<key>com.apple.developer.networking.wifi-info</key>
|
<key>com.apple.developer.networking.wifi-info</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>com.apple.security.application-groups</key>
|
<key>com.apple.security.application-groups</key>
|
||||||
|
|||||||
@@ -24,27 +24,11 @@ struct ImageEntry: TimelineEntry {
|
|||||||
struct ImmichWidgetView: View {
|
struct ImmichWidgetView: View {
|
||||||
var entry: ImageEntry
|
var entry: ImageEntry
|
||||||
|
|
||||||
func getErrorText(_ error: WidgetError?) -> String {
|
|
||||||
switch error {
|
|
||||||
case .noLogin:
|
|
||||||
return "Login to Immich"
|
|
||||||
|
|
||||||
case .fetchFailed:
|
|
||||||
return "Unable to connect to your Immich instance"
|
|
||||||
|
|
||||||
case .albumNotFound:
|
|
||||||
return "Album not found"
|
|
||||||
|
|
||||||
default:
|
|
||||||
return "An unknown error occured"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
if entry.image == nil {
|
if entry.image == nil {
|
||||||
VStack {
|
VStack {
|
||||||
Image("LaunchImage")
|
Image("LaunchImage")
|
||||||
Text(getErrorText(entry.error))
|
Text(entry.error?.errorDescription ?? "")
|
||||||
.minimumScaleFactor(0.25)
|
.minimumScaleFactor(0.25)
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
.foregroundStyle(.secondary)
|
.foregroundStyle(.secondary)
|
||||||
|
|||||||
@@ -8,6 +8,32 @@ enum WidgetError: Error {
|
|||||||
case unknown
|
case unknown
|
||||||
case albumNotFound
|
case albumNotFound
|
||||||
case unableToResize
|
case unableToResize
|
||||||
|
case invalidImage
|
||||||
|
case invalidURL
|
||||||
|
}
|
||||||
|
|
||||||
|
extension WidgetError: LocalizedError {
|
||||||
|
public var errorDescription: String? {
|
||||||
|
switch self {
|
||||||
|
case .noLogin:
|
||||||
|
return "Login to Immich"
|
||||||
|
|
||||||
|
case .fetchFailed:
|
||||||
|
return "Unable to connect to your Immich instance"
|
||||||
|
|
||||||
|
case .albumNotFound:
|
||||||
|
return "Album not found"
|
||||||
|
|
||||||
|
case .invalidURL:
|
||||||
|
return "An invalid URL was used"
|
||||||
|
|
||||||
|
case .invalidImage:
|
||||||
|
return "An invalid image was received"
|
||||||
|
|
||||||
|
default:
|
||||||
|
return "An unknown error occured"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum AssetType: String, Codable {
|
enum AssetType: String, Codable {
|
||||||
@@ -146,7 +172,7 @@ class ImmichAPI {
|
|||||||
return try JSONDecoder().decode([MemoryResult].self, from: data)
|
return try JSONDecoder().decode([MemoryResult].self, from: data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchImage(asset: SearchResult) async throws -> UIImage {
|
func fetchImage(asset: SearchResult) async throws(WidgetError) -> UIImage {
|
||||||
let thumbnailParams = [URLQueryItem(name: "size", value: "preview")]
|
let thumbnailParams = [URLQueryItem(name: "size", value: "preview")]
|
||||||
let assetEndpoint = "/assets/" + asset.id + "/thumbnail"
|
let assetEndpoint = "/assets/" + asset.id + "/thumbnail"
|
||||||
|
|
||||||
@@ -157,16 +183,24 @@ class ImmichAPI {
|
|||||||
params: thumbnailParams
|
params: thumbnailParams
|
||||||
)
|
)
|
||||||
else {
|
else {
|
||||||
throw URLError(.badURL)
|
throw .invalidURL
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let imageSource = CGImageSourceCreateWithURL(fetchURL as CFURL, nil) else {
|
||||||
|
throw .invalidURL
|
||||||
}
|
}
|
||||||
|
|
||||||
let (data, _) = try await URLSession.shared.data(from: fetchURL)
|
let decodeOptions: [NSString: Any] = [
|
||||||
|
kCGImageSourceCreateThumbnailFromImageAlways: true,
|
||||||
guard let img = UIImage(data: data) else {
|
kCGImageSourceThumbnailMaxPixelSize: 400,
|
||||||
throw URLError(.badServerResponse)
|
kCGImageSourceCreateThumbnailWithTransform: true
|
||||||
|
]
|
||||||
|
|
||||||
|
guard let thumbnail = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, decodeOptions as CFDictionary) else {
|
||||||
|
throw .fetchFailed
|
||||||
}
|
}
|
||||||
|
|
||||||
return img
|
return UIImage(cgImage: thumbnail)
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchAlbums() async throws -> [Album] {
|
func fetchAlbums() async throws -> [Album] {
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ struct RandomConfigurationAppIntent: WidgetConfigurationIntent {
|
|||||||
"Choose an album to show images from"
|
"Choose an album to show images from"
|
||||||
}
|
}
|
||||||
|
|
||||||
@Parameter(title: "Album", default: NO_ALBUM)
|
@Parameter(title: "Album")
|
||||||
var album: Album?
|
var album: Album?
|
||||||
|
|
||||||
@Parameter(title: "Show Album Name", default: false)
|
@Parameter(title: "Show Album Name", default: false)
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ platform :ios do
|
|||||||
path: "./Runner.xcodeproj",
|
path: "./Runner.xcodeproj",
|
||||||
)
|
)
|
||||||
increment_version_number(
|
increment_version_number(
|
||||||
version_number: "1.135.0"
|
version_number: "1.135.3"
|
||||||
)
|
)
|
||||||
increment_build_number(
|
increment_build_number(
|
||||||
build_number: latest_testflight_build_number + 1,
|
build_number: latest_testflight_build_number + 1,
|
||||||
|
|||||||
@@ -10,3 +10,5 @@ enum TextSearchType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum AssetVisibilityEnum { timeline, hidden, archive, locked }
|
enum AssetVisibilityEnum { timeline, hidden, archive, locked }
|
||||||
|
|
||||||
|
enum SortUserBy { id }
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ sealed class ImmichErrors {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class NoResponseDtoError extends ImmichErrors implements Exception {
|
class NoResponseDtoError extends ImmichErrors implements Exception {
|
||||||
|
const NoResponseDtoError();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => "Response Dto is null";
|
String toString() => "Response Dto is null";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ const Map<String, Locale> locales = {
|
|||||||
Locale.fromSubtags(languageCode: 'zh', scriptCode: 'SIMPLIFIED'),
|
Locale.fromSubtags(languageCode: 'zh', scriptCode: 'SIMPLIFIED'),
|
||||||
'Chinese Traditional (zh_TW)':
|
'Chinese Traditional (zh_TW)':
|
||||||
Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant'),
|
Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant'),
|
||||||
|
'Croatian (hr)': Locale('hr'),
|
||||||
'Czech (cs)': Locale('cs'),
|
'Czech (cs)': Locale('cs'),
|
||||||
'Danish (da)': Locale('da'),
|
'Danish (da)': Locale('da'),
|
||||||
'Dutch (nl)': Locale('nl'),
|
'Dutch (nl)': Locale('nl'),
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
import 'dart:typed_data';
|
|
||||||
import 'dart:ui';
|
|
||||||
|
|
||||||
abstract interface class IAssetMediaRepository {
|
|
||||||
Future<Uint8List?> getThumbnail(
|
|
||||||
String id, {
|
|
||||||
int quality = 80,
|
|
||||||
Size size = const Size.square(256),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
import 'dart:async';
|
|
||||||
|
|
||||||
import 'package:immich_mobile/domain/interfaces/db.interface.dart';
|
|
||||||
import 'package:immich_mobile/domain/models/device_asset.model.dart';
|
|
||||||
|
|
||||||
abstract interface class IDeviceAssetRepository implements IDatabaseRepository {
|
|
||||||
Future<bool> updateAll(List<DeviceAsset> assetHash);
|
|
||||||
|
|
||||||
Future<List<DeviceAsset>> getByIds(List<String> localIds);
|
|
||||||
|
|
||||||
Future<void> deleteIds(List<String> ids);
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import 'package:immich_mobile/domain/interfaces/db.interface.dart';
|
|
||||||
import 'package:immich_mobile/domain/models/exif.model.dart';
|
|
||||||
|
|
||||||
abstract interface class IExifInfoRepository implements IDatabaseRepository {
|
|
||||||
Future<ExifInfo?> get(int assetId);
|
|
||||||
|
|
||||||
Future<ExifInfo> update(ExifInfo exifInfo);
|
|
||||||
|
|
||||||
Future<List<ExifInfo>> updateAll(List<ExifInfo> exifInfos);
|
|
||||||
|
|
||||||
Future<void> delete(int assetId);
|
|
||||||
|
|
||||||
Future<void> deleteAll();
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
import 'package:immich_mobile/domain/interfaces/db.interface.dart';
|
|
||||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
|
||||||
import 'package:immich_mobile/domain/models/local_album.model.dart';
|
|
||||||
|
|
||||||
abstract interface class ILocalAlbumRepository implements IDatabaseRepository {
|
|
||||||
Future<List<LocalAlbum>> getAll({Set<SortLocalAlbumsBy> sortBy = const {}});
|
|
||||||
|
|
||||||
Future<List<LocalAsset>> getAssets(String albumId);
|
|
||||||
|
|
||||||
Future<List<String>> getAssetIds(String albumId);
|
|
||||||
|
|
||||||
Future<void> upsert(
|
|
||||||
LocalAlbum album, {
|
|
||||||
Iterable<LocalAsset> toUpsert = const [],
|
|
||||||
Iterable<String> toDelete = const [],
|
|
||||||
});
|
|
||||||
|
|
||||||
Future<void> updateAll(Iterable<LocalAlbum> albums);
|
|
||||||
|
|
||||||
Future<void> delete(String albumId);
|
|
||||||
|
|
||||||
Future<void> processDelta({
|
|
||||||
required List<LocalAsset> updates,
|
|
||||||
required List<String> deletes,
|
|
||||||
required Map<String, List<String>> assetAlbums,
|
|
||||||
});
|
|
||||||
|
|
||||||
Future<void> syncDeletes(String albumId, Iterable<String> assetIdsToKeep);
|
|
||||||
|
|
||||||
Future<List<LocalAsset>> getAssetsToHash(String albumId);
|
|
||||||
}
|
|
||||||
|
|
||||||
enum SortLocalAlbumsBy { id, backupSelection, isIosSharedAlbum }
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
import 'package:immich_mobile/domain/interfaces/db.interface.dart';
|
|
||||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
|
||||||
|
|
||||||
abstract interface class ILocalAssetRepository implements IDatabaseRepository {
|
|
||||||
Future<void> updateHashes(Iterable<LocalAsset> hashes);
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
import 'dart:async';
|
|
||||||
|
|
||||||
import 'package:immich_mobile/domain/interfaces/db.interface.dart';
|
|
||||||
import 'package:immich_mobile/domain/models/log.model.dart';
|
|
||||||
|
|
||||||
abstract interface class ILogRepository implements IDatabaseRepository {
|
|
||||||
Future<bool> insert(LogMessage log);
|
|
||||||
|
|
||||||
Future<bool> insertAll(Iterable<LogMessage> logs);
|
|
||||||
|
|
||||||
Future<List<LogMessage>> getAll();
|
|
||||||
|
|
||||||
Future<bool> deleteAll();
|
|
||||||
|
|
||||||
/// Truncates the logs to the most recent [limit]. Defaults to recent 250 logs
|
|
||||||
Future<void> truncate({int limit = 250});
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
|
||||||
|
|
||||||
abstract interface class IStorageRepository {
|
|
||||||
Future<File?> getFileForAsset(LocalAsset asset);
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
import 'package:immich_mobile/domain/interfaces/db.interface.dart';
|
|
||||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
|
||||||
|
|
||||||
abstract interface class IStoreRepository implements IDatabaseRepository {
|
|
||||||
Future<bool> insert<T>(StoreKey<T> key, T value);
|
|
||||||
|
|
||||||
Future<T?> tryGet<T>(StoreKey<T> key);
|
|
||||||
|
|
||||||
Future<List<StoreDto<Object>>> getAll();
|
|
||||||
|
|
||||||
Stream<T?> watch<T>(StoreKey<T> key);
|
|
||||||
|
|
||||||
Stream<StoreDto<Object>> watchAll();
|
|
||||||
|
|
||||||
Future<bool> update<T>(StoreKey<T> key, T value);
|
|
||||||
|
|
||||||
Future<void> delete<T>(StoreKey<T> key);
|
|
||||||
|
|
||||||
Future<void> deleteAll();
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
import 'package:http/http.dart' as http;
|
|
||||||
import 'package:immich_mobile/domain/models/sync_event.model.dart';
|
|
||||||
|
|
||||||
abstract interface class ISyncApiRepository {
|
|
||||||
Future<void> ack(List<String> data);
|
|
||||||
|
|
||||||
Future<void> streamChanges(
|
|
||||||
Function(List<SyncEvent>, Function() abort) onData, {
|
|
||||||
int batchSize,
|
|
||||||
http.Client? httpClient,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
import 'package:immich_mobile/domain/interfaces/db.interface.dart';
|
|
||||||
import 'package:openapi/api.dart';
|
|
||||||
|
|
||||||
abstract interface class ISyncStreamRepository implements IDatabaseRepository {
|
|
||||||
Future<void> updateUsersV1(Iterable<SyncUserV1> data);
|
|
||||||
Future<void> deleteUsersV1(Iterable<SyncUserDeleteV1> data);
|
|
||||||
|
|
||||||
Future<void> updatePartnerV1(Iterable<SyncPartnerV1> data);
|
|
||||||
Future<void> deletePartnerV1(Iterable<SyncPartnerDeleteV1> data);
|
|
||||||
|
|
||||||
Future<void> updateAssetsV1(Iterable<SyncAssetV1> data);
|
|
||||||
Future<void> deleteAssetsV1(Iterable<SyncAssetDeleteV1> data);
|
|
||||||
Future<void> updateAssetsExifV1(Iterable<SyncAssetExifV1> data);
|
|
||||||
|
|
||||||
Future<void> updatePartnerAssetsV1(Iterable<SyncAssetV1> data);
|
|
||||||
Future<void> deletePartnerAssetsV1(Iterable<SyncAssetDeleteV1> data);
|
|
||||||
Future<void> updatePartnerAssetsExifV1(Iterable<SyncAssetExifV1> data);
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
import 'package:immich_mobile/domain/interfaces/db.interface.dart';
|
|
||||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
|
||||||
import 'package:immich_mobile/domain/models/timeline.model.dart';
|
|
||||||
|
|
||||||
abstract interface class ITimelineRepository implements IDatabaseRepository {
|
|
||||||
Stream<List<Bucket>> watchMainBucket(
|
|
||||||
List<String> timelineUsers, {
|
|
||||||
GroupAssetsBy groupBy = GroupAssetsBy.day,
|
|
||||||
});
|
|
||||||
|
|
||||||
Future<List<BaseAsset>> getMainBucketAssets(
|
|
||||||
List<String> timelineUsers, {
|
|
||||||
required int offset,
|
|
||||||
required int count,
|
|
||||||
});
|
|
||||||
|
|
||||||
Stream<List<Bucket>> watchLocalBucket(
|
|
||||||
String albumId, {
|
|
||||||
GroupAssetsBy groupBy = GroupAssetsBy.day,
|
|
||||||
});
|
|
||||||
|
|
||||||
Future<List<BaseAsset>> getLocalBucketAssets(
|
|
||||||
String albumId, {
|
|
||||||
required int offset,
|
|
||||||
required int count,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
import 'package:immich_mobile/domain/interfaces/db.interface.dart';
|
|
||||||
import 'package:immich_mobile/domain/models/user.model.dart';
|
|
||||||
|
|
||||||
abstract interface class IUserRepository implements IDatabaseRepository {
|
|
||||||
Future<bool> insert(UserDto user);
|
|
||||||
|
|
||||||
Future<UserDto?> getByUserId(String id);
|
|
||||||
|
|
||||||
Future<List<UserDto?>> getByUserIds(List<String> ids);
|
|
||||||
|
|
||||||
Future<List<UserDto>> getAll({SortUserBy? sortBy});
|
|
||||||
|
|
||||||
Future<bool> updateAll(List<UserDto> users);
|
|
||||||
|
|
||||||
Future<UserDto> update(UserDto user);
|
|
||||||
|
|
||||||
Future<void> delete(List<String> ids);
|
|
||||||
|
|
||||||
Future<void> deleteAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
enum SortUserBy { id }
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
import 'dart:typed_data';
|
|
||||||
|
|
||||||
import 'package:immich_mobile/domain/models/user.model.dart';
|
|
||||||
|
|
||||||
abstract interface class IUserApiRepository {
|
|
||||||
Future<UserDto?> getMyUser();
|
|
||||||
|
|
||||||
Future<List<UserDto>> getAll();
|
|
||||||
|
|
||||||
/// Saves the [data] in the server and uses it as the current users profile image
|
|
||||||
Future<String> createProfileImage({
|
|
||||||
required String name,
|
|
||||||
required Uint8List data,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -1,13 +1,7 @@
|
|||||||
// ignore_for_file: public_member_api_docs, sort_constructors_first
|
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
abstract interface class IPersonApiRepository {
|
|
||||||
Future<List<Person>> getAll();
|
|
||||||
Future<Person> update(String id, {String? name});
|
|
||||||
}
|
|
||||||
|
|
||||||
class Person {
|
class Person {
|
||||||
Person({
|
const Person({
|
||||||
required this.id,
|
required this.id,
|
||||||
this.birthDate,
|
this.birthDate,
|
||||||
required this.isHidden,
|
required this.isHidden,
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:immich_mobile/constants/constants.dart';
|
import 'package:immich_mobile/constants/constants.dart';
|
||||||
import 'package:immich_mobile/domain/interfaces/local_album.interface.dart';
|
|
||||||
import 'package:immich_mobile/domain/interfaces/local_asset.interface.dart';
|
|
||||||
import 'package:immich_mobile/domain/interfaces/storage.interface.dart';
|
|
||||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/repositories/local_album.repository.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/repositories/local_asset.repository.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart';
|
||||||
import 'package:immich_mobile/platform/native_sync_api.g.dart';
|
import 'package:immich_mobile/platform/native_sync_api.g.dart';
|
||||||
import 'package:immich_mobile/presentation/pages/dev/dev_logger.dart';
|
import 'package:immich_mobile/presentation/pages/dev/dev_logger.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
@@ -12,16 +12,16 @@ import 'package:logging/logging.dart';
|
|||||||
class HashService {
|
class HashService {
|
||||||
final int batchSizeLimit;
|
final int batchSizeLimit;
|
||||||
final int batchFileLimit;
|
final int batchFileLimit;
|
||||||
final ILocalAlbumRepository _localAlbumRepository;
|
final DriftLocalAlbumRepository _localAlbumRepository;
|
||||||
final ILocalAssetRepository _localAssetRepository;
|
final DriftLocalAssetRepository _localAssetRepository;
|
||||||
final IStorageRepository _storageRepository;
|
final StorageRepository _storageRepository;
|
||||||
final NativeSyncApi _nativeSyncApi;
|
final NativeSyncApi _nativeSyncApi;
|
||||||
final _log = Logger('HashService');
|
final _log = Logger('HashService');
|
||||||
|
|
||||||
HashService({
|
HashService({
|
||||||
required ILocalAlbumRepository localAlbumRepository,
|
required DriftLocalAlbumRepository localAlbumRepository,
|
||||||
required ILocalAssetRepository localAssetRepository,
|
required DriftLocalAssetRepository localAssetRepository,
|
||||||
required IStorageRepository storageRepository,
|
required StorageRepository storageRepository,
|
||||||
required NativeSyncApi nativeSyncApi,
|
required NativeSyncApi nativeSyncApi,
|
||||||
this.batchSizeLimit = kBatchHashSizeLimit,
|
this.batchSizeLimit = kBatchHashSizeLimit,
|
||||||
this.batchFileLimit = kBatchHashFileLimit,
|
this.batchFileLimit = kBatchHashFileLimit,
|
||||||
|
|||||||
@@ -2,11 +2,11 @@ import 'dart:async';
|
|||||||
|
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:immich_mobile/domain/interfaces/local_album.interface.dart';
|
|
||||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||||
import 'package:immich_mobile/domain/models/local_album.model.dart';
|
import 'package:immich_mobile/domain/models/local_album.model.dart';
|
||||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||||
import 'package:immich_mobile/domain/services/store.service.dart';
|
import 'package:immich_mobile/domain/services/store.service.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/repositories/local_album.repository.dart';
|
||||||
import 'package:immich_mobile/platform/native_sync_api.g.dart';
|
import 'package:immich_mobile/platform/native_sync_api.g.dart';
|
||||||
import 'package:immich_mobile/presentation/pages/dev/dev_logger.dart';
|
import 'package:immich_mobile/presentation/pages/dev/dev_logger.dart';
|
||||||
import 'package:immich_mobile/utils/diff.dart';
|
import 'package:immich_mobile/utils/diff.dart';
|
||||||
@@ -14,14 +14,14 @@ import 'package:logging/logging.dart';
|
|||||||
import 'package:platform/platform.dart';
|
import 'package:platform/platform.dart';
|
||||||
|
|
||||||
class LocalSyncService {
|
class LocalSyncService {
|
||||||
final ILocalAlbumRepository _localAlbumRepository;
|
final DriftLocalAlbumRepository _localAlbumRepository;
|
||||||
final NativeSyncApi _nativeSyncApi;
|
final NativeSyncApi _nativeSyncApi;
|
||||||
final Platform _platform;
|
final Platform _platform;
|
||||||
final StoreService _storeService;
|
final StoreService _storeService;
|
||||||
final Logger _log = Logger("DeviceSyncService");
|
final Logger _log = Logger("DeviceSyncService");
|
||||||
|
|
||||||
LocalSyncService({
|
LocalSyncService({
|
||||||
required ILocalAlbumRepository localAlbumRepository,
|
required DriftLocalAlbumRepository localAlbumRepository,
|
||||||
required NativeSyncApi nativeSyncApi,
|
required NativeSyncApi nativeSyncApi,
|
||||||
required StoreService storeService,
|
required StoreService storeService,
|
||||||
Platform? platform,
|
Platform? platform,
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ import 'dart:async';
|
|||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:immich_mobile/constants/constants.dart';
|
import 'package:immich_mobile/constants/constants.dart';
|
||||||
import 'package:immich_mobile/domain/interfaces/log.interface.dart';
|
|
||||||
import 'package:immich_mobile/domain/interfaces/store.interface.dart';
|
|
||||||
import 'package:immich_mobile/domain/models/log.model.dart';
|
import 'package:immich_mobile/domain/models/log.model.dart';
|
||||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/repositories/log.repository.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/repositories/store.repository.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
|
|
||||||
/// Service responsible for handling application logging.
|
/// Service responsible for handling application logging.
|
||||||
@@ -14,8 +14,8 @@ import 'package:logging/logging.dart';
|
|||||||
/// writes them to a persistent [ILogRepository], and manages log levels
|
/// writes them to a persistent [ILogRepository], and manages log levels
|
||||||
/// via [IStoreRepository]
|
/// via [IStoreRepository]
|
||||||
class LogService {
|
class LogService {
|
||||||
final ILogRepository _logRepository;
|
final IsarLogRepository _logRepository;
|
||||||
final IStoreRepository _storeRepository;
|
final IsarStoreRepository _storeRepository;
|
||||||
|
|
||||||
final List<LogMessage> _msgBuffer = [];
|
final List<LogMessage> _msgBuffer = [];
|
||||||
|
|
||||||
@@ -37,8 +37,8 @@ class LogService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Future<LogService> init({
|
static Future<LogService> init({
|
||||||
required ILogRepository logRepository,
|
required IsarLogRepository logRepository,
|
||||||
required IStoreRepository storeRepository,
|
required IsarStoreRepository storeRepository,
|
||||||
bool shouldBuffer = true,
|
bool shouldBuffer = true,
|
||||||
}) async {
|
}) async {
|
||||||
_instance ??= await create(
|
_instance ??= await create(
|
||||||
@@ -50,8 +50,8 @@ class LogService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Future<LogService> create({
|
static Future<LogService> create({
|
||||||
required ILogRepository logRepository,
|
required IsarLogRepository logRepository,
|
||||||
required IStoreRepository storeRepository,
|
required IsarStoreRepository storeRepository,
|
||||||
bool shouldBuffer = true,
|
bool shouldBuffer = true,
|
||||||
}) async {
|
}) async {
|
||||||
final instance = LogService._(logRepository, storeRepository, shouldBuffer);
|
final instance = LogService._(logRepository, storeRepository, shouldBuffer);
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:immich_mobile/domain/interfaces/store.interface.dart';
|
|
||||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/repositories/store.repository.dart';
|
||||||
|
|
||||||
/// Provides access to a persistent key-value store with an in-memory cache.
|
/// Provides access to a persistent key-value store with an in-memory cache.
|
||||||
/// Listens for repository changes to keep the cache updated.
|
/// Listens for repository changes to keep the cache updated.
|
||||||
class StoreService {
|
class StoreService {
|
||||||
final IStoreRepository _storeRepository;
|
final IsarStoreRepository _storeRepository;
|
||||||
|
|
||||||
/// In-memory cache. Keys are [StoreKey.id]
|
/// In-memory cache. Keys are [StoreKey.id]
|
||||||
final Map<int, Object?> _cache = {};
|
final Map<int, Object?> _cache = {};
|
||||||
late final StreamSubscription<StoreDto> _storeUpdateSubscription;
|
late final StreamSubscription<StoreDto> _storeUpdateSubscription;
|
||||||
|
|
||||||
StoreService._({required IStoreRepository storeRepository})
|
StoreService._({required IsarStoreRepository storeRepository})
|
||||||
: _storeRepository = storeRepository;
|
: _storeRepository = storeRepository;
|
||||||
|
|
||||||
// TODO: Temporary typedef to make minimal changes. Remove this and make the presentation layer access store through a provider
|
// TODO: Temporary typedef to make minimal changes. Remove this and make the presentation layer access store through a provider
|
||||||
@@ -26,14 +26,14 @@ class StoreService {
|
|||||||
|
|
||||||
// TODO: Replace the implementation with the one from create after removing the typedef
|
// TODO: Replace the implementation with the one from create after removing the typedef
|
||||||
static Future<StoreService> init({
|
static Future<StoreService> init({
|
||||||
required IStoreRepository storeRepository,
|
required IsarStoreRepository storeRepository,
|
||||||
}) async {
|
}) async {
|
||||||
_instance ??= await create(storeRepository: storeRepository);
|
_instance ??= await create(storeRepository: storeRepository);
|
||||||
return _instance!;
|
return _instance!;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<StoreService> create({
|
static Future<StoreService> create({
|
||||||
required IStoreRepository storeRepository,
|
required IsarStoreRepository storeRepository,
|
||||||
}) async {
|
}) async {
|
||||||
final instance = StoreService._(storeRepository: storeRepository);
|
final instance = StoreService._(storeRepository: storeRepository);
|
||||||
await instance._populateCache();
|
await instance._populateCache();
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:immich_mobile/domain/interfaces/sync_api.interface.dart';
|
|
||||||
import 'package:immich_mobile/domain/interfaces/sync_stream.interface.dart';
|
|
||||||
import 'package:immich_mobile/domain/models/sync_event.model.dart';
|
import 'package:immich_mobile/domain/models/sync_event.model.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/repositories/sync_api.repository.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/repositories/sync_stream.repository.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:openapi/api.dart';
|
import 'package:openapi/api.dart';
|
||||||
|
|
||||||
class SyncStreamService {
|
class SyncStreamService {
|
||||||
final Logger _logger = Logger('SyncStreamService');
|
final Logger _logger = Logger('SyncStreamService');
|
||||||
|
|
||||||
final ISyncApiRepository _syncApiRepository;
|
final SyncApiRepository _syncApiRepository;
|
||||||
final ISyncStreamRepository _syncStreamRepository;
|
final SyncStreamRepository _syncStreamRepository;
|
||||||
final bool Function()? _cancelChecker;
|
final bool Function()? _cancelChecker;
|
||||||
|
|
||||||
SyncStreamService({
|
SyncStreamService({
|
||||||
required ISyncApiRepository syncApiRepository,
|
required SyncApiRepository syncApiRepository,
|
||||||
required ISyncStreamRepository syncStreamRepository,
|
required SyncStreamRepository syncStreamRepository,
|
||||||
bool Function()? cancelChecker,
|
bool Function()? cancelChecker,
|
||||||
}) : _syncApiRepository = syncApiRepository,
|
}) : _syncApiRepository = syncApiRepository,
|
||||||
_syncStreamRepository = syncStreamRepository,
|
_syncStreamRepository = syncStreamRepository,
|
||||||
|
|||||||
@@ -3,11 +3,11 @@ import 'dart:math' as math;
|
|||||||
|
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:immich_mobile/constants/constants.dart';
|
import 'package:immich_mobile/constants/constants.dart';
|
||||||
import 'package:immich_mobile/domain/interfaces/timeline.interface.dart';
|
|
||||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||||
import 'package:immich_mobile/domain/models/setting.model.dart';
|
import 'package:immich_mobile/domain/models/setting.model.dart';
|
||||||
import 'package:immich_mobile/domain/models/timeline.model.dart';
|
import 'package:immich_mobile/domain/models/timeline.model.dart';
|
||||||
import 'package:immich_mobile/domain/services/setting.service.dart';
|
import 'package:immich_mobile/domain/services/setting.service.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/repositories/timeline.repository.dart';
|
||||||
import 'package:immich_mobile/utils/async_mutex.dart';
|
import 'package:immich_mobile/utils/async_mutex.dart';
|
||||||
|
|
||||||
typedef TimelineAssetSource = Future<List<BaseAsset>> Function(
|
typedef TimelineAssetSource = Future<List<BaseAsset>> Function(
|
||||||
@@ -18,11 +18,11 @@ typedef TimelineAssetSource = Future<List<BaseAsset>> Function(
|
|||||||
typedef TimelineBucketSource = Stream<List<Bucket>> Function();
|
typedef TimelineBucketSource = Stream<List<Bucket>> Function();
|
||||||
|
|
||||||
class TimelineFactory {
|
class TimelineFactory {
|
||||||
final ITimelineRepository _timelineRepository;
|
final DriftTimelineRepository _timelineRepository;
|
||||||
final SettingsService _settingsService;
|
final SettingsService _settingsService;
|
||||||
|
|
||||||
const TimelineFactory({
|
const TimelineFactory({
|
||||||
required ITimelineRepository timelineRepository,
|
required DriftTimelineRepository timelineRepository,
|
||||||
required SettingsService settingsService,
|
required SettingsService settingsService,
|
||||||
}) : _timelineRepository = timelineRepository,
|
}) : _timelineRepository = timelineRepository,
|
||||||
_settingsService = settingsService;
|
_settingsService = settingsService;
|
||||||
|
|||||||
@@ -1,24 +1,24 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:immich_mobile/domain/interfaces/user.interface.dart';
|
|
||||||
import 'package:immich_mobile/domain/interfaces/user_api.interface.dart';
|
|
||||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||||
import 'package:immich_mobile/domain/models/user.model.dart';
|
import 'package:immich_mobile/domain/models/user.model.dart';
|
||||||
import 'package:immich_mobile/domain/services/store.service.dart';
|
import 'package:immich_mobile/domain/services/store.service.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/repositories/user.repository.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/repositories/user_api.repository.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
|
|
||||||
class UserService {
|
class UserService {
|
||||||
final Logger _log = Logger("UserService");
|
final Logger _log = Logger("UserService");
|
||||||
final IUserRepository _userRepository;
|
final IsarUserRepository _isarUserRepository;
|
||||||
final IUserApiRepository _userApiRepository;
|
final UserApiRepository _userApiRepository;
|
||||||
final StoreService _storeService;
|
final StoreService _storeService;
|
||||||
|
|
||||||
UserService({
|
UserService({
|
||||||
required IUserRepository userRepository,
|
required IsarUserRepository isarUserRepository,
|
||||||
required IUserApiRepository userApiRepository,
|
required UserApiRepository userApiRepository,
|
||||||
required StoreService storeService,
|
required StoreService storeService,
|
||||||
}) : _userRepository = userRepository,
|
}) : _isarUserRepository = isarUserRepository,
|
||||||
_userApiRepository = userApiRepository,
|
_userApiRepository = userApiRepository,
|
||||||
_storeService = storeService;
|
_storeService = storeService;
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@ class UserService {
|
|||||||
final user = await _userApiRepository.getMyUser();
|
final user = await _userApiRepository.getMyUser();
|
||||||
if (user == null) return null;
|
if (user == null) return null;
|
||||||
await _storeService.put(StoreKey.currentUser, user);
|
await _storeService.put(StoreKey.currentUser, user);
|
||||||
await _userRepository.update(user);
|
await _isarUserRepository.update(user);
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ class UserService {
|
|||||||
);
|
);
|
||||||
final updatedUser = getMyUser().copyWith(profileImagePath: path);
|
final updatedUser = getMyUser().copyWith(profileImagePath: path);
|
||||||
await _storeService.put(StoreKey.currentUser, updatedUser);
|
await _storeService.put(StoreKey.currentUser, updatedUser);
|
||||||
await _userRepository.update(updatedUser);
|
await _isarUserRepository.update(updatedUser);
|
||||||
return path;
|
return path;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
_log.warning("Failed to upload profile image", e);
|
_log.warning("Failed to upload profile image", e);
|
||||||
@@ -59,10 +59,10 @@ class UserService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<List<UserDto>> getAll() async {
|
Future<List<UserDto>> getAll() async {
|
||||||
return await _userRepository.getAll();
|
return await _isarUserRepository.getAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> deleteAll() {
|
Future<void> deleteAll() {
|
||||||
return _userRepository.deleteAll();
|
return _isarUserRepository.deleteAll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -554,18 +554,12 @@ class Asset {
|
|||||||
}""";
|
}""";
|
||||||
}
|
}
|
||||||
|
|
||||||
static getVisibility(AssetVisibility visibility) {
|
static getVisibility(AssetVisibility visibility) => switch (visibility) {
|
||||||
switch (visibility) {
|
AssetVisibility.archive => AssetVisibilityEnum.archive,
|
||||||
case AssetVisibility.timeline:
|
AssetVisibility.hidden => AssetVisibilityEnum.hidden,
|
||||||
return AssetVisibilityEnum.timeline;
|
AssetVisibility.locked => AssetVisibilityEnum.locked,
|
||||||
case AssetVisibility.archive:
|
AssetVisibility.timeline || _ => AssetVisibilityEnum.timeline,
|
||||||
return AssetVisibilityEnum.archive;
|
};
|
||||||
case AssetVisibility.hidden:
|
|
||||||
return AssetVisibilityEnum.hidden;
|
|
||||||
case AssetVisibility.locked:
|
|
||||||
return AssetVisibilityEnum.locked;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum AssetType {
|
enum AssetType {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ class SSLClientCertStoreVal {
|
|||||||
final Uint8List data;
|
final Uint8List data;
|
||||||
final String? password;
|
final String? password;
|
||||||
|
|
||||||
SSLClientCertStoreVal(this.data, this.password);
|
const SSLClientCertStoreVal(this.data, this.password);
|
||||||
|
|
||||||
void save() {
|
void save() {
|
||||||
final b64Str = base64Encode(data);
|
final b64Str = base64Encode(data);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ class ApiRepository {
|
|||||||
|
|
||||||
Future<T> checkNull<T>(Future<T?> future) async {
|
Future<T> checkNull<T>(Future<T?> future) async {
|
||||||
final response = await future;
|
final response = await future;
|
||||||
if (response == null) throw NoResponseDtoError();
|
if (response == null) throw const NoResponseDtoError();
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:immich_mobile/domain/interfaces/asset_media.interface.dart';
|
|
||||||
import 'package:photo_manager/photo_manager.dart';
|
import 'package:photo_manager/photo_manager.dart';
|
||||||
|
|
||||||
class AssetMediaRepository implements IAssetMediaRepository {
|
class AssetMediaRepository {
|
||||||
const AssetMediaRepository();
|
const AssetMediaRepository();
|
||||||
|
|
||||||
@override
|
|
||||||
Future<Uint8List?> getThumbnail(
|
Future<Uint8List?> getThumbnail(
|
||||||
String id, {
|
String id, {
|
||||||
int quality = 80,
|
int quality = 80,
|
||||||
|
|||||||
@@ -1,23 +1,19 @@
|
|||||||
import 'package:immich_mobile/domain/interfaces/device_asset.interface.dart';
|
|
||||||
import 'package:immich_mobile/domain/models/device_asset.model.dart';
|
import 'package:immich_mobile/domain/models/device_asset.model.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/device_asset.entity.dart';
|
import 'package:immich_mobile/infrastructure/entities/device_asset.entity.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||||
import 'package:isar/isar.dart';
|
import 'package:isar/isar.dart';
|
||||||
|
|
||||||
class IsarDeviceAssetRepository extends IsarDatabaseRepository
|
class IsarDeviceAssetRepository extends IsarDatabaseRepository {
|
||||||
implements IDeviceAssetRepository {
|
|
||||||
final Isar _db;
|
final Isar _db;
|
||||||
|
|
||||||
const IsarDeviceAssetRepository(this._db) : super(_db);
|
const IsarDeviceAssetRepository(this._db) : super(_db);
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> deleteIds(List<String> ids) {
|
Future<void> deleteIds(List<String> ids) {
|
||||||
return transaction(() async {
|
return transaction(() async {
|
||||||
await _db.deviceAssetEntitys.deleteAllByAssetId(ids.toList());
|
await _db.deviceAssetEntitys.deleteAllByAssetId(ids.toList());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<List<DeviceAsset>> getByIds(List<String> localIds) {
|
Future<List<DeviceAsset>> getByIds(List<String> localIds) {
|
||||||
return _db.deviceAssetEntitys
|
return _db.deviceAssetEntitys
|
||||||
.where()
|
.where()
|
||||||
@@ -26,7 +22,6 @@ class IsarDeviceAssetRepository extends IsarDatabaseRepository
|
|||||||
.then((value) => value.map((e) => e.toModel()).toList());
|
.then((value) => value.map((e) => e.toModel()).toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<bool> updateAll(List<DeviceAsset> assetHash) {
|
Future<bool> updateAll(List<DeviceAsset> assetHash) {
|
||||||
return transaction(() async {
|
return transaction(() async {
|
||||||
await _db.deviceAssetEntitys
|
await _db.deviceAssetEntitys
|
||||||
|
|||||||
@@ -1,36 +1,30 @@
|
|||||||
import 'package:immich_mobile/domain/interfaces/exif.interface.dart';
|
|
||||||
import 'package:immich_mobile/domain/models/exif.model.dart';
|
import 'package:immich_mobile/domain/models/exif.model.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/exif.entity.dart'
|
import 'package:immich_mobile/infrastructure/entities/exif.entity.dart'
|
||||||
as entity;
|
as entity;
|
||||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||||
import 'package:isar/isar.dart';
|
import 'package:isar/isar.dart';
|
||||||
|
|
||||||
class IsarExifRepository extends IsarDatabaseRepository
|
class IsarExifRepository extends IsarDatabaseRepository {
|
||||||
implements IExifInfoRepository {
|
|
||||||
final Isar _db;
|
final Isar _db;
|
||||||
|
|
||||||
const IsarExifRepository(this._db) : super(_db);
|
const IsarExifRepository(this._db) : super(_db);
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> delete(int assetId) async {
|
Future<void> delete(int assetId) async {
|
||||||
await transaction(() async {
|
await transaction(() async {
|
||||||
await _db.exifInfos.delete(assetId);
|
await _db.exifInfos.delete(assetId);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> deleteAll() async {
|
Future<void> deleteAll() async {
|
||||||
await transaction(() async {
|
await transaction(() async {
|
||||||
await _db.exifInfos.clear();
|
await _db.exifInfos.clear();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<ExifInfo?> get(int assetId) async {
|
Future<ExifInfo?> get(int assetId) async {
|
||||||
return (await _db.exifInfos.get(assetId))?.toDto();
|
return (await _db.exifInfos.get(assetId))?.toDto();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<ExifInfo> update(ExifInfo exifInfo) {
|
Future<ExifInfo> update(ExifInfo exifInfo) {
|
||||||
return transaction(() async {
|
return transaction(() async {
|
||||||
await _db.exifInfos.put(entity.ExifInfo.fromDto(exifInfo));
|
await _db.exifInfos.put(entity.ExifInfo.fromDto(exifInfo));
|
||||||
@@ -38,7 +32,6 @@ class IsarExifRepository extends IsarDatabaseRepository
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<List<ExifInfo>> updateAll(List<ExifInfo> exifInfos) {
|
Future<List<ExifInfo>> updateAll(List<ExifInfo> exifInfos) {
|
||||||
return transaction(() async {
|
return transaction(() async {
|
||||||
await _db.exifInfos.putAll(
|
await _db.exifInfos.putAll(
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:immich_mobile/domain/interfaces/local_album.interface.dart';
|
|
||||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||||
import 'package:immich_mobile/domain/models/local_album.model.dart';
|
import 'package:immich_mobile/domain/models/local_album.model.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart';
|
import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart';
|
||||||
@@ -8,15 +7,15 @@ import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.d
|
|||||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||||
import 'package:platform/platform.dart';
|
import 'package:platform/platform.dart';
|
||||||
|
|
||||||
class DriftLocalAlbumRepository extends DriftDatabaseRepository
|
enum SortLocalAlbumsBy { id, backupSelection, isIosSharedAlbum }
|
||||||
implements ILocalAlbumRepository {
|
|
||||||
|
class DriftLocalAlbumRepository extends DriftDatabaseRepository {
|
||||||
final Drift _db;
|
final Drift _db;
|
||||||
final Platform _platform;
|
final Platform _platform;
|
||||||
const DriftLocalAlbumRepository(this._db, {Platform? platform})
|
const DriftLocalAlbumRepository(this._db, {Platform? platform})
|
||||||
: _platform = platform ?? const LocalPlatform(),
|
: _platform = platform ?? const LocalPlatform(),
|
||||||
super(_db);
|
super(_db);
|
||||||
|
|
||||||
@override
|
|
||||||
Future<List<LocalAlbum>> getAll({Set<SortLocalAlbumsBy> sortBy = const {}}) {
|
Future<List<LocalAlbum>> getAll({Set<SortLocalAlbumsBy> sortBy = const {}}) {
|
||||||
final assetCount = _db.localAlbumAssetEntity.assetId.count();
|
final assetCount = _db.localAlbumAssetEntity.assetId.count();
|
||||||
|
|
||||||
@@ -56,7 +55,6 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository
|
|||||||
.get();
|
.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> delete(String albumId) => transaction(() async {
|
Future<void> delete(String albumId) => transaction(() async {
|
||||||
// Remove all assets that are only in this particular album
|
// Remove all assets that are only in this particular album
|
||||||
// We cannot remove all assets in the album because they might be in other albums in iOS
|
// We cannot remove all assets in the album because they might be in other albums in iOS
|
||||||
@@ -72,7 +70,6 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository
|
|||||||
.delete();
|
.delete();
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> syncDeletes(
|
Future<void> syncDeletes(
|
||||||
String albumId,
|
String albumId,
|
||||||
Iterable<String> assetIdsToKeep,
|
Iterable<String> assetIdsToKeep,
|
||||||
@@ -101,7 +98,6 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository
|
|||||||
await deleteSmt.go();
|
await deleteSmt.go();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> upsert(
|
Future<void> upsert(
|
||||||
LocalAlbum localAlbum, {
|
LocalAlbum localAlbum, {
|
||||||
Iterable<LocalAsset> toUpsert = const [],
|
Iterable<LocalAsset> toUpsert = const [],
|
||||||
@@ -134,7 +130,6 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> updateAll(Iterable<LocalAlbum> albums) {
|
Future<void> updateAll(Iterable<LocalAlbum> albums) {
|
||||||
return _db.transaction(() async {
|
return _db.transaction(() async {
|
||||||
await _db.localAlbumEntity
|
await _db.localAlbumEntity
|
||||||
@@ -185,7 +180,6 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<List<LocalAsset>> getAssets(String albumId) {
|
Future<List<LocalAsset>> getAssets(String albumId) {
|
||||||
final query = _db.localAlbumAssetEntity.select().join(
|
final query = _db.localAlbumAssetEntity.select().join(
|
||||||
[
|
[
|
||||||
@@ -202,7 +196,6 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository
|
|||||||
.get();
|
.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<List<String>> getAssetIds(String albumId) {
|
Future<List<String>> getAssetIds(String albumId) {
|
||||||
final query = _db.localAlbumAssetEntity.selectOnly()
|
final query = _db.localAlbumAssetEntity.selectOnly()
|
||||||
..addColumns([_db.localAlbumAssetEntity.assetId])
|
..addColumns([_db.localAlbumAssetEntity.assetId])
|
||||||
@@ -212,7 +205,6 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository
|
|||||||
.get();
|
.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> processDelta({
|
Future<void> processDelta({
|
||||||
required List<LocalAsset> updates,
|
required List<LocalAsset> updates,
|
||||||
required List<String> deletes,
|
required List<String> deletes,
|
||||||
@@ -253,7 +245,6 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<List<LocalAsset>> getAssetsToHash(String albumId) {
|
Future<List<LocalAsset>> getAssetsToHash(String albumId) {
|
||||||
final query = _db.localAlbumAssetEntity.select().join(
|
final query = _db.localAlbumAssetEntity.select().join(
|
||||||
[
|
[
|
||||||
|
|||||||
@@ -1,15 +1,12 @@
|
|||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:immich_mobile/domain/interfaces/local_asset.interface.dart';
|
|
||||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart';
|
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||||
|
|
||||||
class DriftLocalAssetRepository extends DriftDatabaseRepository
|
class DriftLocalAssetRepository extends DriftDatabaseRepository {
|
||||||
implements ILocalAssetRepository {
|
|
||||||
final Drift _db;
|
final Drift _db;
|
||||||
const DriftLocalAssetRepository(this._db) : super(_db);
|
const DriftLocalAssetRepository(this._db) : super(_db);
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> updateHashes(Iterable<LocalAsset> hashes) {
|
Future<void> updateHashes(Iterable<LocalAsset> hashes) {
|
||||||
if (hashes.isEmpty) {
|
if (hashes.isEmpty) {
|
||||||
return Future.value();
|
return Future.value();
|
||||||
|
|||||||
@@ -1,28 +1,23 @@
|
|||||||
import 'package:immich_mobile/domain/interfaces/log.interface.dart';
|
|
||||||
import 'package:immich_mobile/domain/models/log.model.dart';
|
import 'package:immich_mobile/domain/models/log.model.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/log.entity.dart';
|
import 'package:immich_mobile/infrastructure/entities/log.entity.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||||
import 'package:isar/isar.dart';
|
import 'package:isar/isar.dart';
|
||||||
|
|
||||||
class IsarLogRepository extends IsarDatabaseRepository
|
class IsarLogRepository extends IsarDatabaseRepository {
|
||||||
implements ILogRepository {
|
|
||||||
final Isar _db;
|
final Isar _db;
|
||||||
const IsarLogRepository(super.db) : _db = db;
|
const IsarLogRepository(super.db) : _db = db;
|
||||||
|
|
||||||
@override
|
|
||||||
Future<bool> deleteAll() async {
|
Future<bool> deleteAll() async {
|
||||||
await transaction(() async => await _db.loggerMessages.clear());
|
await transaction(() async => await _db.loggerMessages.clear());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<List<LogMessage>> getAll() async {
|
Future<List<LogMessage>> getAll() async {
|
||||||
final logs =
|
final logs =
|
||||||
await _db.loggerMessages.where().sortByCreatedAtDesc().findAll();
|
await _db.loggerMessages.where().sortByCreatedAtDesc().findAll();
|
||||||
return logs.map((l) => l.toDto()).toList();
|
return logs.map((l) => l.toDto()).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<bool> insert(LogMessage log) async {
|
Future<bool> insert(LogMessage log) async {
|
||||||
final logEntity = LoggerMessage.fromDto(log);
|
final logEntity = LoggerMessage.fromDto(log);
|
||||||
await transaction(() async {
|
await transaction(() async {
|
||||||
@@ -31,7 +26,6 @@ class IsarLogRepository extends IsarDatabaseRepository
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<bool> insertAll(Iterable<LogMessage> logs) async {
|
Future<bool> insertAll(Iterable<LogMessage> logs) async {
|
||||||
await transaction(() async {
|
await transaction(() async {
|
||||||
final logEntities =
|
final logEntities =
|
||||||
@@ -41,7 +35,6 @@ class IsarLogRepository extends IsarDatabaseRepository
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> truncate({int limit = 250}) async {
|
Future<void> truncate({int limit = 250}) async {
|
||||||
await transaction(() async {
|
await transaction(() async {
|
||||||
final count = await _db.loggerMessages.count();
|
final count = await _db.loggerMessages.count();
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:immich_mobile/domain/interfaces/storage.interface.dart';
|
|
||||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:photo_manager/photo_manager.dart';
|
import 'package:photo_manager/photo_manager.dart';
|
||||||
|
|
||||||
class StorageRepository implements IStorageRepository {
|
class StorageRepository {
|
||||||
final _log = Logger('StorageRepository');
|
final _log = Logger('StorageRepository');
|
||||||
|
|
||||||
@override
|
|
||||||
Future<File?> getFileForAsset(LocalAsset asset) async {
|
Future<File?> getFileForAsset(LocalAsset asset) async {
|
||||||
File? file;
|
File? file;
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import 'package:immich_mobile/domain/interfaces/store.interface.dart';
|
|
||||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||||
import 'package:immich_mobile/domain/models/user.model.dart';
|
import 'package:immich_mobile/domain/models/user.model.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/store.entity.dart';
|
import 'package:immich_mobile/infrastructure/entities/store.entity.dart';
|
||||||
@@ -6,14 +5,12 @@ import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
|||||||
import 'package:immich_mobile/infrastructure/repositories/user.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/user.repository.dart';
|
||||||
import 'package:isar/isar.dart';
|
import 'package:isar/isar.dart';
|
||||||
|
|
||||||
class IsarStoreRepository extends IsarDatabaseRepository
|
class IsarStoreRepository extends IsarDatabaseRepository {
|
||||||
implements IStoreRepository {
|
|
||||||
final Isar _db;
|
final Isar _db;
|
||||||
final validStoreKeys = StoreKey.values.map((e) => e.id).toSet();
|
final validStoreKeys = StoreKey.values.map((e) => e.id).toSet();
|
||||||
|
|
||||||
IsarStoreRepository(super.db) : _db = db;
|
IsarStoreRepository(super.db) : _db = db;
|
||||||
|
|
||||||
@override
|
|
||||||
Future<bool> deleteAll() async {
|
Future<bool> deleteAll() async {
|
||||||
return await transaction(() async {
|
return await transaction(() async {
|
||||||
await _db.storeValues.clear();
|
await _db.storeValues.clear();
|
||||||
@@ -21,7 +18,6 @@ class IsarStoreRepository extends IsarDatabaseRepository
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Stream<StoreDto<Object>> watchAll() {
|
Stream<StoreDto<Object>> watchAll() {
|
||||||
return _db.storeValues
|
return _db.storeValues
|
||||||
.filter()
|
.filter()
|
||||||
@@ -34,12 +30,10 @@ class IsarStoreRepository extends IsarDatabaseRepository
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> delete<T>(StoreKey<T> key) async {
|
Future<void> delete<T>(StoreKey<T> key) async {
|
||||||
return await transaction(() async => await _db.storeValues.delete(key.id));
|
return await transaction(() async => await _db.storeValues.delete(key.id));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<bool> insert<T>(StoreKey<T> key, T value) async {
|
Future<bool> insert<T>(StoreKey<T> key, T value) async {
|
||||||
return await transaction(() async {
|
return await transaction(() async {
|
||||||
await _db.storeValues.put(await _fromValue(key, value));
|
await _db.storeValues.put(await _fromValue(key, value));
|
||||||
@@ -47,7 +41,6 @@ class IsarStoreRepository extends IsarDatabaseRepository
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<T?> tryGet<T>(StoreKey<T> key) async {
|
Future<T?> tryGet<T>(StoreKey<T> key) async {
|
||||||
final entity = (await _db.storeValues.get(key.id));
|
final entity = (await _db.storeValues.get(key.id));
|
||||||
if (entity == null) {
|
if (entity == null) {
|
||||||
@@ -56,7 +49,6 @@ class IsarStoreRepository extends IsarDatabaseRepository
|
|||||||
return await _toValue(key, entity);
|
return await _toValue(key, entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<bool> update<T>(StoreKey<T> key, T value) async {
|
Future<bool> update<T>(StoreKey<T> key, T value) async {
|
||||||
return await transaction(() async {
|
return await transaction(() async {
|
||||||
await _db.storeValues.put(await _fromValue(key, value));
|
await _db.storeValues.put(await _fromValue(key, value));
|
||||||
@@ -64,7 +56,6 @@ class IsarStoreRepository extends IsarDatabaseRepository
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Stream<T?> watch<T>(StoreKey<T> key) async* {
|
Stream<T?> watch<T>(StoreKey<T> key) async* {
|
||||||
yield* _db.storeValues
|
yield* _db.storeValues
|
||||||
.watchObject(key.id, fireImmediately: true)
|
.watchObject(key.id, fireImmediately: true)
|
||||||
@@ -109,7 +100,6 @@ class IsarStoreRepository extends IsarDatabaseRepository
|
|||||||
return StoreValue(key.id, intValue: intValue, strValue: strValue);
|
return StoreValue(key.id, intValue: intValue, strValue: strValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<List<StoreDto<Object>>> getAll() async {
|
Future<List<StoreDto<Object>>> getAll() async {
|
||||||
final entities = await _db.storeValues
|
final entities = await _db.storeValues
|
||||||
.filter()
|
.filter()
|
||||||
|
|||||||
@@ -3,24 +3,21 @@ import 'dart:convert';
|
|||||||
|
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
import 'package:immich_mobile/constants/constants.dart';
|
import 'package:immich_mobile/constants/constants.dart';
|
||||||
import 'package:immich_mobile/domain/interfaces/sync_api.interface.dart';
|
|
||||||
import 'package:immich_mobile/domain/models/sync_event.model.dart';
|
import 'package:immich_mobile/domain/models/sync_event.model.dart';
|
||||||
import 'package:immich_mobile/presentation/pages/dev/dev_logger.dart';
|
import 'package:immich_mobile/presentation/pages/dev/dev_logger.dart';
|
||||||
import 'package:immich_mobile/services/api.service.dart';
|
import 'package:immich_mobile/services/api.service.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:openapi/api.dart';
|
import 'package:openapi/api.dart';
|
||||||
|
|
||||||
class SyncApiRepository implements ISyncApiRepository {
|
class SyncApiRepository {
|
||||||
final Logger _logger = Logger('SyncApiRepository');
|
final Logger _logger = Logger('SyncApiRepository');
|
||||||
final ApiService _api;
|
final ApiService _api;
|
||||||
SyncApiRepository(this._api);
|
SyncApiRepository(this._api);
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> ack(List<String> data) {
|
Future<void> ack(List<String> data) {
|
||||||
return _api.syncApi.sendSyncAck(SyncAckSetDto(acks: data));
|
return _api.syncApi.sendSyncAck(SyncAckSetDto(acks: data));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> streamChanges(
|
Future<void> streamChanges(
|
||||||
Function(List<SyncEvent>, Function() abort) onData, {
|
Function(List<SyncEvent>, Function() abort) onData, {
|
||||||
int batchSize = kSyncEventBatchSize,
|
int batchSize = kSyncEventBatchSize,
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:immich_mobile/domain/interfaces/sync_stream.interface.dart';
|
|
||||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart';
|
import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart';
|
import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart';
|
||||||
@@ -10,14 +9,12 @@ import 'package:logging/logging.dart';
|
|||||||
import 'package:openapi/api.dart' as api show AssetVisibility;
|
import 'package:openapi/api.dart' as api show AssetVisibility;
|
||||||
import 'package:openapi/api.dart' hide AssetVisibility;
|
import 'package:openapi/api.dart' hide AssetVisibility;
|
||||||
|
|
||||||
class DriftSyncStreamRepository extends DriftDatabaseRepository
|
class SyncStreamRepository extends DriftDatabaseRepository {
|
||||||
implements ISyncStreamRepository {
|
|
||||||
final Logger _logger = Logger('DriftSyncStreamRepository');
|
final Logger _logger = Logger('DriftSyncStreamRepository');
|
||||||
final Drift _db;
|
final Drift _db;
|
||||||
|
|
||||||
DriftSyncStreamRepository(super.db) : _db = db;
|
SyncStreamRepository(super.db) : _db = db;
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> deleteUsersV1(Iterable<SyncUserDeleteV1> data) async {
|
Future<void> deleteUsersV1(Iterable<SyncUserDeleteV1> data) async {
|
||||||
try {
|
try {
|
||||||
await _db.batch((batch) {
|
await _db.batch((batch) {
|
||||||
@@ -34,7 +31,6 @@ class DriftSyncStreamRepository extends DriftDatabaseRepository
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> updateUsersV1(Iterable<SyncUserV1> data) async {
|
Future<void> updateUsersV1(Iterable<SyncUserV1> data) async {
|
||||||
try {
|
try {
|
||||||
await _db.batch((batch) {
|
await _db.batch((batch) {
|
||||||
@@ -57,7 +53,6 @@ class DriftSyncStreamRepository extends DriftDatabaseRepository
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> deletePartnerV1(Iterable<SyncPartnerDeleteV1> data) async {
|
Future<void> deletePartnerV1(Iterable<SyncPartnerDeleteV1> data) async {
|
||||||
try {
|
try {
|
||||||
await _db.batch((batch) {
|
await _db.batch((batch) {
|
||||||
@@ -77,7 +72,6 @@ class DriftSyncStreamRepository extends DriftDatabaseRepository
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> updatePartnerV1(Iterable<SyncPartnerV1> data) async {
|
Future<void> updatePartnerV1(Iterable<SyncPartnerV1> data) async {
|
||||||
try {
|
try {
|
||||||
await _db.batch((batch) {
|
await _db.batch((batch) {
|
||||||
@@ -101,7 +95,6 @@ class DriftSyncStreamRepository extends DriftDatabaseRepository
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> deleteAssetsV1(Iterable<SyncAssetDeleteV1> data) async {
|
Future<void> deleteAssetsV1(Iterable<SyncAssetDeleteV1> data) async {
|
||||||
try {
|
try {
|
||||||
await _deleteAssetsV1(data);
|
await _deleteAssetsV1(data);
|
||||||
@@ -111,7 +104,6 @@ class DriftSyncStreamRepository extends DriftDatabaseRepository
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> updateAssetsV1(Iterable<SyncAssetV1> data) async {
|
Future<void> updateAssetsV1(Iterable<SyncAssetV1> data) async {
|
||||||
try {
|
try {
|
||||||
await _updateAssetsV1(data);
|
await _updateAssetsV1(data);
|
||||||
@@ -121,7 +113,6 @@ class DriftSyncStreamRepository extends DriftDatabaseRepository
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> deletePartnerAssetsV1(Iterable<SyncAssetDeleteV1> data) async {
|
Future<void> deletePartnerAssetsV1(Iterable<SyncAssetDeleteV1> data) async {
|
||||||
try {
|
try {
|
||||||
await _deleteAssetsV1(data);
|
await _deleteAssetsV1(data);
|
||||||
@@ -131,7 +122,6 @@ class DriftSyncStreamRepository extends DriftDatabaseRepository
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> updatePartnerAssetsV1(Iterable<SyncAssetV1> data) async {
|
Future<void> updatePartnerAssetsV1(Iterable<SyncAssetV1> data) async {
|
||||||
try {
|
try {
|
||||||
await _updateAssetsV1(data);
|
await _updateAssetsV1(data);
|
||||||
@@ -141,7 +131,6 @@ class DriftSyncStreamRepository extends DriftDatabaseRepository
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> updateAssetsExifV1(Iterable<SyncAssetExifV1> data) async {
|
Future<void> updateAssetsExifV1(Iterable<SyncAssetExifV1> data) async {
|
||||||
try {
|
try {
|
||||||
await _updateAssetExifV1(data);
|
await _updateAssetExifV1(data);
|
||||||
@@ -151,7 +140,6 @@ class DriftSyncStreamRepository extends DriftDatabaseRepository
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> updatePartnerAssetsExifV1(Iterable<SyncAssetExifV1> data) async {
|
Future<void> updatePartnerAssetsExifV1(Iterable<SyncAssetExifV1> data) async {
|
||||||
try {
|
try {
|
||||||
await _updateAssetExifV1(data);
|
await _updateAssetExifV1(data);
|
||||||
|
|||||||
@@ -3,15 +3,13 @@ import 'dart:async';
|
|||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:immich_mobile/constants/constants.dart';
|
import 'package:immich_mobile/constants/constants.dart';
|
||||||
import 'package:immich_mobile/domain/interfaces/timeline.interface.dart';
|
|
||||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||||
import 'package:immich_mobile/domain/models/timeline.model.dart';
|
import 'package:immich_mobile/domain/models/timeline.model.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart';
|
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||||
import 'package:stream_transform/stream_transform.dart';
|
import 'package:stream_transform/stream_transform.dart';
|
||||||
|
|
||||||
class DriftTimelineRepository extends DriftDatabaseRepository
|
class DriftTimelineRepository extends DriftDatabaseRepository {
|
||||||
implements ITimelineRepository {
|
|
||||||
final Drift _db;
|
final Drift _db;
|
||||||
|
|
||||||
const DriftTimelineRepository(super._db) : _db = _db;
|
const DriftTimelineRepository(super._db) : _db = _db;
|
||||||
@@ -28,7 +26,6 @@ class DriftTimelineRepository extends DriftDatabaseRepository
|
|||||||
return buckets;
|
return buckets;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Stream<List<Bucket>> watchMainBucket(
|
Stream<List<Bucket>> watchMainBucket(
|
||||||
List<String> userIds, {
|
List<String> userIds, {
|
||||||
GroupAssetsBy groupBy = GroupAssetsBy.day,
|
GroupAssetsBy groupBy = GroupAssetsBy.day,
|
||||||
@@ -49,7 +46,6 @@ class DriftTimelineRepository extends DriftDatabaseRepository
|
|||||||
.throttle(const Duration(seconds: 3), trailing: true);
|
.throttle(const Duration(seconds: 3), trailing: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<List<BaseAsset>> getMainBucketAssets(
|
Future<List<BaseAsset>> getMainBucketAssets(
|
||||||
List<String> userIds, {
|
List<String> userIds, {
|
||||||
required int offset,
|
required int offset,
|
||||||
@@ -90,7 +86,6 @@ class DriftTimelineRepository extends DriftDatabaseRepository
|
|||||||
.get();
|
.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Stream<List<Bucket>> watchLocalBucket(
|
Stream<List<Bucket>> watchLocalBucket(
|
||||||
String albumId, {
|
String albumId, {
|
||||||
GroupAssetsBy groupBy = GroupAssetsBy.day,
|
GroupAssetsBy groupBy = GroupAssetsBy.day,
|
||||||
@@ -124,7 +119,6 @@ class DriftTimelineRepository extends DriftDatabaseRepository
|
|||||||
}).watch();
|
}).watch();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<List<BaseAsset>> getLocalBucketAssets(
|
Future<List<BaseAsset>> getLocalBucketAssets(
|
||||||
String albumId, {
|
String albumId, {
|
||||||
required int offset,
|
required int offset,
|
||||||
|
|||||||
@@ -1,30 +1,26 @@
|
|||||||
import 'package:immich_mobile/domain/interfaces/user.interface.dart';
|
import 'package:immich_mobile/constants/enums.dart';
|
||||||
import 'package:immich_mobile/domain/models/user.model.dart';
|
import 'package:immich_mobile/domain/models/user.model.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/user.entity.dart'
|
import 'package:immich_mobile/infrastructure/entities/user.entity.dart'
|
||||||
as entity;
|
as entity;
|
||||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||||
import 'package:isar/isar.dart';
|
import 'package:isar/isar.dart';
|
||||||
|
|
||||||
class IsarUserRepository extends IsarDatabaseRepository
|
class IsarUserRepository extends IsarDatabaseRepository {
|
||||||
implements IUserRepository {
|
|
||||||
final Isar _db;
|
final Isar _db;
|
||||||
const IsarUserRepository(super.db) : _db = db;
|
const IsarUserRepository(super.db) : _db = db;
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> delete(List<String> ids) async {
|
Future<void> delete(List<String> ids) async {
|
||||||
await transaction(() async {
|
await transaction(() async {
|
||||||
await _db.users.deleteAllById(ids);
|
await _db.users.deleteAllById(ids);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> deleteAll() async {
|
Future<void> deleteAll() async {
|
||||||
await transaction(() async {
|
await transaction(() async {
|
||||||
await _db.users.clear();
|
await _db.users.clear();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<List<UserDto>> getAll({SortUserBy? sortBy}) async {
|
Future<List<UserDto>> getAll({SortUserBy? sortBy}) async {
|
||||||
return (await _db.users
|
return (await _db.users
|
||||||
.where()
|
.where()
|
||||||
@@ -39,17 +35,14 @@ class IsarUserRepository extends IsarDatabaseRepository
|
|||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<UserDto?> getByUserId(String id) async {
|
Future<UserDto?> getByUserId(String id) async {
|
||||||
return (await _db.users.getById(id))?.toDto();
|
return (await _db.users.getById(id))?.toDto();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<List<UserDto?>> getByUserIds(List<String> ids) async {
|
Future<List<UserDto?>> getByUserIds(List<String> ids) async {
|
||||||
return (await _db.users.getAllById(ids)).map((u) => u?.toDto()).toList();
|
return (await _db.users.getAllById(ids)).map((u) => u?.toDto()).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<bool> insert(UserDto user) async {
|
Future<bool> insert(UserDto user) async {
|
||||||
await transaction(() async {
|
await transaction(() async {
|
||||||
await _db.users.put(entity.User.fromDto(user));
|
await _db.users.put(entity.User.fromDto(user));
|
||||||
@@ -57,7 +50,6 @@ class IsarUserRepository extends IsarDatabaseRepository
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<UserDto> update(UserDto user) async {
|
Future<UserDto> update(UserDto user) async {
|
||||||
await transaction(() async {
|
await transaction(() async {
|
||||||
await _db.users.put(entity.User.fromDto(user));
|
await _db.users.put(entity.User.fromDto(user));
|
||||||
@@ -65,7 +57,6 @@ class IsarUserRepository extends IsarDatabaseRepository
|
|||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<bool> updateAll(List<UserDto> users) async {
|
Future<bool> updateAll(List<UserDto> users) async {
|
||||||
await transaction(() async {
|
await transaction(() async {
|
||||||
await _db.users.putAll(users.map(entity.User.fromDto).toList());
|
await _db.users.putAll(users.map(entity.User.fromDto).toList());
|
||||||
|
|||||||
@@ -1,17 +1,15 @@
|
|||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:http/http.dart';
|
import 'package:http/http.dart';
|
||||||
import 'package:immich_mobile/domain/interfaces/user_api.interface.dart';
|
|
||||||
import 'package:immich_mobile/domain/models/user.model.dart';
|
import 'package:immich_mobile/domain/models/user.model.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/api.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/api.repository.dart';
|
||||||
import 'package:immich_mobile/infrastructure/utils/user.converter.dart';
|
import 'package:immich_mobile/infrastructure/utils/user.converter.dart';
|
||||||
import 'package:openapi/api.dart';
|
import 'package:openapi/api.dart';
|
||||||
|
|
||||||
class UserApiRepository extends ApiRepository implements IUserApiRepository {
|
class UserApiRepository extends ApiRepository {
|
||||||
final UsersApi _api;
|
final UsersApi _api;
|
||||||
const UserApiRepository(this._api);
|
const UserApiRepository(this._api);
|
||||||
|
|
||||||
@override
|
|
||||||
Future<UserDto?> getMyUser() async {
|
Future<UserDto?> getMyUser() async {
|
||||||
final (adminDto, preferenceDto) =
|
final (adminDto, preferenceDto) =
|
||||||
await (_api.getMyUser(), _api.getMyPreferences()).wait;
|
await (_api.getMyUser(), _api.getMyPreferences()).wait;
|
||||||
@@ -20,7 +18,6 @@ class UserApiRepository extends ApiRepository implements IUserApiRepository {
|
|||||||
return UserConverter.fromAdminDto(adminDto, preferenceDto);
|
return UserConverter.fromAdminDto(adminDto, preferenceDto);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<String> createProfileImage({
|
Future<String> createProfileImage({
|
||||||
required String name,
|
required String name,
|
||||||
required Uint8List data,
|
required Uint8List data,
|
||||||
@@ -33,7 +30,6 @@ class UserApiRepository extends ApiRepository implements IUserApiRepository {
|
|||||||
return res.profileImagePath;
|
return res.profileImagePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<List<UserDto>> getAll() async {
|
Future<List<UserDto>> getAll() async {
|
||||||
final dto = await checkNull(_api.searchUsers());
|
final dto = await checkNull(_api.searchUsers());
|
||||||
return dto.map(UserConverter.fromSimpleUserDto).toList();
|
return dto.map(UserConverter.fromSimpleUserDto).toList();
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
import 'package:immich_mobile/models/activities/activity.model.dart';
|
|
||||||
|
|
||||||
abstract interface class IActivityApiRepository {
|
|
||||||
Future<List<Activity>> getAll(
|
|
||||||
String albumId, {
|
|
||||||
String? assetId,
|
|
||||||
});
|
|
||||||
Future<Activity> create(
|
|
||||||
String albumId,
|
|
||||||
ActivityType type, {
|
|
||||||
String? assetId,
|
|
||||||
String? comment,
|
|
||||||
});
|
|
||||||
Future<void> delete(String id);
|
|
||||||
Future<ActivityStats> getStats(String albumId, {String? assetId});
|
|
||||||
}
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
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/interfaces/database.interface.dart';
|
|
||||||
import 'package:immich_mobile/models/albums/album_search.model.dart';
|
|
||||||
|
|
||||||
abstract interface class IAlbumRepository implements IDatabaseRepository {
|
|
||||||
Future<Album> create(Album album);
|
|
||||||
|
|
||||||
Future<Album?> get(int id);
|
|
||||||
|
|
||||||
Future<Album?> getByName(
|
|
||||||
String name, {
|
|
||||||
bool? shared,
|
|
||||||
bool? remote,
|
|
||||||
bool? owner,
|
|
||||||
});
|
|
||||||
|
|
||||||
Future<List<Album>> getAll({
|
|
||||||
bool? shared,
|
|
||||||
bool? remote,
|
|
||||||
int? ownerId,
|
|
||||||
AlbumSort? sortBy,
|
|
||||||
});
|
|
||||||
|
|
||||||
Future<Album> update(Album album);
|
|
||||||
|
|
||||||
Future<void> delete(int albumId);
|
|
||||||
|
|
||||||
Future<void> deleteAllLocal();
|
|
||||||
|
|
||||||
Future<int> count({bool? local});
|
|
||||||
|
|
||||||
Future<void> addUsers(Album album, List<UserDto> users);
|
|
||||||
|
|
||||||
Future<void> removeUsers(Album album, List<UserDto> users);
|
|
||||||
|
|
||||||
Future<void> addAssets(Album album, List<Asset> assets);
|
|
||||||
|
|
||||||
Future<void> removeAssets(Album album, List<Asset> assets);
|
|
||||||
|
|
||||||
Future<Album> recalculateMetadata(Album album);
|
|
||||||
|
|
||||||
Future<List<Album>> search(String searchTerm, QuickFilterMode filterMode);
|
|
||||||
|
|
||||||
Stream<List<Album>> watchRemoteAlbums();
|
|
||||||
|
|
||||||
Stream<List<Album>> watchLocalAlbums();
|
|
||||||
|
|
||||||
Stream<Album?> watchAlbum(int id);
|
|
||||||
|
|
||||||
Future<void> clearTable();
|
|
||||||
}
|
|
||||||
|
|
||||||
enum AlbumSort { remoteId, localId }
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
import 'package:immich_mobile/constants/enums.dart';
|
|
||||||
import 'package:immich_mobile/entities/album.entity.dart';
|
|
||||||
|
|
||||||
abstract interface class IAlbumApiRepository {
|
|
||||||
Future<Album> get(String id);
|
|
||||||
|
|
||||||
Future<List<Album>> getAll({bool? shared});
|
|
||||||
|
|
||||||
Future<Album> create(
|
|
||||||
String name, {
|
|
||||||
required Iterable<String> assetIds,
|
|
||||||
Iterable<String> sharedUserIds = const [],
|
|
||||||
});
|
|
||||||
|
|
||||||
Future<Album> update(
|
|
||||||
String albumId, {
|
|
||||||
String? name,
|
|
||||||
String? thumbnailAssetId,
|
|
||||||
String? description,
|
|
||||||
bool? activityEnabled,
|
|
||||||
SortOrder? sortOrder,
|
|
||||||
});
|
|
||||||
|
|
||||||
Future<void> delete(String albumId);
|
|
||||||
|
|
||||||
Future<({List<String> added, List<String> duplicates})> addAssets(
|
|
||||||
String albumId,
|
|
||||||
Iterable<String> assetIds,
|
|
||||||
);
|
|
||||||
|
|
||||||
Future<({List<String> removed, List<String> failed})> removeAssets(
|
|
||||||
String albumId,
|
|
||||||
Iterable<String> assetIds,
|
|
||||||
);
|
|
||||||
|
|
||||||
Future<Album> addUsers(
|
|
||||||
String albumId,
|
|
||||||
Iterable<String> userIds,
|
|
||||||
);
|
|
||||||
|
|
||||||
Future<void> removeUser(String albumId, {required String userId});
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
import 'package:immich_mobile/entities/album.entity.dart';
|
|
||||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
|
||||||
|
|
||||||
abstract interface class IAlbumMediaRepository {
|
|
||||||
Future<List<Album>> getAll();
|
|
||||||
|
|
||||||
Future<List<String>> getAssetIds(String albumId);
|
|
||||||
|
|
||||||
Future<int> getAssetCount(String albumId);
|
|
||||||
|
|
||||||
Future<List<Asset>> getAssets(
|
|
||||||
String albumId, {
|
|
||||||
int start = 0,
|
|
||||||
int end = 0x7fffffffffffffff,
|
|
||||||
DateTime? modifiedFrom,
|
|
||||||
DateTime? modifiedUntil,
|
|
||||||
bool orderByModificationDate = false,
|
|
||||||
});
|
|
||||||
|
|
||||||
Future<Album> get(String id);
|
|
||||||
}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
import 'package:immich_mobile/entities/album.entity.dart';
|
|
||||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
|
||||||
import 'package:immich_mobile/interfaces/database.interface.dart';
|
|
||||||
|
|
||||||
abstract interface class IAssetRepository implements IDatabaseRepository {
|
|
||||||
Future<Asset?> getByRemoteId(String id);
|
|
||||||
|
|
||||||
Future<Asset?> getByOwnerIdChecksum(int ownerId, String checksum);
|
|
||||||
|
|
||||||
Future<List<Asset>> getAllByRemoteId(
|
|
||||||
Iterable<String> ids, {
|
|
||||||
AssetState? state,
|
|
||||||
});
|
|
||||||
|
|
||||||
Future<List<Asset?>> getAllByOwnerIdChecksum(
|
|
||||||
List<int> ids,
|
|
||||||
List<String> checksums,
|
|
||||||
);
|
|
||||||
|
|
||||||
Future<List<Asset>> getAll({
|
|
||||||
required String ownerId,
|
|
||||||
AssetState? state,
|
|
||||||
AssetSort? sortBy,
|
|
||||||
int? limit,
|
|
||||||
});
|
|
||||||
|
|
||||||
Future<List<Asset>> getAllLocal();
|
|
||||||
|
|
||||||
Future<List<Asset>> getByAlbum(
|
|
||||||
Album album, {
|
|
||||||
Iterable<String> notOwnedBy = const [],
|
|
||||||
String? ownerId,
|
|
||||||
AssetState? state,
|
|
||||||
AssetSort? sortBy,
|
|
||||||
});
|
|
||||||
|
|
||||||
Future<Asset> update(Asset asset);
|
|
||||||
|
|
||||||
Future<List<Asset>> updateAll(List<Asset> assets);
|
|
||||||
|
|
||||||
Future<void> deleteAllByRemoteId(List<String> ids, {AssetState? state});
|
|
||||||
|
|
||||||
Future<void> deleteByIds(List<int> ids);
|
|
||||||
|
|
||||||
Future<List<Asset>> getMatches({
|
|
||||||
required List<Asset> assets,
|
|
||||||
required String ownerId,
|
|
||||||
AssetState? state,
|
|
||||||
int limit = 100,
|
|
||||||
});
|
|
||||||
|
|
||||||
Future<void> upsertDuplicatedAssets(Iterable<String> duplicatedAssets);
|
|
||||||
|
|
||||||
Future<List<String>> getAllDuplicatedAssetIds();
|
|
||||||
|
|
||||||
Future<List<Asset>> getStackAssets(String stackId);
|
|
||||||
|
|
||||||
Future<void> clearTable();
|
|
||||||
|
|
||||||
Stream<Asset?> watchAsset(int id, {bool fireImmediately = false});
|
|
||||||
|
|
||||||
Future<List<Asset>> getTrashAssets(String userId);
|
|
||||||
|
|
||||||
Future<List<Asset>> getRecentlyTakenAssets(String userId);
|
|
||||||
Future<List<Asset>> getMotionAssets(String userId);
|
|
||||||
}
|
|
||||||
|
|
||||||
enum AssetSort { checksum, ownerIdChecksum }
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
import 'package:immich_mobile/constants/enums.dart';
|
|
||||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
|
||||||
|
|
||||||
abstract interface class IAssetApiRepository {
|
|
||||||
// Future<Asset> get(String id);
|
|
||||||
|
|
||||||
// Future<List<Asset>> getAll();
|
|
||||||
|
|
||||||
// Future<Asset> create(Asset asset);
|
|
||||||
|
|
||||||
Future<Asset> update(
|
|
||||||
String id, {
|
|
||||||
String? description,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Future<void> delete(String id);
|
|
||||||
|
|
||||||
Future<List<Asset>> search({List<String> personIds = const []});
|
|
||||||
|
|
||||||
Future<void> updateVisibility(
|
|
||||||
List<String> list,
|
|
||||||
AssetVisibilityEnum visibility,
|
|
||||||
);
|
|
||||||
|
|
||||||
Future<String?> getAssetMIMEType(String id);
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
|
||||||
|
|
||||||
abstract interface class IAssetMediaRepository {
|
|
||||||
Future<List<String>> deleteAll(List<String> ids);
|
|
||||||
|
|
||||||
Future<Asset?> get(String id);
|
|
||||||
|
|
||||||
/// Obtaining the correct original filename of the asset
|
|
||||||
Future<String?> getOriginalFilename(String id);
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
import 'package:immich_mobile/interfaces/database.interface.dart';
|
|
||||||
import 'package:immich_mobile/models/auth/auxilary_endpoint.model.dart';
|
|
||||||
|
|
||||||
abstract interface class IAuthRepository implements IDatabaseRepository {
|
|
||||||
Future<void> clearLocalData();
|
|
||||||
String getAccessToken();
|
|
||||||
bool getEndpointSwitchingFeature();
|
|
||||||
String? getPreferredWifiName();
|
|
||||||
String? getLocalEndpoint();
|
|
||||||
List<AuxilaryEndpoint> getExternalEndpointList();
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import 'package:immich_mobile/models/auth/login_response.model.dart';
|
|
||||||
|
|
||||||
abstract interface class IAuthApiRepository {
|
|
||||||
Future<LoginResponse> login(String email, String password);
|
|
||||||
|
|
||||||
Future<void> logout();
|
|
||||||
|
|
||||||
Future<void> changePassword(String newPassword);
|
|
||||||
|
|
||||||
Future<bool> unlockPinCode(String pinCode);
|
|
||||||
Future<void> lockPinCode();
|
|
||||||
|
|
||||||
Future<void> setupPinCode(String pinCode);
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
import 'package:immich_mobile/entities/backup_album.entity.dart';
|
|
||||||
import 'package:immich_mobile/interfaces/database.interface.dart';
|
|
||||||
|
|
||||||
abstract interface class IBackupAlbumRepository implements IDatabaseRepository {
|
|
||||||
Future<List<BackupAlbum>> getAll({BackupAlbumSort? sort});
|
|
||||||
|
|
||||||
Future<List<String>> getIdsBySelection(BackupSelection backup);
|
|
||||||
|
|
||||||
Future<List<BackupAlbum>> getAllBySelection(BackupSelection backup);
|
|
||||||
|
|
||||||
Future<void> updateAll(List<BackupAlbum> backupAlbums);
|
|
||||||
|
|
||||||
Future<void> deleteAll(List<int> ids);
|
|
||||||
}
|
|
||||||
|
|
||||||
enum BackupAlbumSort { id }
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
import 'package:immich_mobile/models/auth/biometric_status.model.dart';
|
|
||||||
|
|
||||||
abstract interface class IBiometricRepository {
|
|
||||||
Future<BiometricStatus> getStatus();
|
|
||||||
Future<bool> authenticate(String? message);
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
|
||||||
import 'package:immich_mobile/models/cast/cast_manager_state.dart';
|
|
||||||
|
|
||||||
abstract interface class ICastDestinationService {
|
|
||||||
Future<bool> initialize();
|
|
||||||
CastDestinationType getType();
|
|
||||||
|
|
||||||
void Function(bool)? onConnectionState;
|
|
||||||
|
|
||||||
void Function(Duration)? onCurrentTime;
|
|
||||||
void Function(Duration)? onDuration;
|
|
||||||
|
|
||||||
void Function(String)? onReceiverName;
|
|
||||||
void Function(CastState)? onCastState;
|
|
||||||
|
|
||||||
Future<void> connect(dynamic device);
|
|
||||||
|
|
||||||
void loadMedia(Asset asset, bool reload);
|
|
||||||
|
|
||||||
void play();
|
|
||||||
void pause();
|
|
||||||
void seekTo(Duration position);
|
|
||||||
void stop();
|
|
||||||
Future<void> disconnect();
|
|
||||||
|
|
||||||
Future<List<(String, CastDestinationType, dynamic)>> getDevices();
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
import 'package:background_downloader/background_downloader.dart';
|
|
||||||
|
|
||||||
abstract interface class IDownloadRepository {
|
|
||||||
void Function(TaskStatusUpdate)? onImageDownloadStatus;
|
|
||||||
void Function(TaskStatusUpdate)? onVideoDownloadStatus;
|
|
||||||
void Function(TaskStatusUpdate)? onLivePhotoDownloadStatus;
|
|
||||||
void Function(TaskProgressUpdate)? onTaskProgress;
|
|
||||||
|
|
||||||
Future<List<TaskRecord>> getLiveVideoTasks();
|
|
||||||
Future<List<bool>> downloadAll(List<DownloadTask> tasks);
|
|
||||||
|
|
||||||
Future<bool> cancel(String id);
|
|
||||||
Future<void> deleteAllTrackingRecords();
|
|
||||||
Future<void> deleteRecordsWithIds(List<String> id);
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
import 'package:immich_mobile/entities/etag.entity.dart';
|
|
||||||
import 'package:immich_mobile/interfaces/database.interface.dart';
|
|
||||||
|
|
||||||
abstract interface class IETagRepository implements IDatabaseRepository {
|
|
||||||
Future<ETag?> get(String id);
|
|
||||||
|
|
||||||
Future<ETag?> getById(String id);
|
|
||||||
|
|
||||||
Future<List<String>> getAllIds();
|
|
||||||
|
|
||||||
Future<void> upsertAll(List<ETag> etags);
|
|
||||||
|
|
||||||
Future<void> deleteByIds(List<String> ids);
|
|
||||||
|
|
||||||
Future<void> clearTable();
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
import 'dart:io';
|
|
||||||
import 'dart:typed_data';
|
|
||||||
|
|
||||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
|
||||||
|
|
||||||
abstract interface class IFileMediaRepository {
|
|
||||||
Future<Asset?> saveImage(
|
|
||||||
Uint8List data, {
|
|
||||||
required String title,
|
|
||||||
String? relativePath,
|
|
||||||
});
|
|
||||||
|
|
||||||
Future<Asset?> saveImageWithFile(
|
|
||||||
String filePath, {
|
|
||||||
String? title,
|
|
||||||
String? relativePath,
|
|
||||||
});
|
|
||||||
|
|
||||||
Future<Asset?> saveVideo(
|
|
||||||
File file, {
|
|
||||||
required String title,
|
|
||||||
String? relativePath,
|
|
||||||
});
|
|
||||||
|
|
||||||
Future<Asset?> saveLivePhoto({
|
|
||||||
required File image,
|
|
||||||
required File video,
|
|
||||||
required String title,
|
|
||||||
});
|
|
||||||
|
|
||||||
Future<void> clearFileCache();
|
|
||||||
|
|
||||||
Future<void> enableBackgroundAccess();
|
|
||||||
|
|
||||||
Future<void> requestExtendedPermissions();
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user