Compare commits
125 Commits
asset-user
...
feat/share
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8b0684ee9c | ||
|
|
7bf142dc43 | ||
|
|
d8cda6ee40 | ||
|
|
a31bc94460 | ||
|
|
516709ffe1 | ||
|
|
425cf62482 | ||
|
|
58242b3b4a | ||
|
|
9d4aee36e2 | ||
|
|
70d08a2b2a | ||
|
|
f1b98d5f45 | ||
|
|
749eff03d5 | ||
|
|
5f257b9a84 | ||
|
|
0cae20033c | ||
|
|
115ee0d6cc | ||
|
|
bfdd6eac01 | ||
|
|
9eab770e79 | ||
|
|
efd8d8b884 | ||
|
|
25e1c8cc7f | ||
|
|
7c26663013 | ||
|
|
2c88ce8559 | ||
|
|
50b072803d | ||
|
|
1689cecaf7 | ||
|
|
5cd1018db3 | ||
|
|
31e6270a28 | ||
|
|
b3fbd0809b | ||
|
|
129a4a82e0 | ||
|
|
924d11a913 | ||
|
|
425c87bce4 | ||
|
|
25fcda6eeb | ||
|
|
f386b4d377 | ||
|
|
c524fcf084 | ||
|
|
194c567a45 | ||
|
|
411f96ef49 | ||
|
|
4f912de018 | ||
|
|
47203d2760 | ||
|
|
8ab87a8803 | ||
|
|
5b4f894211 | ||
|
|
b1f05fc18b | ||
|
|
dbbefde98d | ||
|
|
5407a28533 | ||
|
|
f5edc87e4d | ||
|
|
bf16b61d43 | ||
|
|
8c882b54cd | ||
|
|
2d7c333c8c | ||
|
|
7c821dd205 | ||
|
|
703361da1a | ||
|
|
fa5aeaf539 | ||
|
|
5f3a42a132 | ||
|
|
9d85272c2b | ||
|
|
d2575d8f00 | ||
|
|
f0a4c945bd | ||
|
|
a3766b879e | ||
|
|
1a190c33a0 | ||
|
|
17a63e37b2 | ||
|
|
bf1f8da884 | ||
|
|
2271984dbd | ||
|
|
b40963ec52 | ||
|
|
735f8d661e | ||
|
|
8794c84e9d | ||
|
|
cef19eed97 | ||
|
|
90c607c1a6 | ||
|
|
52b650093d | ||
|
|
fe4c49c8e3 | ||
|
|
4cad23aaa3 | ||
|
|
feba590de7 | ||
|
|
64f0333306 | ||
|
|
758bcd1e97 | ||
|
|
fb21950ad8 | ||
|
|
758449e9f0 | ||
|
|
d7d4d22fe0 | ||
|
|
03948a69e2 | ||
|
|
61b8eb85b5 | ||
|
|
c5360e78c5 | ||
|
|
23014c263b | ||
|
|
2e5007adef | ||
|
|
c4531fc4d3 | ||
|
|
252d3f5f2c | ||
|
|
ef6c2bf547 | ||
|
|
6aad9fae8e | ||
|
|
45f7401513 | ||
|
|
3c7edba388 | ||
|
|
76a70703a5 | ||
|
|
f78066d4b9 | ||
|
|
48d421e28c | ||
|
|
1492b55c07 | ||
|
|
1d6a4e9318 | ||
|
|
fe42e7410b | ||
|
|
58bf58b393 | ||
|
|
99de52479e | ||
|
|
97574d7296 | ||
|
|
5015210f37 | ||
|
|
0bb1219b5f | ||
|
|
b730aa60ed | ||
|
|
7ec3610753 | ||
|
|
69e88ef985 | ||
|
|
9358b4dc7e | ||
|
|
06f077bac2 | ||
|
|
47f6181d42 | ||
|
|
aac029d92b | ||
|
|
ef245ea2d2 | ||
|
|
e8d05e78ad | ||
|
|
52c9fbea5f | ||
|
|
882163f545 | ||
|
|
96a6cc20b7 | ||
|
|
4efacfbb91 | ||
|
|
a808a840c8 | ||
|
|
3f18acdb1a | ||
|
|
2b41b5efe1 | ||
|
|
9ac95d6845 | ||
|
|
221e197633 | ||
|
|
1b141d5ca9 | ||
|
|
098bab7c9b | ||
|
|
4fccc09fc1 | ||
|
|
c016b65ef2 | ||
|
|
844eed8707 | ||
|
|
6e31ac4c75 | ||
|
|
b287c0cbe8 | ||
|
|
1fcc75fb44 | ||
|
|
ca79e25a6e | ||
|
|
4fd8c1b3c1 | ||
|
|
f3ba994186 | ||
|
|
b4a4abbf51 | ||
|
|
a0aea021a1 | ||
|
|
9033a99587 | ||
|
|
cc0cbd705e |
1
.github/PULL_REQUEST_TEMPLATE/config.yml
vendored
1
.github/PULL_REQUEST_TEMPLATE/config.yml
vendored
@@ -1,2 +1 @@
|
|||||||
blank_issues_enabled: false
|
|
||||||
blank_pull_request_template_enabled: false
|
blank_pull_request_template_enabled: false
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
## Description
|
|
||||||
<!--- Describe your changes in detail -->
|
|
||||||
<!--- Why is this change required? What problem does it solve? -->
|
|
||||||
<!--- If it fixes an open issue, please link to the issue here. -->
|
|
||||||
|
|
||||||
Fixes # (issue)
|
|
||||||
|
|
||||||
|
|
||||||
## How Has This Been Tested?
|
|
||||||
|
|
||||||
<!-- Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration -->
|
|
||||||
|
|
||||||
- [ ] Test A
|
|
||||||
- [ ] Test B
|
|
||||||
|
|
||||||
## Screenshots (if appropriate):
|
|
||||||
|
|
||||||
|
|
||||||
## Checklist:
|
|
||||||
|
|
||||||
- [ ] I have performed a self-review of my own code
|
|
||||||
- [ ] I have made corresponding changes to the documentation if applicable
|
|
||||||
36
.github/pull_request_template.md
vendored
Normal file
36
.github/pull_request_template.md
vendored
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
## Description
|
||||||
|
|
||||||
|
<!--- Describe your changes in detail -->
|
||||||
|
<!--- Why is this change required? What problem does it solve? -->
|
||||||
|
<!--- If it fixes an open issue, please link to the issue here. -->
|
||||||
|
|
||||||
|
Fixes # (issue)
|
||||||
|
|
||||||
|
## How Has This Been Tested?
|
||||||
|
|
||||||
|
<!-- Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration -->
|
||||||
|
|
||||||
|
- [ ] Test A
|
||||||
|
- [ ] Test B
|
||||||
|
|
||||||
|
<details><summary><h2>Screenshots (if appropriate)</h2></summary>
|
||||||
|
|
||||||
|
<!-- Images go below this line. -->
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<!-- API endpoint changes (if relevant)
|
||||||
|
## API Changes
|
||||||
|
The `/api/something` endpoint is now `/api/something-else`
|
||||||
|
-->
|
||||||
|
|
||||||
|
## Checklist:
|
||||||
|
|
||||||
|
- [ ] I have performed a self-review of my own code
|
||||||
|
- [ ] I have made corresponding changes to the documentation if applicable
|
||||||
|
- [ ] I have no unrelated changes in the PR.
|
||||||
|
- [ ] I have confirmed that any new dependencies are strictly necessary.
|
||||||
|
- [ ] I have written tests for new code (if applicable)
|
||||||
|
- [ ] I have followed naming conventions/patterns in the surrounding code
|
||||||
|
- [ ] All code in `src/services` uses repositories implementations for database calls, filesystem operations, etc.
|
||||||
|
- [ ] All code in `src/repositories/` is pretty basic/simple and does not have any immich specific logic (that belongs in `src/services`)
|
||||||
4
.github/workflows/build-mobile.yml
vendored
4
.github/workflows/build-mobile.yml
vendored
@@ -29,9 +29,11 @@ jobs:
|
|||||||
filters: |
|
filters: |
|
||||||
mobile:
|
mobile:
|
||||||
- 'mobile/**'
|
- 'mobile/**'
|
||||||
|
workflow:
|
||||||
|
- '.github/workflows/build-mobile.yml'
|
||||||
- name: Check if we should force jobs to run
|
- name: Check if we should force jobs to run
|
||||||
id: should_force
|
id: should_force
|
||||||
run: echo "should_force=${{ github.event_name == 'workflow_call' || github.event_name == 'workflow_dispatch' }}" >> "$GITHUB_OUTPUT"
|
run: echo "should_force=${{ steps.found_paths.outputs.workflow == 'true' || github.event_name == 'workflow_call' || github.event_name == 'workflow_dispatch' }}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
build-sign-android:
|
build-sign-android:
|
||||||
name: Build and sign Android
|
name: Build and sign Android
|
||||||
|
|||||||
6
.github/workflows/cli.yml
vendored
6
.github/workflows/cli.yml
vendored
@@ -56,10 +56,10 @@ jobs:
|
|||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v3.3.0
|
uses: docker/setup-qemu-action@v3.4.0
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3.8.0
|
uses: docker/setup-buildx-action@v3.9.0
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
@@ -88,7 +88,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@v6.12.0
|
uses: docker/build-push-action@v6.13.0
|
||||||
with:
|
with:
|
||||||
file: cli/Dockerfile
|
file: cli/Dockerfile
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/amd64,linux/arm64
|
||||||
|
|||||||
73
.github/workflows/docker-cleanup.yml
vendored
73
.github/workflows/docker-cleanup.yml
vendored
@@ -1,73 +0,0 @@
|
|||||||
# This workflow runs on certain conditions to check for and potentially
|
|
||||||
# delete container images from the GHCR which no longer have an associated
|
|
||||||
# code branch.
|
|
||||||
# Requires a PAT with the correct scope set in the secrets.
|
|
||||||
#
|
|
||||||
# This workflow will not trigger runs on forked repos.
|
|
||||||
|
|
||||||
name: Docker Cleanup
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
types:
|
|
||||||
- "closed"
|
|
||||||
push:
|
|
||||||
paths:
|
|
||||||
- ".github/workflows/docker-cleanup.yml"
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: registry-tags-cleanup
|
|
||||||
cancel-in-progress: false
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
cleanup-images:
|
|
||||||
name: Cleanup Stale Images Tags for ${{ matrix.primary-name }}
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- primary-name: "immich-server"
|
|
||||||
- primary-name: "immich-machine-learning"
|
|
||||||
env:
|
|
||||||
# Requires a personal access token with the OAuth scope delete:packages
|
|
||||||
TOKEN: ${{ secrets.PACKAGE_DELETE_TOKEN }}
|
|
||||||
steps:
|
|
||||||
- name: Clean temporary images
|
|
||||||
if: "${{ env.TOKEN != '' }}"
|
|
||||||
uses: stumpylog/image-cleaner-action/ephemeral@v0.9.0
|
|
||||||
with:
|
|
||||||
token: "${{ env.TOKEN }}"
|
|
||||||
owner: "immich-app"
|
|
||||||
is_org: "true"
|
|
||||||
do_delete: "true"
|
|
||||||
package_name: "${{ matrix.primary-name }}"
|
|
||||||
scheme: "pull_request"
|
|
||||||
repo_name: "immich"
|
|
||||||
match_regex: '^pr-(\d+)$|^(\d+)$'
|
|
||||||
|
|
||||||
cleanup-untagged-images:
|
|
||||||
name: Cleanup Untagged Images Tags for ${{ matrix.primary-name }}
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
needs:
|
|
||||||
- cleanup-images
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- primary-name: "immich-server"
|
|
||||||
- primary-name: "immich-machine-learning"
|
|
||||||
- primary-name: "immich-build-cache"
|
|
||||||
env:
|
|
||||||
# Requires a personal access token with the OAuth scope delete:packages
|
|
||||||
TOKEN: ${{ secrets.PACKAGE_DELETE_TOKEN }}
|
|
||||||
steps:
|
|
||||||
- name: Clean untagged images
|
|
||||||
if: "${{ env.TOKEN != '' }}"
|
|
||||||
uses: stumpylog/image-cleaner-action/untagged@v0.9.0
|
|
||||||
with:
|
|
||||||
token: "${{ env.TOKEN }}"
|
|
||||||
owner: "immich-app"
|
|
||||||
do_delete: "true"
|
|
||||||
is_org: "true"
|
|
||||||
package_name: "${{ matrix.primary-name }}"
|
|
||||||
344
.github/workflows/docker.yml
vendored
344
.github/workflows/docker.yml
vendored
@@ -36,10 +36,12 @@ jobs:
|
|||||||
- 'i18n/**'
|
- 'i18n/**'
|
||||||
machine-learning:
|
machine-learning:
|
||||||
- 'machine-learning/**'
|
- 'machine-learning/**'
|
||||||
|
workflow:
|
||||||
|
- '.github/workflows/docker.yml'
|
||||||
|
|
||||||
- name: Check if we should force jobs to run
|
- name: Check if we should force jobs to run
|
||||||
id: should_force
|
id: should_force
|
||||||
run: echo "should_force=${{ github.event_name == 'workflow_dispatch' || github.event_name == 'release' }}" >> "$GITHUB_OUTPUT"
|
run: echo "should_force=${{ steps.found_paths.outputs.workflow == 'true' || github.event_name == 'workflow_dispatch' || github.event_name == 'release' }}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
retag_ml:
|
retag_ml:
|
||||||
name: Re-Tag ML
|
name: Re-Tag ML
|
||||||
@@ -61,8 +63,10 @@ jobs:
|
|||||||
REGISTRY_NAME="ghcr.io"
|
REGISTRY_NAME="ghcr.io"
|
||||||
REPOSITORY=${{ github.repository_owner }}/immich-machine-learning
|
REPOSITORY=${{ github.repository_owner }}/immich-machine-learning
|
||||||
TAG_OLD=main${{ matrix.suffix }}
|
TAG_OLD=main${{ matrix.suffix }}
|
||||||
TAG_NEW=${{ github.event.number == 0 && github.ref_name || format('pr-{0}', github.event.number) }}${{ matrix.suffix }}
|
TAG_PR=${{ github.event.number == 0 && github.ref_name || format('pr-{0}', github.event.number) }}${{ matrix.suffix }}
|
||||||
docker buildx imagetools create -t $REGISTRY_NAME/$REPOSITORY:$TAG_NEW $REGISTRY_NAME/$REPOSITORY:$TAG_OLD
|
TAG_COMMIT=commit-${{ github.event_name != 'pull_request' && github.sha || github.event.pull_request.head.sha }}${{ matrix.suffix }}
|
||||||
|
docker buildx imagetools create -t $REGISTRY_NAME/$REPOSITORY:$TAG_PR $REGISTRY_NAME/$REPOSITORY:$TAG_OLD
|
||||||
|
docker buildx imagetools create -t $REGISTRY_NAME/$REPOSITORY:$TAG_COMMIT $REGISTRY_NAME/$REPOSITORY:$TAG_OLD
|
||||||
|
|
||||||
retag_server:
|
retag_server:
|
||||||
name: Re-Tag Server
|
name: Re-Tag Server
|
||||||
@@ -84,107 +88,100 @@ jobs:
|
|||||||
REGISTRY_NAME="ghcr.io"
|
REGISTRY_NAME="ghcr.io"
|
||||||
REPOSITORY=${{ github.repository_owner }}/immich-server
|
REPOSITORY=${{ github.repository_owner }}/immich-server
|
||||||
TAG_OLD=main${{ matrix.suffix }}
|
TAG_OLD=main${{ matrix.suffix }}
|
||||||
TAG_NEW=${{ github.event.number == 0 && github.ref_name || format('pr-{0}', github.event.number) }}${{ matrix.suffix }}
|
TAG_PR=${{ github.event.number == 0 && github.ref_name || format('pr-{0}', github.event.number) }}${{ matrix.suffix }}
|
||||||
docker buildx imagetools create -t $REGISTRY_NAME/$REPOSITORY:$TAG_NEW $REGISTRY_NAME/$REPOSITORY:$TAG_OLD
|
TAG_COMMIT=commit-${{ github.event_name != 'pull_request' && github.sha || github.event.pull_request.head.sha }}${{ matrix.suffix }}
|
||||||
|
docker buildx imagetools create -t $REGISTRY_NAME/$REPOSITORY:$TAG_PR $REGISTRY_NAME/$REPOSITORY:$TAG_OLD
|
||||||
|
docker buildx imagetools create -t $REGISTRY_NAME/$REPOSITORY:$TAG_COMMIT $REGISTRY_NAME/$REPOSITORY:$TAG_OLD
|
||||||
|
|
||||||
build_and_push_ml:
|
build_and_push_ml:
|
||||||
name: Build and Push ML
|
name: Build and Push ML
|
||||||
needs: pre-job
|
needs: pre-job
|
||||||
if: ${{ needs.pre-job.outputs.should_run_ml == 'true' }}
|
if: ${{ needs.pre-job.outputs.should_run_ml == 'true' }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ${{ matrix.runner }}
|
||||||
env:
|
env:
|
||||||
image: immich-machine-learning
|
image: immich-machine-learning
|
||||||
context: machine-learning
|
context: machine-learning
|
||||||
file: machine-learning/Dockerfile
|
file: machine-learning/Dockerfile
|
||||||
|
GHCR_REPO: ghcr.io/${{ github.repository_owner }}/immich-machine-learning
|
||||||
strategy:
|
strategy:
|
||||||
# Prevent a failure in one image from stopping the other builds
|
# Prevent a failure in one image from stopping the other builds
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- platforms: linux/amd64,linux/arm64
|
- platform: linux/amd64
|
||||||
|
runner: ubuntu-latest
|
||||||
device: cpu
|
device: cpu
|
||||||
|
|
||||||
- platforms: linux/amd64
|
- platform: linux/arm64
|
||||||
|
runner: ubuntu-24.04-arm
|
||||||
|
device: cpu
|
||||||
|
|
||||||
|
- platform: linux/amd64
|
||||||
|
runner: ubuntu-latest
|
||||||
device: cuda
|
device: cuda
|
||||||
suffix: -cuda
|
suffix: -cuda
|
||||||
|
|
||||||
- platforms: linux/amd64
|
- platform: linux/amd64
|
||||||
|
runner: ubuntu-latest
|
||||||
device: openvino
|
device: openvino
|
||||||
suffix: -openvino
|
suffix: -openvino
|
||||||
|
|
||||||
- platforms: linux/arm64
|
- platform: linux/arm64
|
||||||
|
runner: ubuntu-24.04-arm
|
||||||
device: armnn
|
device: armnn
|
||||||
suffix: -armnn
|
suffix: -armnn
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
- name: Prepare
|
||||||
|
run: |
|
||||||
|
platform=${{ matrix.platform }}
|
||||||
|
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v3.3.0
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3.8.0
|
uses: docker/setup-buildx-action@v3.9.0
|
||||||
|
|
||||||
- name: Login to Docker Hub
|
|
||||||
# Only push to Docker Hub when making a release
|
|
||||||
if: ${{ github.event_name == 'release' }}
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
# Skip when PR from a fork
|
|
||||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Generate docker image tags
|
- name: Generate cache key suffix
|
||||||
id: metadata
|
|
||||||
uses: docker/metadata-action@v5
|
|
||||||
with:
|
|
||||||
flavor: |
|
|
||||||
# Disable latest tag
|
|
||||||
latest=false
|
|
||||||
images: |
|
|
||||||
name=ghcr.io/${{ github.repository_owner }}/${{env.image}}
|
|
||||||
name=altran1502/${{env.image}},enable=${{ github.event_name == 'release' }}
|
|
||||||
tags: |
|
|
||||||
# Tag with branch name
|
|
||||||
type=ref,event=branch,suffix=${{ matrix.suffix }}
|
|
||||||
# Tag with pr-number
|
|
||||||
type=ref,event=pr,suffix=${{ matrix.suffix }}
|
|
||||||
# Tag with git tag on release
|
|
||||||
type=ref,event=tag,suffix=${{ matrix.suffix }}
|
|
||||||
type=raw,value=release,enable=${{ github.event_name == 'release' }},suffix=${{ matrix.suffix }}
|
|
||||||
|
|
||||||
- name: Determine build cache output
|
|
||||||
id: cache-target
|
|
||||||
run: |
|
run: |
|
||||||
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
|
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
|
||||||
# Essentially just ignore the cache output (PR can't write to registry cache)
|
echo "CACHE_KEY_SUFFIX=pr-${{ github.event.number }}" >> $GITHUB_ENV
|
||||||
|
else
|
||||||
|
echo "CACHE_KEY_SUFFIX=$(echo ${{ github.ref_name }} | sed 's/[^a-zA-Z0-9]/-/g')" >> $GITHUB_ENV
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Generate cache target
|
||||||
|
id: cache-target
|
||||||
|
run: |
|
||||||
|
if [[ "${{ github.event.pull_request.head.repo.fork }}" == "true" ]]; then
|
||||||
|
# Essentially just ignore the cache output (forks can't write to registry cache)
|
||||||
echo "cache-to=type=local,dest=/tmp/discard,ignore-error=true" >> $GITHUB_OUTPUT
|
echo "cache-to=type=local,dest=/tmp/discard,ignore-error=true" >> $GITHUB_OUTPUT
|
||||||
else
|
else
|
||||||
echo "cache-to=type=registry,mode=max,ref=ghcr.io/${{ github.repository_owner }}/immich-build-cache:${{ env.image }}" >> $GITHUB_OUTPUT
|
echo "cache-to=type=registry,ref=${{ env.GHCR_REPO }}-build-cache:${{ env.PLATFORM_PAIR }}-${{ matrix.device }}-${{ env.CACHE_KEY_SUFFIX }},mode=max,compression=zstd" >> $GITHUB_OUTPUT
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Build and push image
|
- name: Build and push image
|
||||||
uses: docker/build-push-action@v6.12.0
|
id: build
|
||||||
|
uses: docker/build-push-action@v6.13.0
|
||||||
with:
|
with:
|
||||||
context: ${{ env.context }}
|
context: ${{ env.context }}
|
||||||
file: ${{ env.file }}
|
file: ${{ env.file }}
|
||||||
platforms: ${{ matrix.platforms }}
|
platforms: ${{ matrix.platforms }}
|
||||||
# Skip pushing when PR from a fork
|
|
||||||
push: ${{ !github.event.pull_request.head.repo.fork }}
|
|
||||||
cache-from: type=registry,ref=ghcr.io/${{ github.repository_owner }}/immich-build-cache:${{env.image}}
|
|
||||||
cache-to: ${{ steps.cache-target.outputs.cache-to }}
|
|
||||||
tags: ${{ steps.metadata.outputs.tags }}
|
|
||||||
labels: ${{ steps.metadata.outputs.labels }}
|
labels: ${{ steps.metadata.outputs.labels }}
|
||||||
|
cache-to: ${{ steps.cache-target.outputs.cache-to }}
|
||||||
|
cache-from: |
|
||||||
|
type=registry,ref=${{ env.GHCR_REPO }}-build-cache:${{ env.PLATFORM_PAIR }}-${{ matrix.device }}-${{ env.CACHE_KEY_SUFFIX }}
|
||||||
|
type=registry,ref=${{ env.GHCR_REPO }}-build-cache:${{ env.PLATFORM_PAIR }}-${{ matrix.device }}-main
|
||||||
|
outputs: type=image,"name=${{ env.GHCR_REPO }}",push-by-digest=true,name-canonical=true,push=${{ !github.event.pull_request.head.repo.fork }}
|
||||||
build-args: |
|
build-args: |
|
||||||
DEVICE=${{ matrix.device }}
|
DEVICE=${{ matrix.device }}
|
||||||
BUILD_ID=${{ github.run_id }}
|
BUILD_ID=${{ github.run_id }}
|
||||||
@@ -192,100 +189,245 @@ jobs:
|
|||||||
BUILD_SOURCE_REF=${{ github.ref_name }}
|
BUILD_SOURCE_REF=${{ github.ref_name }}
|
||||||
BUILD_SOURCE_COMMIT=${{ github.sha }}
|
BUILD_SOURCE_COMMIT=${{ github.sha }}
|
||||||
|
|
||||||
|
- name: Export digest
|
||||||
|
run: |
|
||||||
|
mkdir -p ${{ runner.temp }}/digests
|
||||||
|
digest="${{ steps.build.outputs.digest }}"
|
||||||
|
touch "${{ runner.temp }}/digests/${digest#sha256:}"
|
||||||
|
|
||||||
|
- name: Upload digest
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: ml-digests-${{ matrix.device }}-${{ env.PLATFORM_PAIR }}
|
||||||
|
path: ${{ runner.temp }}/digests/*
|
||||||
|
if-no-files-found: error
|
||||||
|
retention-days: 1
|
||||||
|
|
||||||
|
merge_ml:
|
||||||
|
name: Merge & Push ML
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ needs.pre-job.outputs.should_run_ml == 'true' && !github.event.pull_request.head.repo.fork }}
|
||||||
|
env:
|
||||||
|
GHCR_REPO: ghcr.io/${{ github.repository_owner }}/immich-machine-learning
|
||||||
|
DOCKER_REPO: altran1502/immich-machine-learning
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- device: cpu
|
||||||
|
- device: cuda
|
||||||
|
suffix: -cuda
|
||||||
|
- device: openvino
|
||||||
|
suffix: -openvino
|
||||||
|
- device: armnn
|
||||||
|
suffix: -armnn
|
||||||
|
needs:
|
||||||
|
- build_and_push_ml
|
||||||
|
steps:
|
||||||
|
- name: Download digests
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
path: ${{ runner.temp }}/digests
|
||||||
|
pattern: ml-digests-${{ matrix.device }}-*
|
||||||
|
merge-multiple: true
|
||||||
|
|
||||||
|
- name: Login to Docker Hub
|
||||||
|
if: ${{ github.event_name == 'release' }}
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Login to GHCR
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.repository_owner }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Generate docker image tags
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@v5
|
||||||
|
with:
|
||||||
|
flavor: |
|
||||||
|
# Disable latest tag
|
||||||
|
latest=false
|
||||||
|
images: |
|
||||||
|
name=${{ env.GHCR_REPO }}
|
||||||
|
name=${{ env.DOCKER_REPO }},enable=${{ github.event_name == 'release' }}
|
||||||
|
tags: |
|
||||||
|
# Tag with branch name
|
||||||
|
type=ref,event=branch,suffix=${{ matrix.suffix }}
|
||||||
|
# Tag with pr-number
|
||||||
|
type=ref,event=pr,suffix=${{ matrix.suffix }}
|
||||||
|
# Tag with long commit sha hash
|
||||||
|
type=sha,format=long,prefix=commit-,suffix=${{ matrix.suffix }}
|
||||||
|
# Tag with git tag on release
|
||||||
|
type=ref,event=tag,suffix=${{ matrix.suffix }}
|
||||||
|
type=raw,value=release,enable=${{ github.event_name == 'release' }},suffix=${{ matrix.suffix }}
|
||||||
|
|
||||||
|
- name: Create manifest list and push
|
||||||
|
working-directory: ${{ runner.temp }}/digests
|
||||||
|
run: |
|
||||||
|
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
||||||
|
$(printf '${{ env.GHCR_REPO }}@sha256:%s ' *)
|
||||||
|
|
||||||
build_and_push_server:
|
build_and_push_server:
|
||||||
name: Build and Push Server
|
name: Build and Push Server
|
||||||
runs-on: ubuntu-latest
|
runs-on: ${{ matrix.runner }}
|
||||||
needs: pre-job
|
needs: pre-job
|
||||||
if: ${{ needs.pre-job.outputs.should_run_server == 'true' }}
|
if: ${{ needs.pre-job.outputs.should_run_server == 'true' }}
|
||||||
env:
|
env:
|
||||||
image: immich-server
|
image: immich-server
|
||||||
context: .
|
context: .
|
||||||
file: server/Dockerfile
|
file: server/Dockerfile
|
||||||
|
GHCR_REPO: ghcr.io/${{ github.repository_owner }}/immich-server
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- platforms: linux/amd64,linux/arm64
|
- platform: linux/amd64
|
||||||
device: cpu
|
runner: ubuntu-latest
|
||||||
|
- platform: linux/arm64
|
||||||
|
runner: ubuntu-24.04-arm
|
||||||
steps:
|
steps:
|
||||||
|
- name: Prepare
|
||||||
|
run: |
|
||||||
|
platform=${{ matrix.platform }}
|
||||||
|
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v3.3.0
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3.8.0
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
- name: Login to Docker Hub
|
|
||||||
# Only push to Docker Hub when making a release
|
|
||||||
if: ${{ github.event_name == 'release' }}
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
# Skip when PR from a fork
|
|
||||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Generate cache key suffix
|
||||||
|
run: |
|
||||||
|
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
|
||||||
|
echo "CACHE_KEY_SUFFIX=pr-${{ github.event.number }}" >> $GITHUB_ENV
|
||||||
|
else
|
||||||
|
echo "CACHE_KEY_SUFFIX=$(echo ${{ github.ref_name }} | sed 's/[^a-zA-Z0-9]/-/g')" >> $GITHUB_ENV
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Generate cache target
|
||||||
|
id: cache-target
|
||||||
|
run: |
|
||||||
|
if [[ "${{ github.event.pull_request.head.repo.fork }}" == "true" ]]; then
|
||||||
|
# Essentially just ignore the cache output (forks can't write to registry cache)
|
||||||
|
echo "cache-to=type=local,dest=/tmp/discard,ignore-error=true" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo "cache-to=type=registry,ref=${{ env.GHCR_REPO }}-build-cache:${{ env.PLATFORM_PAIR }}-${{ matrix.device }}-${{ env.CACHE_KEY_SUFFIX }},mode=max,compression=zstd" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Build and push image
|
||||||
|
id: build
|
||||||
|
uses: docker/build-push-action@v6.13.0
|
||||||
|
with:
|
||||||
|
context: ${{ env.context }}
|
||||||
|
file: ${{ env.file }}
|
||||||
|
platforms: ${{ matrix.platform }}
|
||||||
|
labels: ${{ steps.metadata.outputs.labels }}
|
||||||
|
cache-to: ${{ steps.cache-target.outputs.cache-to }}
|
||||||
|
cache-from: |
|
||||||
|
type=registry,ref=${{ env.GHCR_REPO }}-build-cache:${{ env.PLATFORM_PAIR }}-${{ env.CACHE_KEY_SUFFIX }}
|
||||||
|
type=registry,ref=${{ env.GHCR_REPO }}-build-cache:${{ env.PLATFORM_PAIR }}-main
|
||||||
|
outputs: type=image,"name=${{ env.GHCR_REPO }}",push-by-digest=true,name-canonical=true,push=${{ !github.event.pull_request.head.repo.fork }}
|
||||||
|
build-args: |
|
||||||
|
DEVICE=cpu
|
||||||
|
BUILD_ID=${{ github.run_id }}
|
||||||
|
BUILD_IMAGE=${{ github.event_name == 'release' && github.ref_name || steps.metadata.outputs.tags }}
|
||||||
|
BUILD_SOURCE_REF=${{ github.ref_name }}
|
||||||
|
BUILD_SOURCE_COMMIT=${{ github.sha }}
|
||||||
|
|
||||||
|
- name: Export digest
|
||||||
|
run: |
|
||||||
|
mkdir -p ${{ runner.temp }}/digests
|
||||||
|
digest="${{ steps.build.outputs.digest }}"
|
||||||
|
touch "${{ runner.temp }}/digests/${digest#sha256:}"
|
||||||
|
|
||||||
|
- name: Upload digest
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: server-digests-${{ env.PLATFORM_PAIR }}
|
||||||
|
path: ${{ runner.temp }}/digests/*
|
||||||
|
if-no-files-found: error
|
||||||
|
retention-days: 1
|
||||||
|
|
||||||
|
merge_server:
|
||||||
|
name: Merge & Push Server
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ needs.pre-job.outputs.should_run_server == 'true' && !github.event.pull_request.head.repo.fork }}
|
||||||
|
env:
|
||||||
|
GHCR_REPO: ghcr.io/${{ github.repository_owner }}/immich-server
|
||||||
|
DOCKER_REPO: altran1502/immich-server
|
||||||
|
needs:
|
||||||
|
- build_and_push_server
|
||||||
|
steps:
|
||||||
|
- name: Download digests
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
path: ${{ runner.temp }}/digests
|
||||||
|
pattern: server-digests-*
|
||||||
|
merge-multiple: true
|
||||||
|
|
||||||
|
- name: Login to Docker Hub
|
||||||
|
if: ${{ github.event_name == 'release' }}
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Login to GHCR
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.repository_owner }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
- name: Generate docker image tags
|
- name: Generate docker image tags
|
||||||
id: metadata
|
id: meta
|
||||||
uses: docker/metadata-action@v5
|
uses: docker/metadata-action@v5
|
||||||
with:
|
with:
|
||||||
flavor: |
|
flavor: |
|
||||||
# Disable latest tag
|
# Disable latest tag
|
||||||
latest=false
|
latest=false
|
||||||
images: |
|
images: |
|
||||||
name=ghcr.io/${{ github.repository_owner }}/${{env.image}}
|
name=${{ env.GHCR_REPO }}
|
||||||
name=altran1502/${{env.image}},enable=${{ github.event_name == 'release' }}
|
name=${{ env.DOCKER_REPO }},enable=${{ github.event_name == 'release' }}
|
||||||
tags: |
|
tags: |
|
||||||
# Tag with branch name
|
# Tag with branch name
|
||||||
type=ref,event=branch,suffix=${{ matrix.suffix }}
|
type=ref,event=branch,suffix=${{ matrix.suffix }}
|
||||||
# Tag with pr-number
|
# Tag with pr-number
|
||||||
type=ref,event=pr,suffix=${{ matrix.suffix }}
|
type=ref,event=pr,suffix=${{ matrix.suffix }}
|
||||||
|
# Tag with long commit sha hash
|
||||||
|
type=sha,format=long,prefix=commit-,suffix=${{ matrix.suffix }}
|
||||||
# Tag with git tag on release
|
# Tag with git tag on release
|
||||||
type=ref,event=tag,suffix=${{ matrix.suffix }}
|
type=ref,event=tag,suffix=${{ matrix.suffix }}
|
||||||
type=raw,value=release,enable=${{ github.event_name == 'release' }},suffix=${{ matrix.suffix }}
|
type=raw,value=release,enable=${{ github.event_name == 'release' }},suffix=${{ matrix.suffix }}
|
||||||
|
|
||||||
- name: Determine build cache output
|
- name: Create manifest list and push
|
||||||
id: cache-target
|
working-directory: ${{ runner.temp }}/digests
|
||||||
run: |
|
run: |
|
||||||
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
|
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
||||||
# Essentially just ignore the cache output (PR can't write to registry cache)
|
$(printf '${{ env.GHCR_REPO }}@sha256:%s ' *)
|
||||||
echo "cache-to=type=local,dest=/tmp/discard,ignore-error=true" >> $GITHUB_OUTPUT
|
|
||||||
else
|
|
||||||
echo "cache-to=type=registry,mode=max,ref=ghcr.io/${{ github.repository_owner }}/immich-build-cache:${{ env.image }}" >> $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Build and push image
|
|
||||||
uses: docker/build-push-action@v6.12.0
|
|
||||||
with:
|
|
||||||
context: ${{ env.context }}
|
|
||||||
file: ${{ env.file }}
|
|
||||||
platforms: ${{ matrix.platforms }}
|
|
||||||
# Skip pushing when PR from a fork
|
|
||||||
push: ${{ !github.event.pull_request.head.repo.fork }}
|
|
||||||
cache-from: type=registry,ref=ghcr.io/${{ github.repository_owner }}/immich-build-cache:${{env.image}}
|
|
||||||
cache-to: ${{ steps.cache-target.outputs.cache-to }}
|
|
||||||
tags: ${{ steps.metadata.outputs.tags }}
|
|
||||||
labels: ${{ steps.metadata.outputs.labels }}
|
|
||||||
build-args: |
|
|
||||||
DEVICE=${{ matrix.device }}
|
|
||||||
BUILD_ID=${{ github.run_id }}
|
|
||||||
BUILD_IMAGE=${{ github.event_name == 'release' && github.ref_name || steps.metadata.outputs.tags }}
|
|
||||||
BUILD_SOURCE_REF=${{ github.ref_name }}
|
|
||||||
BUILD_SOURCE_COMMIT=${{ github.sha }}
|
|
||||||
|
|
||||||
success-check-server:
|
success-check-server:
|
||||||
name: Docker Build & Push Server Success
|
name: Docker Build & Push Server Success
|
||||||
needs: [build_and_push_server, retag_server]
|
needs: [merge_server, retag_server]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: always()
|
if: always()
|
||||||
steps:
|
steps:
|
||||||
@@ -298,7 +440,7 @@ jobs:
|
|||||||
|
|
||||||
success-check-ml:
|
success-check-ml:
|
||||||
name: Docker Build & Push ML Success
|
name: Docker Build & Push ML Success
|
||||||
needs: [build_and_push_ml, retag_ml]
|
needs: [merge_ml, retag_ml]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: always()
|
if: always()
|
||||||
steps:
|
steps:
|
||||||
|
|||||||
6
.github/workflows/docs-build.yml
vendored
6
.github/workflows/docs-build.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
|||||||
pre-job:
|
pre-job:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
outputs:
|
outputs:
|
||||||
should_run: ${{ steps.found_paths.outputs.docs == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
should_run: ${{ steps.found_paths.outputs.docs == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -25,9 +25,11 @@ jobs:
|
|||||||
filters: |
|
filters: |
|
||||||
docs:
|
docs:
|
||||||
- 'docs/**'
|
- 'docs/**'
|
||||||
|
workflow:
|
||||||
|
- '.github/workflows/docs-build.yml'
|
||||||
- name: Check if we should force jobs to run
|
- name: Check if we should force jobs to run
|
||||||
id: should_force
|
id: should_force
|
||||||
run: echo "should_force=${{ github.event_name == 'release' || github.ref_name == 'main' }}" >> "$GITHUB_OUTPUT"
|
run: echo "should_force=${{ steps.found_paths.outputs.workflow == 'true' || github.event_name == 'release' || github.ref_name == 'main' }}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
build:
|
build:
|
||||||
name: Docs Build
|
name: Docs Build
|
||||||
|
|||||||
17
.github/workflows/preview-comment.yaml
vendored
Normal file
17
.github/workflows/preview-comment.yaml
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
name: Preview comment
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [labeled]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
comment-status:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ github.event.label.name == 'preview' }}
|
||||||
|
permissions:
|
||||||
|
pull-requests: write
|
||||||
|
steps:
|
||||||
|
- uses: mshick/add-pr-comment@v2
|
||||||
|
with:
|
||||||
|
message-id: "preview-status"
|
||||||
|
message: "Deploying preview environment to https://pr-${{ github.event.pull_request.number }}.preview.internal.immich.cloud/"
|
||||||
4
.github/workflows/static_analysis.yml
vendored
4
.github/workflows/static_analysis.yml
vendored
@@ -23,9 +23,11 @@ jobs:
|
|||||||
filters: |
|
filters: |
|
||||||
mobile:
|
mobile:
|
||||||
- 'mobile/**'
|
- 'mobile/**'
|
||||||
|
workflow:
|
||||||
|
- '.github/workflows/static_analysis.yml'
|
||||||
- name: Check if we should force jobs to run
|
- name: Check if we should force jobs to run
|
||||||
id: should_force
|
id: should_force
|
||||||
run: echo "should_force=${{ github.event_name == 'release' }}" >> "$GITHUB_OUTPUT"
|
run: echo "should_force=${{ steps.found_paths.outputs.workflow == 'true' || github.event_name == 'release' }}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
mobile-dart-analyze:
|
mobile-dart-analyze:
|
||||||
name: Run Dart Code Analysis
|
name: Run Dart Code Analysis
|
||||||
|
|||||||
4
.github/workflows/test.yml
vendored
4
.github/workflows/test.yml
vendored
@@ -43,10 +43,12 @@ jobs:
|
|||||||
- 'mobile/**'
|
- 'mobile/**'
|
||||||
machine-learning:
|
machine-learning:
|
||||||
- 'machine-learning/**'
|
- 'machine-learning/**'
|
||||||
|
workflow:
|
||||||
|
- '.github/workflows/test.yml'
|
||||||
|
|
||||||
- name: Check if we should force jobs to run
|
- name: Check if we should force jobs to run
|
||||||
id: should_force
|
id: should_force
|
||||||
run: echo "should_force=${{ github.event_name == 'workflow_dispatch' }}" >> "$GITHUB_OUTPUT"
|
run: echo "should_force=${{ steps.found_paths.outputs.workflow == 'true' || github.event_name == 'workflow_dispatch' }}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
server-unit-tests:
|
server-unit-tests:
|
||||||
name: Test & Lint Server
|
name: Test & Lint Server
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
22.13.1
|
22.14.0
|
||||||
|
|||||||
295
cli/package-lock.json
generated
295
cli/package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@immich/cli",
|
"name": "@immich/cli",
|
||||||
"version": "2.2.47",
|
"version": "2.2.50",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@immich/cli",
|
"name": "@immich/cli",
|
||||||
"version": "2.2.47",
|
"version": "2.2.50",
|
||||||
"license": "GNU Affero General Public License version 3",
|
"license": "GNU Affero General Public License version 3",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fast-glob": "^3.3.2",
|
"fast-glob": "^3.3.2",
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
"@types/cli-progress": "^3.11.0",
|
"@types/cli-progress": "^3.11.0",
|
||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"@types/mock-fs": "^4.13.1",
|
"@types/mock-fs": "^4.13.1",
|
||||||
"@types/node": "^22.10.9",
|
"@types/node": "^22.13.2",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.15.0",
|
"@typescript-eslint/eslint-plugin": "^8.15.0",
|
||||||
"@typescript-eslint/parser": "^8.15.0",
|
"@typescript-eslint/parser": "^8.15.0",
|
||||||
"@vitest/coverage-v8": "^3.0.0",
|
"@vitest/coverage-v8": "^3.0.0",
|
||||||
@@ -52,14 +52,14 @@
|
|||||||
},
|
},
|
||||||
"../open-api/typescript-sdk": {
|
"../open-api/typescript-sdk": {
|
||||||
"name": "@immich/sdk",
|
"name": "@immich/sdk",
|
||||||
"version": "1.125.6",
|
"version": "1.126.1",
|
||||||
"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.10.9",
|
"@types/node": "^22.13.2",
|
||||||
"typescript": "^5.3.3"
|
"typescript": "^5.3.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -881,9 +881,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint/js": {
|
"node_modules/@eslint/js": {
|
||||||
"version": "9.18.0",
|
"version": "9.20.0",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.20.0.tgz",
|
||||||
"integrity": "sha512-fK6L7rxcq6/z+AaQMtiFTkvbHkBLNlwyRxHpKawP0x3u9+NC6MQTnFW+AdpwC6gfHTW0051cokQgtTN2FqlxQA==",
|
"integrity": "sha512-iZA07H9io9Wn836aVTytRaNqh00Sad+EamwOVJT12GTLw1VGMFV/4JaME+JjLtr9fiGaoWgYnS54wrfWsSs4oQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -1482,9 +1482,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "22.10.9",
|
"version": "22.13.4",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.9.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.4.tgz",
|
||||||
"integrity": "sha512-Ir6hwgsKyNESl/gLOcEz3krR4CBGgliDqBQ2ma4wIhEx0w+xnoeTq3tdrNw15kU3SxogDjOgv9sqdtLW8mIHaw==",
|
"integrity": "sha512-ywP2X0DYtX3y08eFVx5fNIw7/uIv8hYUKgXoK8oayJlLnKcRfEYCxWMVE1XagUdVtCJlZT1AU4LXEABW+L1Peg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -1498,21 +1498,21 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||||
"version": "8.20.0",
|
"version": "8.24.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.20.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.24.0.tgz",
|
||||||
"integrity": "sha512-naduuphVw5StFfqp4Gq4WhIBE2gN1GEmMUExpJYknZJdRnc+2gDzB8Z3+5+/Kv33hPQRDGzQO/0opHE72lZZ6A==",
|
"integrity": "sha512-aFcXEJJCI4gUdXgoo/j9udUYIHgF23MFkg09LFz2dzEmU0+1Plk4rQWv/IYKvPHAtlkkGoB3m5e6oUp+JPsNaQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/regexpp": "^4.10.0",
|
"@eslint-community/regexpp": "^4.10.0",
|
||||||
"@typescript-eslint/scope-manager": "8.20.0",
|
"@typescript-eslint/scope-manager": "8.24.0",
|
||||||
"@typescript-eslint/type-utils": "8.20.0",
|
"@typescript-eslint/type-utils": "8.24.0",
|
||||||
"@typescript-eslint/utils": "8.20.0",
|
"@typescript-eslint/utils": "8.24.0",
|
||||||
"@typescript-eslint/visitor-keys": "8.20.0",
|
"@typescript-eslint/visitor-keys": "8.24.0",
|
||||||
"graphemer": "^1.4.0",
|
"graphemer": "^1.4.0",
|
||||||
"ignore": "^5.3.1",
|
"ignore": "^5.3.1",
|
||||||
"natural-compare": "^1.4.0",
|
"natural-compare": "^1.4.0",
|
||||||
"ts-api-utils": "^2.0.0"
|
"ts-api-utils": "^2.0.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
@@ -1528,16 +1528,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/parser": {
|
"node_modules/@typescript-eslint/parser": {
|
||||||
"version": "8.20.0",
|
"version": "8.24.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.20.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.24.0.tgz",
|
||||||
"integrity": "sha512-gKXG7A5HMyjDIedBi6bUrDcun8GIjnI8qOwVLiY3rx6T/sHP/19XLJOnIq/FgQvWLHja5JN/LSE7eklNBr612g==",
|
"integrity": "sha512-MFDaO9CYiard9j9VepMNa9MTcqVvSny2N4hkY6roquzj8pdCBRENhErrteaQuu7Yjn1ppk0v1/ZF9CG3KIlrTA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/scope-manager": "8.20.0",
|
"@typescript-eslint/scope-manager": "8.24.0",
|
||||||
"@typescript-eslint/types": "8.20.0",
|
"@typescript-eslint/types": "8.24.0",
|
||||||
"@typescript-eslint/typescript-estree": "8.20.0",
|
"@typescript-eslint/typescript-estree": "8.24.0",
|
||||||
"@typescript-eslint/visitor-keys": "8.20.0",
|
"@typescript-eslint/visitor-keys": "8.24.0",
|
||||||
"debug": "^4.3.4"
|
"debug": "^4.3.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -1553,14 +1553,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/scope-manager": {
|
"node_modules/@typescript-eslint/scope-manager": {
|
||||||
"version": "8.20.0",
|
"version": "8.24.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.20.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.24.0.tgz",
|
||||||
"integrity": "sha512-J7+VkpeGzhOt3FeG1+SzhiMj9NzGD/M6KoGn9f4dbz3YzK9hvbhVTmLj/HiTp9DazIzJ8B4XcM80LrR9Dm1rJw==",
|
"integrity": "sha512-HZIX0UByphEtdVBKaQBgTDdn9z16l4aTUz8e8zPQnyxwHBtf5vtl1L+OhH+m1FGV9DrRmoDuYKqzVrvWDcDozw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "8.20.0",
|
"@typescript-eslint/types": "8.24.0",
|
||||||
"@typescript-eslint/visitor-keys": "8.20.0"
|
"@typescript-eslint/visitor-keys": "8.24.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
@@ -1571,16 +1571,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/type-utils": {
|
"node_modules/@typescript-eslint/type-utils": {
|
||||||
"version": "8.20.0",
|
"version": "8.24.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.20.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.24.0.tgz",
|
||||||
"integrity": "sha512-bPC+j71GGvA7rVNAHAtOjbVXbLN5PkwqMvy1cwGeaxUoRQXVuKCebRoLzm+IPW/NtFFpstn1ummSIasD5t60GA==",
|
"integrity": "sha512-8fitJudrnY8aq0F1wMiPM1UUgiXQRJ5i8tFjq9kGfRajU+dbPyOuHbl0qRopLEidy0MwqgTHDt6CnSeXanNIwA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/typescript-estree": "8.20.0",
|
"@typescript-eslint/typescript-estree": "8.24.0",
|
||||||
"@typescript-eslint/utils": "8.20.0",
|
"@typescript-eslint/utils": "8.24.0",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"ts-api-utils": "^2.0.0"
|
"ts-api-utils": "^2.0.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
@@ -1595,9 +1595,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/types": {
|
"node_modules/@typescript-eslint/types": {
|
||||||
"version": "8.20.0",
|
"version": "8.24.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.20.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.24.0.tgz",
|
||||||
"integrity": "sha512-cqaMiY72CkP+2xZRrFt3ExRBu0WmVitN/rYPZErA80mHjHx/Svgp8yfbzkJmDoQ/whcytOPO9/IZXnOc+wigRA==",
|
"integrity": "sha512-VacJCBTyje7HGAw7xp11q439A+zeGG0p0/p2zsZwpnMzjPB5WteaWqt4g2iysgGFafrqvyLWqq6ZPZAOCoefCw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -1609,20 +1609,20 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/typescript-estree": {
|
"node_modules/@typescript-eslint/typescript-estree": {
|
||||||
"version": "8.20.0",
|
"version": "8.24.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.20.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.24.0.tgz",
|
||||||
"integrity": "sha512-Y7ncuy78bJqHI35NwzWol8E0X7XkRVS4K4P4TCyzWkOJih5NDvtoRDW4Ba9YJJoB2igm9yXDdYI/+fkiiAxPzA==",
|
"integrity": "sha512-ITjYcP0+8kbsvT9bysygfIfb+hBj6koDsu37JZG7xrCiy3fPJyNmfVtaGsgTUSEuTzcvME5YI5uyL5LD1EV5ZQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "8.20.0",
|
"@typescript-eslint/types": "8.24.0",
|
||||||
"@typescript-eslint/visitor-keys": "8.20.0",
|
"@typescript-eslint/visitor-keys": "8.24.0",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"fast-glob": "^3.3.2",
|
"fast-glob": "^3.3.2",
|
||||||
"is-glob": "^4.0.3",
|
"is-glob": "^4.0.3",
|
||||||
"minimatch": "^9.0.4",
|
"minimatch": "^9.0.4",
|
||||||
"semver": "^7.6.0",
|
"semver": "^7.6.0",
|
||||||
"ts-api-utils": "^2.0.0"
|
"ts-api-utils": "^2.0.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
@@ -1636,16 +1636,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/utils": {
|
"node_modules/@typescript-eslint/utils": {
|
||||||
"version": "8.20.0",
|
"version": "8.24.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.20.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.24.0.tgz",
|
||||||
"integrity": "sha512-dq70RUw6UK9ei7vxc4KQtBRk7qkHZv447OUZ6RPQMQl71I3NZxQJX/f32Smr+iqWrB02pHKn2yAdHBb0KNrRMA==",
|
"integrity": "sha512-07rLuUBElvvEb1ICnafYWr4hk8/U7X9RDCOqd9JcAMtjh/9oRmcfN4yGzbPVirgMR0+HLVHehmu19CWeh7fsmQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.4.0",
|
"@eslint-community/eslint-utils": "^4.4.0",
|
||||||
"@typescript-eslint/scope-manager": "8.20.0",
|
"@typescript-eslint/scope-manager": "8.24.0",
|
||||||
"@typescript-eslint/types": "8.20.0",
|
"@typescript-eslint/types": "8.24.0",
|
||||||
"@typescript-eslint/typescript-estree": "8.20.0"
|
"@typescript-eslint/typescript-estree": "8.24.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
@@ -1660,13 +1660,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/visitor-keys": {
|
"node_modules/@typescript-eslint/visitor-keys": {
|
||||||
"version": "8.20.0",
|
"version": "8.24.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.20.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.24.0.tgz",
|
||||||
"integrity": "sha512-v/BpkeeYAsPkKCkR8BDwcno0llhzWVqPOamQrAEMdpZav2Y9OVjd9dwJyBLJWwf335B5DmlifECIkZRJCaGaHA==",
|
"integrity": "sha512-kArLq83QxGLbuHrTMoOEWO+l2MwsNS2TGISEdx8xgqpkbytB07XmlQyQdNDrCc1ecSqx0cnmhGvpX+VBwqqSkg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "8.20.0",
|
"@typescript-eslint/types": "8.24.0",
|
||||||
"eslint-visitor-keys": "^4.2.0"
|
"eslint-visitor-keys": "^4.2.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -1691,9 +1691,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/coverage-v8": {
|
"node_modules/@vitest/coverage-v8": {
|
||||||
"version": "3.0.3",
|
"version": "3.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.0.5.tgz",
|
||||||
"integrity": "sha512-uVbJ/xhImdNtzPnLyxCZJMTeTIYdgcC2nWtBBBpR1H6z0w8m7D+9/zrDIx2nNxgMg9r+X8+RY2qVpUDeW2b3nw==",
|
"integrity": "sha512-zOOWIsj5fHh3jjGwQg+P+J1FW3s4jBu1Zqga0qW60yutsBtqEqNEJKWYh7cYn1yGD+1bdPsPdC/eL4eVK56xMg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -1714,8 +1714,8 @@
|
|||||||
"url": "https://opencollective.com/vitest"
|
"url": "https://opencollective.com/vitest"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@vitest/browser": "3.0.3",
|
"@vitest/browser": "3.0.5",
|
||||||
"vitest": "3.0.3"
|
"vitest": "3.0.5"
|
||||||
},
|
},
|
||||||
"peerDependenciesMeta": {
|
"peerDependenciesMeta": {
|
||||||
"@vitest/browser": {
|
"@vitest/browser": {
|
||||||
@@ -1724,14 +1724,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/expect": {
|
"node_modules/@vitest/expect": {
|
||||||
"version": "3.0.3",
|
"version": "3.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.0.5.tgz",
|
||||||
"integrity": "sha512-SbRCHU4qr91xguu+dH3RUdI5dC86zm8aZWydbp961aIR7G8OYNN6ZiayFuf9WAngRbFOfdrLHCGgXTj3GtoMRQ==",
|
"integrity": "sha512-nNIOqupgZ4v5jWuQx2DSlHLEs7Q4Oh/7AYwNyE+k0UQzG7tSmjPXShUikn1mpNGzYEN2jJbTvLejwShMitovBA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitest/spy": "3.0.3",
|
"@vitest/spy": "3.0.5",
|
||||||
"@vitest/utils": "3.0.3",
|
"@vitest/utils": "3.0.5",
|
||||||
"chai": "^5.1.2",
|
"chai": "^5.1.2",
|
||||||
"tinyrainbow": "^2.0.0"
|
"tinyrainbow": "^2.0.0"
|
||||||
},
|
},
|
||||||
@@ -1740,13 +1740,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/mocker": {
|
"node_modules/@vitest/mocker": {
|
||||||
"version": "3.0.3",
|
"version": "3.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.0.5.tgz",
|
||||||
"integrity": "sha512-XT2XBc4AN9UdaxJAeIlcSZ0ILi/GzmG5G8XSly4gaiqIvPV3HMTSIDZWJVX6QRJ0PX1m+W8Cy0K9ByXNb/bPIA==",
|
"integrity": "sha512-CLPNBFBIE7x6aEGbIjaQAX03ZZlBMaWwAjBdMkIf/cAn6xzLTiM3zYqO/WAbieEjsAZir6tO71mzeHZoodThvw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitest/spy": "3.0.3",
|
"@vitest/spy": "3.0.5",
|
||||||
"estree-walker": "^3.0.3",
|
"estree-walker": "^3.0.3",
|
||||||
"magic-string": "^0.30.17"
|
"magic-string": "^0.30.17"
|
||||||
},
|
},
|
||||||
@@ -1767,9 +1767,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/pretty-format": {
|
"node_modules/@vitest/pretty-format": {
|
||||||
"version": "3.0.3",
|
"version": "3.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.5.tgz",
|
||||||
"integrity": "sha512-gCrM9F7STYdsDoNjGgYXKPq4SkSxwwIU5nkaQvdUxiQ0EcNlez+PdKOVIsUJvh9P9IeIFmjn4IIREWblOBpP2Q==",
|
"integrity": "sha512-CjUtdmpOcm4RVtB+up8r2vVDLR16Mgm/bYdkGFe3Yj/scRfCpbSi2W/BDSDcFK7ohw8UXvjMbOp9H4fByd/cOA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -1780,38 +1780,38 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/runner": {
|
"node_modules/@vitest/runner": {
|
||||||
"version": "3.0.3",
|
"version": "3.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.0.5.tgz",
|
||||||
"integrity": "sha512-Rgi2kOAk5ZxWZlwPguRJFOBmWs6uvvyAAR9k3MvjRvYrG7xYvKChZcmnnpJCS98311CBDMqsW9MzzRFsj2gX3g==",
|
"integrity": "sha512-BAiZFityFexZQi2yN4OX3OkJC6scwRo8EhRB0Z5HIGGgd2q+Nq29LgHU/+ovCtd0fOfXj5ZI6pwdlUmC5bpi8A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitest/utils": "3.0.3",
|
"@vitest/utils": "3.0.5",
|
||||||
"pathe": "^2.0.1"
|
"pathe": "^2.0.2"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://opencollective.com/vitest"
|
"url": "https://opencollective.com/vitest"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/snapshot": {
|
"node_modules/@vitest/snapshot": {
|
||||||
"version": "3.0.3",
|
"version": "3.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.0.5.tgz",
|
||||||
"integrity": "sha512-kNRcHlI4txBGztuJfPEJ68VezlPAXLRT1u5UCx219TU3kOG2DplNxhWLwDf2h6emwmTPogzLnGVwP6epDaJN6Q==",
|
"integrity": "sha512-GJPZYcd7v8QNUJ7vRvLDmRwl+a1fGg4T/54lZXe+UOGy47F9yUfE18hRCtXL5aHN/AONu29NGzIXSVFh9K0feA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitest/pretty-format": "3.0.3",
|
"@vitest/pretty-format": "3.0.5",
|
||||||
"magic-string": "^0.30.17",
|
"magic-string": "^0.30.17",
|
||||||
"pathe": "^2.0.1"
|
"pathe": "^2.0.2"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://opencollective.com/vitest"
|
"url": "https://opencollective.com/vitest"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/spy": {
|
"node_modules/@vitest/spy": {
|
||||||
"version": "3.0.3",
|
"version": "3.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.5.tgz",
|
||||||
"integrity": "sha512-7/dgux8ZBbF7lEIKNnEqQlyRaER9nkAL9eTmdKJkDO3hS8p59ATGwKOCUDHcBLKr7h/oi/6hP+7djQk8049T2A==",
|
"integrity": "sha512-5fOzHj0WbUNqPK6blI/8VzZdkBlQLnT25knX0r4dbZI9qoZDf3qAdjoMmDcLG5A83W6oUUFJgUd0EYBc2P5xqg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -1822,13 +1822,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/utils": {
|
"node_modules/@vitest/utils": {
|
||||||
"version": "3.0.3",
|
"version": "3.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.5.tgz",
|
||||||
"integrity": "sha512-f+s8CvyzPtMFY1eZKkIHGhPsQgYo5qCm6O8KZoim9qm1/jT64qBgGpO5tHscNH6BzRHM+edLNOP+3vO8+8pE/A==",
|
"integrity": "sha512-N9AX0NUoUtVwKwy21JtwzaqR5L5R5A99GAbrHfCCXK1lp593i/3AZAXhSP43wRQuxYsflrdzEfXZFo1reR1Nkg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitest/pretty-format": "3.0.3",
|
"@vitest/pretty-format": "3.0.5",
|
||||||
"loupe": "^3.1.2",
|
"loupe": "^3.1.2",
|
||||||
"tinyrainbow": "^2.0.0"
|
"tinyrainbow": "^2.0.0"
|
||||||
},
|
},
|
||||||
@@ -2334,18 +2334,18 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint": {
|
"node_modules/eslint": {
|
||||||
"version": "9.18.0",
|
"version": "9.20.1",
|
||||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.20.1.tgz",
|
||||||
"integrity": "sha512-+waTfRWQlSbpt3KWE+CjrPPYnbq9kfZIYUqapc0uBXyjTp8aYXZDsUH16m39Ryq3NjAVP4tjuF7KaukeqoCoaA==",
|
"integrity": "sha512-m1mM33o6dBUjxl2qb6wv6nGNwCAsns1eKtaQ4l/NPHeTvhiUPbtdfMyktxN4B3fgHIgsYh1VT3V9txblpQHq+g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.2.0",
|
"@eslint-community/eslint-utils": "^4.2.0",
|
||||||
"@eslint-community/regexpp": "^4.12.1",
|
"@eslint-community/regexpp": "^4.12.1",
|
||||||
"@eslint/config-array": "^0.19.0",
|
"@eslint/config-array": "^0.19.0",
|
||||||
"@eslint/core": "^0.10.0",
|
"@eslint/core": "^0.11.0",
|
||||||
"@eslint/eslintrc": "^3.2.0",
|
"@eslint/eslintrc": "^3.2.0",
|
||||||
"@eslint/js": "9.18.0",
|
"@eslint/js": "9.20.0",
|
||||||
"@eslint/plugin-kit": "^0.2.5",
|
"@eslint/plugin-kit": "^0.2.5",
|
||||||
"@humanfs/node": "^0.16.6",
|
"@humanfs/node": "^0.16.6",
|
||||||
"@humanwhocodes/module-importer": "^1.0.1",
|
"@humanwhocodes/module-importer": "^1.0.1",
|
||||||
@@ -2407,9 +2407,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint-plugin-prettier": {
|
"node_modules/eslint-plugin-prettier": {
|
||||||
"version": "5.2.2",
|
"version": "5.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.3.tgz",
|
||||||
"integrity": "sha512-1yI3/hf35wmlq66C8yOyrujQnel+v5l1Vop5Cl2I6ylyNTT1JbuUUnV3/41PzwTzcyDp/oF0jWE3HXvcH5AQOQ==",
|
"integrity": "sha512-qJ+y0FfCp/mQYQ/vWQ3s7eUlFEL4PyKfAJxsnYTJ4YT73nsJBWqmEpFryxV9OeUiqmsTsYJ5Y+KDNaeP31wrRw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -2500,6 +2500,19 @@
|
|||||||
"url": "https://opencollective.com/eslint"
|
"url": "https://opencollective.com/eslint"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/eslint/node_modules/@eslint/core": {
|
||||||
|
"version": "0.11.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.11.0.tgz",
|
||||||
|
"integrity": "sha512-DWUB2pksgNEb6Bz2fggIy1wh6fGgZP4Xyy/Mt0QZPiloKKXerbqq9D3SBQTlCRYOrcRPu4vuz+CGjwdfqxnoWA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/json-schema": "^7.0.15"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/eslint/node_modules/brace-expansion": {
|
"node_modules/eslint/node_modules/brace-expansion": {
|
||||||
"version": "1.1.11",
|
"version": "1.1.11",
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||||
@@ -2685,9 +2698,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/fastq": {
|
"node_modules/fastq": {
|
||||||
"version": "1.18.0",
|
"version": "1.19.0",
|
||||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.0.tgz",
|
||||||
"integrity": "sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==",
|
"integrity": "sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA==",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"reusify": "^1.0.4"
|
"reusify": "^1.0.4"
|
||||||
@@ -2828,9 +2841,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/globals": {
|
"node_modules/globals": {
|
||||||
"version": "15.14.0",
|
"version": "15.15.0",
|
||||||
"resolved": "https://registry.npmjs.org/globals/-/globals-15.14.0.tgz",
|
"resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz",
|
||||||
"integrity": "sha512-OkToC372DtlQeje9/zHIo5CT8lRP/FUgEOKBEhU4e0abL7J7CD24fD9ohiLN5hagG/kWCYj4K5oaxxtj2Z0Dig==",
|
"integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -3180,9 +3193,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/loupe": {
|
"node_modules/loupe": {
|
||||||
"version": "3.1.2",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.3.tgz",
|
||||||
"integrity": "sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==",
|
"integrity": "sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
@@ -3285,9 +3298,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/mock-fs": {
|
"node_modules/mock-fs": {
|
||||||
"version": "5.4.1",
|
"version": "5.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-5.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-5.5.0.tgz",
|
||||||
"integrity": "sha512-sz/Q8K1gXXXHR+qr0GZg2ysxCRr323kuN10O7CtQjraJsFDJ4SJ+0I5MzALz7aRp9lHk8Cc/YdsT95h9Ka1aFw==",
|
"integrity": "sha512-d/P1M/RacgM3dB0sJ8rjeRNXxtapkPCUnMGmIN0ixJ16F/E4GUZCvWcSGfWGz8eaXYvn1s9baUwNjI4LOPEjiA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -3569,9 +3582,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/prettier": {
|
"node_modules/prettier": {
|
||||||
"version": "3.4.2",
|
"version": "3.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.1.tgz",
|
||||||
"integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==",
|
"integrity": "sha512-hPpFQvHwL3Qv5AdRvBFMhnKo4tYxp0ReXiPn2bxkiohEX6mBeBwEpBSQTkD458RaaDKQMYSp4hX4UtfUTA5wDw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"bin": {
|
"bin": {
|
||||||
@@ -4166,9 +4179,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ts-api-utils": {
|
"node_modules/ts-api-utils": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.1.tgz",
|
||||||
"integrity": "sha512-xCt/TOAc+EOHS1XPnijD3/yzpH6qg2xppZO1YDqGoVsNXfQfzHpOdNuXwrwOU8u4ITXJyDCTyt8w5g1sZv9ynQ==",
|
"integrity": "sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -4288,15 +4301,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "6.0.11",
|
"version": "6.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-6.0.11.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-6.1.0.tgz",
|
||||||
"integrity": "sha512-4VL9mQPKoHy4+FE0NnRE/kbY51TOfaknxAjt3fJbGJxhIpBZiqVzlZDEesWWsuREXHwNdAoOFZ9MkPEVXczHwg==",
|
"integrity": "sha512-RjjMipCKVoR4hVfPY6GQTgveinjNuyLw+qruksLDvA5ktI1150VmcMBKmQaEWJhg/j6Uaf6dNCNA0AfdzUb/hQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.24.2",
|
"esbuild": "^0.24.2",
|
||||||
"postcss": "^8.4.49",
|
"postcss": "^8.5.1",
|
||||||
"rollup": "^4.23.0"
|
"rollup": "^4.30.1"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"vite": "bin/vite.js"
|
"vite": "bin/vite.js"
|
||||||
@@ -4360,16 +4373,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vite-node": {
|
"node_modules/vite-node": {
|
||||||
"version": "3.0.3",
|
"version": "3.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.0.5.tgz",
|
||||||
"integrity": "sha512-0sQcwhwAEw/UJGojbhOrnq3HtiZ3tC7BzpAa0lx3QaTX0S3YX70iGcik25UBdB96pmdwjyY2uyKNYruxCDmiEg==",
|
"integrity": "sha512-02JEJl7SbtwSDJdYS537nU6l+ktdvcREfLksk/NDAqtdKWGqHl+joXzEubHROmS3E6pip+Xgu2tFezMu75jH7A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cac": "^6.7.14",
|
"cac": "^6.7.14",
|
||||||
"debug": "^4.4.0",
|
"debug": "^4.4.0",
|
||||||
"es-module-lexer": "^1.6.0",
|
"es-module-lexer": "^1.6.0",
|
||||||
"pathe": "^2.0.1",
|
"pathe": "^2.0.2",
|
||||||
"vite": "^5.0.0 || ^6.0.0"
|
"vite": "^5.0.0 || ^6.0.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
@@ -4403,31 +4416,31 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vitest": {
|
"node_modules/vitest": {
|
||||||
"version": "3.0.3",
|
"version": "3.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.5.tgz",
|
||||||
"integrity": "sha512-dWdwTFUW9rcnL0LyF2F+IfvNQWB0w9DERySCk8VMG75F8k25C7LsZoh6XfCjPvcR8Nb+Lqi9JKr6vnzH7HSrpQ==",
|
"integrity": "sha512-4dof+HvqONw9bvsYxtkfUp2uHsTN9bV2CZIi1pWgoFpL1Lld8LA1ka9q/ONSsoScAKG7NVGf2stJTI7XRkXb2Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitest/expect": "3.0.3",
|
"@vitest/expect": "3.0.5",
|
||||||
"@vitest/mocker": "3.0.3",
|
"@vitest/mocker": "3.0.5",
|
||||||
"@vitest/pretty-format": "^3.0.3",
|
"@vitest/pretty-format": "^3.0.5",
|
||||||
"@vitest/runner": "3.0.3",
|
"@vitest/runner": "3.0.5",
|
||||||
"@vitest/snapshot": "3.0.3",
|
"@vitest/snapshot": "3.0.5",
|
||||||
"@vitest/spy": "3.0.3",
|
"@vitest/spy": "3.0.5",
|
||||||
"@vitest/utils": "3.0.3",
|
"@vitest/utils": "3.0.5",
|
||||||
"chai": "^5.1.2",
|
"chai": "^5.1.2",
|
||||||
"debug": "^4.4.0",
|
"debug": "^4.4.0",
|
||||||
"expect-type": "^1.1.0",
|
"expect-type": "^1.1.0",
|
||||||
"magic-string": "^0.30.17",
|
"magic-string": "^0.30.17",
|
||||||
"pathe": "^2.0.1",
|
"pathe": "^2.0.2",
|
||||||
"std-env": "^3.8.0",
|
"std-env": "^3.8.0",
|
||||||
"tinybench": "^2.9.0",
|
"tinybench": "^2.9.0",
|
||||||
"tinyexec": "^0.3.2",
|
"tinyexec": "^0.3.2",
|
||||||
"tinypool": "^1.0.2",
|
"tinypool": "^1.0.2",
|
||||||
"tinyrainbow": "^2.0.0",
|
"tinyrainbow": "^2.0.0",
|
||||||
"vite": "^5.0.0 || ^6.0.0",
|
"vite": "^5.0.0 || ^6.0.0",
|
||||||
"vite-node": "3.0.3",
|
"vite-node": "3.0.5",
|
||||||
"why-is-node-running": "^2.3.0"
|
"why-is-node-running": "^2.3.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
@@ -4441,9 +4454,10 @@
|
|||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@edge-runtime/vm": "*",
|
"@edge-runtime/vm": "*",
|
||||||
|
"@types/debug": "^4.1.12",
|
||||||
"@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
|
"@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
|
||||||
"@vitest/browser": "3.0.3",
|
"@vitest/browser": "3.0.5",
|
||||||
"@vitest/ui": "3.0.3",
|
"@vitest/ui": "3.0.5",
|
||||||
"happy-dom": "*",
|
"happy-dom": "*",
|
||||||
"jsdom": "*"
|
"jsdom": "*"
|
||||||
},
|
},
|
||||||
@@ -4451,6 +4465,9 @@
|
|||||||
"@edge-runtime/vm": {
|
"@edge-runtime/vm": {
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
|
"@types/debug": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"@types/node": {
|
"@types/node": {
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@immich/cli",
|
"name": "@immich/cli",
|
||||||
"version": "2.2.47",
|
"version": "2.2.50",
|
||||||
"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",
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
"@types/cli-progress": "^3.11.0",
|
"@types/cli-progress": "^3.11.0",
|
||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"@types/mock-fs": "^4.13.1",
|
"@types/mock-fs": "^4.13.1",
|
||||||
"@types/node": "^22.10.9",
|
"@types/node": "^22.13.2",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.15.0",
|
"@typescript-eslint/eslint-plugin": "^8.15.0",
|
||||||
"@typescript-eslint/parser": "^8.15.0",
|
"@typescript-eslint/parser": "^8.15.0",
|
||||||
"@vitest/coverage-v8": "^3.0.0",
|
"@vitest/coverage-v8": "^3.0.0",
|
||||||
@@ -67,6 +67,6 @@
|
|||||||
"lodash-es": "^4.17.21"
|
"lodash-es": "^4.17.21"
|
||||||
},
|
},
|
||||||
"volta": {
|
"volta": {
|
||||||
"node": "22.13.1"
|
"node": "22.14.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,37 +2,37 @@
|
|||||||
# Manual edits may be lost in future updates.
|
# Manual edits may be lost in future updates.
|
||||||
|
|
||||||
provider "registry.opentofu.org/cloudflare/cloudflare" {
|
provider "registry.opentofu.org/cloudflare/cloudflare" {
|
||||||
version = "4.50.0"
|
version = "4.52.0"
|
||||||
constraints = "4.50.0"
|
constraints = "4.52.0"
|
||||||
hashes = [
|
hashes = [
|
||||||
"h1:0qvD5ZKn2tMZ8cOjQrUSITIC9tKCZbrSaSswV9lOyiU=",
|
"h1:2BEJyXJtYC4B4nda/WCYUmuJYDaYk88F8t1pwPzr0iQ=",
|
||||||
"h1:4N0gplrZ0zOsJv3Kx1VfIx2FwrZHbYU0Un2yfiLZIGQ=",
|
"h1:4IASk5SESeWKQ7JU0+M7KApuF5mZyklvwMXPBabim3c=",
|
||||||
"h1:81AMQq4kNKU/35U8ElQegUxG4E6xB0erIjG5xVmjIyo=",
|
"h1:5ImZxxALSnWfH/4EXw/wFirSmk5Tr0ACmcysy51AafE=",
|
||||||
"h1:EEQNADUmV3IL6x00yzy04i7OCSLeOMgM9XQkV3w71gA=",
|
"h1:6TJ3dxLSin4ZKBJLsZDn95H2ZYnGm8S7GGHvvXuuMQU=",
|
||||||
"h1:HD0KI7td6oiSSAnJNn8UPSGf+hKiTo4JVQYfAiU1SqM=",
|
"h1:IzTUjg9kQ4N3qizP9CjYLeHwjsuGgtxwXvfUQWyOLcA=",
|
||||||
"h1:Hl+o5LtcvZg2f3l1hh9vaG/DFK6k+dTIZSeM0lXyfpo=",
|
"h1:NTaOQfYINA0YTG/V1/9+SYtgX1it63+cBugj4WK4FWc=",
|
||||||
"h1:ZUO2oIJ6jtZdvl816h0cEIiIeZ/fFCF64+abGEVxZZM=",
|
"h1:PXH48LuJn329sCfMXprdMDk51EZaWFyajVvS03qhQLs=",
|
||||||
"h1:Zio80fnEeUKdlSOhTVskMEFSLUQ6TMsMKnXc+Dy2P2A=",
|
"h1:Pi5M+GeoMSN2eJ6QnIeXjBf19O+rby/74CfB2ocpv20=",
|
||||||
"h1:aLLvg36evTyqjtXGV2MjAV8imktXFmry7p/xCu9GQC4=",
|
"h1:ShXZ2ZjBvm3thfoPPzPT8+OhyismnydQVkUAfI8X12w=",
|
||||||
"h1:azL05eWyy2V8SWkbZZImPWvv8ynG4eqmrbZhjXBDFug=",
|
"h1:WQ9hu0Wge2msBbODfottCSKgu8oKUrw4Opz+fDPVVHk=",
|
||||||
"h1:ckMysHY4fJmr7o58XMi+DdgOTB/U/Mf1u1JA9ly3g/I=",
|
"h1:Z5yXML2DE0uH9UU+M0ut9JMQAORcwVZz1CxBHzeBmao=",
|
||||||
"h1:jxOwjDNjt5WCb4YjjiMsman91O8Y+MAPz6UwJ4a6F+0=",
|
"h1:jqI2qKknpleS3JDSplyGYHMu0u9K/tor1ZOjFwDgEMk=",
|
||||||
"h1:u4OfnjSLa4Wk1IUFAzrvMnGgr8MvRHEWVDHEScPK2E8=",
|
"h1:kgfutDh14Q5nw4eg6qGFamFxIiY8Ae0FPKRBLDOzpcI=",
|
||||||
"h1:wQkR1oeSkzlHn3rnVuLJRJLBHlg4EHt7Y64DeTjfkjQ=",
|
"h1:zCAO7GZmfYhWb+i6TfqlqhMeDyPZWGio2IzEzAh3YTs=",
|
||||||
"zh:0ef99ed39472a94e6a0d6fa733cf0a46bce3bf66eba2873efae8846efdddc237",
|
"zh:19be1a91c982b902c42aba47766860dfa5dc151eed1e95fd39ca642229381ef0",
|
||||||
"zh:2929cbbffcead171d45c88e4a7a59e9c013ea775dafa68b10da8db7cd04b6140",
|
"zh:1de451c4d1ecf7efbe67b6dace3426ba810711afdd644b0f1b870364c8ae91f8",
|
||||||
"zh:462601c87118088e1a718842e367af7d8e7620598d426980a6d6b33de759865e",
|
"zh:352b4a2120173298622e669258744554339d959ac3a95607b117a48ee4a83238",
|
||||||
"zh:56766eb62a74a9d88d9efb8486dd3a0c5c9db873d0a980ae9ef1e8af27d74231",
|
"zh:3c6f1346d9154afbd2d558fabb4b0150fc8d559aa961254144fe1bc17fe6032f",
|
||||||
"zh:6b4e8810d99498a5a20a5872982a0f1354e79cfc4a7dfe7cc656f1c7eaae47d8",
|
"zh:4c4c92d53fb535b1e0eff26f222bbd627b97d3b4c891ec9c321268676d06152f",
|
||||||
"zh:6d65bdb4ec94b6eecc8abe26d94e2ca09262dc1e7a9934db829f418be0119920",
|
"zh:53276f68006c9ceb7cdb10a6ccf91a5c1eadd1407a28edb5741e84e88d7e29e8",
|
||||||
"zh:71adeaf31e41a358ec6095004062e43f56ee7d4b2504e5613ab351d511695641",
|
"zh:7925a97773948171a63d4f65bb81ee92fd6d07a447e36012977313293a5435c9",
|
||||||
|
"zh:7dfb0a4496cfe032437386d0a2cd9229a1956e9c30bd920923c141b0f0440060",
|
||||||
"zh:890df766e9b839623b1f0437355032a3c006226a6c200cd911e15ee1a9014e9f",
|
"zh:890df766e9b839623b1f0437355032a3c006226a6c200cd911e15ee1a9014e9f",
|
||||||
"zh:89761c15908ccc2cf9c50bb5cb3be45d3ad0c45fc7c608c6b95f48c0288b7160",
|
"zh:8d4aa79f0a414bb4163d771063c70cd991c8fac6c766e685bac2ee12903c5bd6",
|
||||||
"zh:8cc5d7c5939da89cfd01f3e51c84f3576564783acea9db86bd9e32049805ed96",
|
"zh:a67540c13565616a7e7e51ee9366e88b0dc60046e1d75c72680e150bd02725bb",
|
||||||
"zh:987cff8225b1dd436cdcb4fc6228689ae7e4281de6896412a2a9a3325c49f05e",
|
"zh:a936383a4767f5393f38f622e92bf2d0c03fe04b69c284951f27345766c7b31b",
|
||||||
"zh:991e83ebb89867d71e01a1c215ed159efb425683b0a44707be8579eb0a337f06",
|
"zh:d4887d73c466ff036eecf50ad6404ba38fd82ea4855296b1846d244b0f13c380",
|
||||||
"zh:ab8177ae2d8f5cfa90043a6f867435012cae115f6061b832a7e2462e0ae87a67",
|
"zh:e9093c8bd5b6cd99c81666e315197791781b8f93afa14fc2e0f732d1bb2a44b7",
|
||||||
"zh:d1ca34df1398f201274a6a18102975148c10ca15aa43cfc56cc9897620929509",
|
"zh:efd3b3f1ec59a37f635aa1d4efcf178734c2fcf8ddb0d56ea690bec342da8672",
|
||||||
"zh:d34946f70201baf6dda03e3b294c6bbe40d95d0278e97b9f636ded94822b24ac",
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ terraform {
|
|||||||
required_providers {
|
required_providers {
|
||||||
cloudflare = {
|
cloudflare = {
|
||||||
source = "cloudflare/cloudflare"
|
source = "cloudflare/cloudflare"
|
||||||
version = "4.50.0"
|
version = "4.52.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,37 +2,37 @@
|
|||||||
# Manual edits may be lost in future updates.
|
# Manual edits may be lost in future updates.
|
||||||
|
|
||||||
provider "registry.opentofu.org/cloudflare/cloudflare" {
|
provider "registry.opentofu.org/cloudflare/cloudflare" {
|
||||||
version = "4.50.0"
|
version = "4.52.0"
|
||||||
constraints = "4.50.0"
|
constraints = "4.52.0"
|
||||||
hashes = [
|
hashes = [
|
||||||
"h1:0qvD5ZKn2tMZ8cOjQrUSITIC9tKCZbrSaSswV9lOyiU=",
|
"h1:2BEJyXJtYC4B4nda/WCYUmuJYDaYk88F8t1pwPzr0iQ=",
|
||||||
"h1:4N0gplrZ0zOsJv3Kx1VfIx2FwrZHbYU0Un2yfiLZIGQ=",
|
"h1:4IASk5SESeWKQ7JU0+M7KApuF5mZyklvwMXPBabim3c=",
|
||||||
"h1:81AMQq4kNKU/35U8ElQegUxG4E6xB0erIjG5xVmjIyo=",
|
"h1:5ImZxxALSnWfH/4EXw/wFirSmk5Tr0ACmcysy51AafE=",
|
||||||
"h1:EEQNADUmV3IL6x00yzy04i7OCSLeOMgM9XQkV3w71gA=",
|
"h1:6TJ3dxLSin4ZKBJLsZDn95H2ZYnGm8S7GGHvvXuuMQU=",
|
||||||
"h1:HD0KI7td6oiSSAnJNn8UPSGf+hKiTo4JVQYfAiU1SqM=",
|
"h1:IzTUjg9kQ4N3qizP9CjYLeHwjsuGgtxwXvfUQWyOLcA=",
|
||||||
"h1:Hl+o5LtcvZg2f3l1hh9vaG/DFK6k+dTIZSeM0lXyfpo=",
|
"h1:NTaOQfYINA0YTG/V1/9+SYtgX1it63+cBugj4WK4FWc=",
|
||||||
"h1:ZUO2oIJ6jtZdvl816h0cEIiIeZ/fFCF64+abGEVxZZM=",
|
"h1:PXH48LuJn329sCfMXprdMDk51EZaWFyajVvS03qhQLs=",
|
||||||
"h1:Zio80fnEeUKdlSOhTVskMEFSLUQ6TMsMKnXc+Dy2P2A=",
|
"h1:Pi5M+GeoMSN2eJ6QnIeXjBf19O+rby/74CfB2ocpv20=",
|
||||||
"h1:aLLvg36evTyqjtXGV2MjAV8imktXFmry7p/xCu9GQC4=",
|
"h1:ShXZ2ZjBvm3thfoPPzPT8+OhyismnydQVkUAfI8X12w=",
|
||||||
"h1:azL05eWyy2V8SWkbZZImPWvv8ynG4eqmrbZhjXBDFug=",
|
"h1:WQ9hu0Wge2msBbODfottCSKgu8oKUrw4Opz+fDPVVHk=",
|
||||||
"h1:ckMysHY4fJmr7o58XMi+DdgOTB/U/Mf1u1JA9ly3g/I=",
|
"h1:Z5yXML2DE0uH9UU+M0ut9JMQAORcwVZz1CxBHzeBmao=",
|
||||||
"h1:jxOwjDNjt5WCb4YjjiMsman91O8Y+MAPz6UwJ4a6F+0=",
|
"h1:jqI2qKknpleS3JDSplyGYHMu0u9K/tor1ZOjFwDgEMk=",
|
||||||
"h1:u4OfnjSLa4Wk1IUFAzrvMnGgr8MvRHEWVDHEScPK2E8=",
|
"h1:kgfutDh14Q5nw4eg6qGFamFxIiY8Ae0FPKRBLDOzpcI=",
|
||||||
"h1:wQkR1oeSkzlHn3rnVuLJRJLBHlg4EHt7Y64DeTjfkjQ=",
|
"h1:zCAO7GZmfYhWb+i6TfqlqhMeDyPZWGio2IzEzAh3YTs=",
|
||||||
"zh:0ef99ed39472a94e6a0d6fa733cf0a46bce3bf66eba2873efae8846efdddc237",
|
"zh:19be1a91c982b902c42aba47766860dfa5dc151eed1e95fd39ca642229381ef0",
|
||||||
"zh:2929cbbffcead171d45c88e4a7a59e9c013ea775dafa68b10da8db7cd04b6140",
|
"zh:1de451c4d1ecf7efbe67b6dace3426ba810711afdd644b0f1b870364c8ae91f8",
|
||||||
"zh:462601c87118088e1a718842e367af7d8e7620598d426980a6d6b33de759865e",
|
"zh:352b4a2120173298622e669258744554339d959ac3a95607b117a48ee4a83238",
|
||||||
"zh:56766eb62a74a9d88d9efb8486dd3a0c5c9db873d0a980ae9ef1e8af27d74231",
|
"zh:3c6f1346d9154afbd2d558fabb4b0150fc8d559aa961254144fe1bc17fe6032f",
|
||||||
"zh:6b4e8810d99498a5a20a5872982a0f1354e79cfc4a7dfe7cc656f1c7eaae47d8",
|
"zh:4c4c92d53fb535b1e0eff26f222bbd627b97d3b4c891ec9c321268676d06152f",
|
||||||
"zh:6d65bdb4ec94b6eecc8abe26d94e2ca09262dc1e7a9934db829f418be0119920",
|
"zh:53276f68006c9ceb7cdb10a6ccf91a5c1eadd1407a28edb5741e84e88d7e29e8",
|
||||||
"zh:71adeaf31e41a358ec6095004062e43f56ee7d4b2504e5613ab351d511695641",
|
"zh:7925a97773948171a63d4f65bb81ee92fd6d07a447e36012977313293a5435c9",
|
||||||
|
"zh:7dfb0a4496cfe032437386d0a2cd9229a1956e9c30bd920923c141b0f0440060",
|
||||||
"zh:890df766e9b839623b1f0437355032a3c006226a6c200cd911e15ee1a9014e9f",
|
"zh:890df766e9b839623b1f0437355032a3c006226a6c200cd911e15ee1a9014e9f",
|
||||||
"zh:89761c15908ccc2cf9c50bb5cb3be45d3ad0c45fc7c608c6b95f48c0288b7160",
|
"zh:8d4aa79f0a414bb4163d771063c70cd991c8fac6c766e685bac2ee12903c5bd6",
|
||||||
"zh:8cc5d7c5939da89cfd01f3e51c84f3576564783acea9db86bd9e32049805ed96",
|
"zh:a67540c13565616a7e7e51ee9366e88b0dc60046e1d75c72680e150bd02725bb",
|
||||||
"zh:987cff8225b1dd436cdcb4fc6228689ae7e4281de6896412a2a9a3325c49f05e",
|
"zh:a936383a4767f5393f38f622e92bf2d0c03fe04b69c284951f27345766c7b31b",
|
||||||
"zh:991e83ebb89867d71e01a1c215ed159efb425683b0a44707be8579eb0a337f06",
|
"zh:d4887d73c466ff036eecf50ad6404ba38fd82ea4855296b1846d244b0f13c380",
|
||||||
"zh:ab8177ae2d8f5cfa90043a6f867435012cae115f6061b832a7e2462e0ae87a67",
|
"zh:e9093c8bd5b6cd99c81666e315197791781b8f93afa14fc2e0f732d1bb2a44b7",
|
||||||
"zh:d1ca34df1398f201274a6a18102975148c10ca15aa43cfc56cc9897620929509",
|
"zh:efd3b3f1ec59a37f635aa1d4efcf178734c2fcf8ddb0d56ea690bec342da8672",
|
||||||
"zh:d34946f70201baf6dda03e3b294c6bbe40d95d0278e97b9f636ded94822b24ac",
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ terraform {
|
|||||||
required_providers {
|
required_providers {
|
||||||
cloudflare = {
|
cloudflare = {
|
||||||
source = "cloudflare/cloudflare"
|
source = "cloudflare/cloudflare"
|
||||||
version = "4.50.0"
|
version = "4.52.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,13 @@
|
|||||||
# See:
|
#
|
||||||
|
# WARNING: To install Immich, follow our guide: https://immich.app/docs/install/docker-compose
|
||||||
|
#
|
||||||
|
# Make sure to use the docker-compose.yml of the current release:
|
||||||
|
#
|
||||||
|
# https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml
|
||||||
|
#
|
||||||
|
# The compose file on main may not be compatible with the latest release.
|
||||||
|
|
||||||
|
# For development see:
|
||||||
# - https://immich.app/docs/developer/setup
|
# - https://immich.app/docs/developer/setup
|
||||||
# - https://immich.app/docs/developer/troubleshooting
|
# - https://immich.app/docs/developer/troubleshooting
|
||||||
|
|
||||||
@@ -107,7 +116,7 @@ services:
|
|||||||
|
|
||||||
redis:
|
redis:
|
||||||
container_name: immich_redis
|
container_name: immich_redis
|
||||||
image: redis:6.2-alpine@sha256:905c4ee67b8e0aa955331960d2aa745781e6bd89afc44a8584bfd13bc890f0ae
|
image: redis:6.2-alpine@sha256:148bb5411c184abd288d9aaed139c98123eeb8824c5d3fce03cf721db58066d8
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: redis-cli ping || exit 1
|
test: redis-cli ping || exit 1
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,12 @@
|
|||||||
|
#
|
||||||
|
# WARNING: To install Immich, follow our guide: https://immich.app/docs/install/docker-compose
|
||||||
|
#
|
||||||
|
# Make sure to use the docker-compose.yml of the current release:
|
||||||
|
#
|
||||||
|
# https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml
|
||||||
|
#
|
||||||
|
# The compose file on main may not be compatible with the latest release.
|
||||||
|
|
||||||
name: immich-prod
|
name: immich-prod
|
||||||
|
|
||||||
services:
|
services:
|
||||||
@@ -47,7 +56,7 @@ services:
|
|||||||
|
|
||||||
redis:
|
redis:
|
||||||
container_name: immich_redis
|
container_name: immich_redis
|
||||||
image: redis:6.2-alpine@sha256:905c4ee67b8e0aa955331960d2aa745781e6bd89afc44a8584bfd13bc890f0ae
|
image: redis:6.2-alpine@sha256:148bb5411c184abd288d9aaed139c98123eeb8824c5d3fce03cf721db58066d8
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: redis-cli ping || exit 1
|
test: redis-cli ping || exit 1
|
||||||
restart: always
|
restart: always
|
||||||
@@ -91,7 +100,7 @@ services:
|
|||||||
container_name: immich_prometheus
|
container_name: immich_prometheus
|
||||||
ports:
|
ports:
|
||||||
- 9090:9090
|
- 9090:9090
|
||||||
image: prom/prometheus@sha256:6559acbd5d770b15bb3c954629ce190ac3cbbdb2b7f1c30f0385c4e05104e218
|
image: prom/prometheus@sha256:5888c188cf09e3f7eebc97369c3b2ce713e844cdbd88ccf36f5047c958aea120
|
||||||
volumes:
|
volumes:
|
||||||
- ./prometheus.yml:/etc/prometheus/prometheus.yml
|
- ./prometheus.yml:/etc/prometheus/prometheus.yml
|
||||||
- prometheus-data:/prometheus
|
- prometheus-data:/prometheus
|
||||||
@@ -103,7 +112,7 @@ services:
|
|||||||
command: ['./run.sh', '-disable-reporting']
|
command: ['./run.sh', '-disable-reporting']
|
||||||
ports:
|
ports:
|
||||||
- 3000:3000
|
- 3000:3000
|
||||||
image: grafana/grafana:11.4.0-ubuntu@sha256:afccec22ba0e4815cca1d2bf3836e414322390dc78d77f1851976ffa8d61051c
|
image: grafana/grafana:11.5.1-ubuntu@sha256:9a4ab78cec1a2ec7d1ca5dfd5aacec6412706a1bc9e971fc7184e2f6696a63f5
|
||||||
volumes:
|
volumes:
|
||||||
- grafana-data:/var/lib/grafana
|
- grafana-data:/var/lib/grafana
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
#
|
#
|
||||||
# WARNING: Make sure to use the docker-compose.yml of the current release:
|
# WARNING: To install Immich, follow our guide: https://immich.app/docs/install/docker-compose
|
||||||
|
#
|
||||||
|
# Make sure to use the docker-compose.yml of the current release:
|
||||||
#
|
#
|
||||||
# https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml
|
# https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml
|
||||||
#
|
#
|
||||||
# The compose file on main may not be compatible with the latest release.
|
# The compose file on main may not be compatible with the latest release.
|
||||||
#
|
|
||||||
|
|
||||||
name: immich
|
name: immich
|
||||||
|
|
||||||
@@ -48,7 +49,7 @@ services:
|
|||||||
|
|
||||||
redis:
|
redis:
|
||||||
container_name: immich_redis
|
container_name: immich_redis
|
||||||
image: docker.io/redis:6.2-alpine@sha256:905c4ee67b8e0aa955331960d2aa745781e6bd89afc44a8584bfd13bc890f0ae
|
image: docker.io/redis:6.2-alpine@sha256:148bb5411c184abd288d9aaed139c98123eeb8824c5d3fce03cf721db58066d8
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: redis-cli ping || exit 1
|
test: redis-cli ping || exit 1
|
||||||
restart: always
|
restart: always
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ services:
|
|||||||
vaapi-wsl: # use this for VAAPI if you're running Immich in WSL2
|
vaapi-wsl: # use this for VAAPI if you're running Immich in WSL2
|
||||||
devices:
|
devices:
|
||||||
- /dev/dri:/dev/dri
|
- /dev/dri:/dev/dri
|
||||||
|
- /dev/dxg:/dev/dxg
|
||||||
volumes:
|
volumes:
|
||||||
- /usr/lib/wsl:/usr/lib/wsl
|
- /usr/lib/wsl:/usr/lib/wsl
|
||||||
environment:
|
environment:
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
22.13.1
|
22.14.0
|
||||||
|
|||||||
@@ -77,9 +77,7 @@ docker start immich_postgres # Start Postgres server
|
|||||||
sleep 10 # Wait for Postgres server to start up
|
sleep 10 # Wait for Postgres server to start up
|
||||||
docker exec -it immich_postgres bash # Enter the Docker shell and run the following command
|
docker exec -it immich_postgres bash # Enter the Docker shell and run the following command
|
||||||
# Check the database user if you deviated from the default. If your backup ends in `.gz`, replace `cat` with `gunzip`
|
# Check the database user if you deviated from the default. If your backup ends in `.gz`, replace `cat` with `gunzip`
|
||||||
cat < "/dump.sql" \
|
cat < "/dump.sql" | sed "s/SELECT pg_catalog.set_config('search_path', '', false);/SELECT pg_catalog.set_config('search_path', 'public, pg_catalog', true);/g" | psql --dbname=postgres --username=<DB_USERNAME>
|
||||||
| sed "s/SELECT pg_catalog.set_config('search_path', '', false);/SELECT pg_catalog.set_config('search_path', 'public, pg_catalog', true);/g" \
|
|
||||||
| psql --dbname=postgres --username=<DB_USERNAME> # Restore Backup
|
|
||||||
exit # Exit the Docker shell
|
exit # Exit the Docker shell
|
||||||
docker compose up -d # Start remainder of Immich apps
|
docker compose up -d # Start remainder of Immich apps
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -70,4 +70,4 @@ When installing a new version of pgvecto.rs, you will need to manually update th
|
|||||||
|
|
||||||
If you get the error `driverError: error: permission denied for view pg_vector_index_stat`, you can fix this by connecting to the Immich database and running `GRANT SELECT ON TABLE pg_vector_index_stat TO <immichdbusername>;`.
|
If you get the error `driverError: error: permission denied for view pg_vector_index_stat`, you can fix this by connecting to the Immich database and running `GRANT SELECT ON TABLE pg_vector_index_stat TO <immichdbusername>;`.
|
||||||
|
|
||||||
[vectors-install]: https://docs.pgvecto.rs/getting-started/installation.html
|
[vectors-install]: https://docs.vectorchord.ai/getting-started/installation.html
|
||||||
|
|||||||
@@ -50,19 +50,18 @@ The Immich CLI is an [npm](https://www.npmjs.com/) package that lets users contr
|
|||||||
|
|
||||||
The Immich backend is divided into several services, which are run as individual docker containers.
|
The Immich backend is divided into several services, which are run as individual docker containers.
|
||||||
|
|
||||||
1. `immich-server` - Handle and respond to REST API requests
|
1. `immich-server` - Handle and respond to REST API requests, execute background jobs (thumbnail generation, metadata extraction, transcoding, etc.)
|
||||||
1. `immich-microservices` - Execute background jobs (thumbnail generation, metadata extraction, transcoding, etc.)
|
|
||||||
1. `immich-machine-learning` - Execute machine learning models
|
1. `immich-machine-learning` - Execute machine learning models
|
||||||
1. `postgres` - Persistent data storage
|
1. `postgres` - Persistent data storage
|
||||||
1. `redis`- Queue management for `immich-microservices`
|
1. `redis`- Queue management for background jobs
|
||||||
|
|
||||||
### Immich Server
|
### Immich Server
|
||||||
|
|
||||||
The Immich Server is a [TypeScript](https://www.typescriptlang.org/) project written for [Node.js](https://nodejs.org/). It uses the [Nest.js](https://nestjs.com) framework, with [TypeORM](https://typeorm.io/) for database management. The server codebase also loosely follows the [Hexagonal Architecture](<https://en.wikipedia.org/wiki/Hexagonal_architecture_(software)>). Specifically, we aim to separate technology specific implementations (`infra/`) from core business logic (`domain/`).
|
The Immich Server is a [TypeScript](https://www.typescriptlang.org/) project written for [Node.js](https://nodejs.org/). It uses the [Nest.js](https://nestjs.com) framework, [Express](https://expressjs.com/) server, and the query builder [Kysely](https://kysely.dev/). The server codebase also loosely follows the [Hexagonal Architecture](<https://en.wikipedia.org/wiki/Hexagonal_architecture_(software)>). Specifically, we aim to separate technology specific implementations (`src/repositories`) from core business logic (`src/services`).
|
||||||
|
|
||||||
#### REST Endpoints
|
### API Endpoints
|
||||||
|
|
||||||
The server is a list of HTTP endpoints and associated handlers (controllers). Each controller usually implements the following CRUD operations:
|
An incoming HTTP request is mapped to a controller (`src/controllers`). Controllers are collections of HTTP endpoints. Each controller usually implements the following CRUD operations for its respective resource type:
|
||||||
|
|
||||||
- `POST` `/<type>` - **Create**
|
- `POST` `/<type>` - **Create**
|
||||||
- `GET` `/<type>` - **Read** (all)
|
- `GET` `/<type>` - **Read** (all)
|
||||||
@@ -70,13 +69,13 @@ The server is a list of HTTP endpoints and associated handlers (controllers). Ea
|
|||||||
- `PUT` `/<type>/:id` - **Updated** (by id)
|
- `PUT` `/<type>/:id` - **Updated** (by id)
|
||||||
- `DELETE` `/<type>/:id` - **Delete** (by id)
|
- `DELETE` `/<type>/:id` - **Delete** (by id)
|
||||||
|
|
||||||
#### DTOs
|
### Domain Transfer Objects (DTOs)
|
||||||
|
|
||||||
The server uses [Domain Transfer Objects](https://en.wikipedia.org/wiki/Data_transfer_object) as public interfaces for the inputs (query, params, and body) and outputs (response) for each endpoint. DTOs translate to [OpenAPI](./open-api.md) schemas and control the generated code used by each client.
|
The server uses [Domain Transfer Objects](https://en.wikipedia.org/wiki/Data_transfer_object) as public interfaces for the inputs (query, params, and body) and outputs (response) for each endpoint. DTOs translate to [OpenAPI](./open-api.md) schemas and control the generated code used by each client.
|
||||||
|
|
||||||
### Microservices
|
### Background Jobs
|
||||||
|
|
||||||
The Immich Microservices image uses the same `Dockerfile` as the Immich Server, but with a different entrypoint. The Immich Microservices service mainly handles executing jobs, which include the following:
|
Immich uses a [worker](https://github.com/immich-app/immich/blob/main/server/src/utils/misc.ts#L266) to run background jobs. These jobs include:
|
||||||
|
|
||||||
- Thumbnail Generation
|
- Thumbnail Generation
|
||||||
- Metadata Extraction
|
- Metadata Extraction
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ To see local changes to `@immich/ui` in Immich, do the following:
|
|||||||
|
|
||||||
### Mobile app
|
### Mobile app
|
||||||
|
|
||||||
The mobile app `(/mobile)` will required Flutter toolchain 3.13.x to be installed on your system.
|
The mobile app `(/mobile)` will required Flutter toolchain 3.13.x and FVM to be installed on your system.
|
||||||
|
|
||||||
Please refer to the [Flutter's official documentation](https://flutter.dev/docs/get-started/install) for more information on setting up the toolchain on your machine.
|
Please refer to the [Flutter's official documentation](https://flutter.dev/docs/get-started/install) for more information on setting up the toolchain on your machine.
|
||||||
|
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ If your photos are on a network drive, automatic file watching likely won't work
|
|||||||
|
|
||||||
#### Troubleshooting
|
#### Troubleshooting
|
||||||
|
|
||||||
If you encounter an `ENOSPC` error, you need to increase your file watcher limit. In sysctl, this key is called `fs.inotify.max_user_watched` and has a default value of 8192. Increase this number to a suitable value greater than the number of files you will be watching. Note that Immich has to watch all files in your import paths including any ignored files.
|
If you encounter an `ENOSPC` error, you need to increase your file watcher limit. In sysctl, this key is called `fs.inotify.max_user_watches` and has a default value of 8192. Increase this number to a suitable value greater than the number of files you will be watching. Note that Immich has to watch all files in your import paths including any ignored files.
|
||||||
|
|
||||||
```
|
```
|
||||||
ERROR [LibraryService] Library watcher for library c69faf55-f96d-4aa0-b83b-2d80cbc27d98 encountered error: Error: ENOSPC: System limit for number of file watchers reached, watch '/media/photo.jpg'
|
ERROR [LibraryService] Library watcher for library c69faf55-f96d-4aa0-b83b-2d80cbc27d98 encountered error: Error: ENOSPC: System limit for number of file watchers reached, watch '/media/photo.jpg'
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ You do not need to redo any machine learning jobs after enabling hardware accele
|
|||||||
|
|
||||||
- ARM NN (Mali)
|
- ARM NN (Mali)
|
||||||
- CUDA (NVIDIA GPUs with [compute capability](https://developer.nvidia.com/cuda-gpus) 5.2 or higher)
|
- CUDA (NVIDIA GPUs with [compute capability](https://developer.nvidia.com/cuda-gpus) 5.2 or higher)
|
||||||
- OpenVINO (Intel discrete GPUs such as Iris Xe and Arc)
|
- OpenVINO (Intel GPUs such as Iris Xe and Arc)
|
||||||
|
|
||||||
## Limitations
|
## Limitations
|
||||||
|
|
||||||
@@ -43,8 +43,9 @@ You do not need to redo any machine learning jobs after enabling hardware accele
|
|||||||
|
|
||||||
#### OpenVINO
|
#### OpenVINO
|
||||||
|
|
||||||
- The server must have a discrete GPU, i.e. Iris Xe or Arc. Expect issues when attempting to use integrated graphics.
|
- Integrated GPUs are more likely to experience issues than discrete GPUs, especially for older processors or servers with low RAM.
|
||||||
- Ensure the server's kernel version is new enough to use the device for hardware accceleration.
|
- Ensure the server's kernel version is new enough to use the device for hardware accceleration.
|
||||||
|
- Expect higher RAM usage when using OpenVINO compared to CPU processing.
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
|
|||||||
@@ -8,22 +8,23 @@ For the full list, refer to the [Immich source code](https://github.com/immich-a
|
|||||||
|
|
||||||
## Image formats
|
## Image formats
|
||||||
|
|
||||||
| Format | Extension(s) | Supported? | Notes |
|
| Format | Extension(s) | Supported? | Notes |
|
||||||
| :-------- | :---------------------------- | :----------------: | :-------------- |
|
| :---------- | :---------------------------- | :----------------: | :-------------- |
|
||||||
| `AVIF` | `.avif` | :white_check_mark: | |
|
| `AVIF` | `.avif` | :white_check_mark: | |
|
||||||
| `BMP` | `.bmp` | :white_check_mark: | |
|
| `BMP` | `.bmp` | :white_check_mark: | |
|
||||||
| `GIF` | `.gif` | :white_check_mark: | |
|
| `GIF` | `.gif` | :white_check_mark: | |
|
||||||
| `HEIC` | `.heic` | :white_check_mark: | |
|
| `HEIC` | `.heic` | :white_check_mark: | |
|
||||||
| `HEIF` | `.heif` | :white_check_mark: | |
|
| `HEIF` | `.heif` | :white_check_mark: | |
|
||||||
| `JPEG` | `.webp` `.jpg` `.jpe` `.insp` | :white_check_mark: | |
|
| `JPEG 2000` | `.jp2` | :white_check_mark: | |
|
||||||
| `JPEG XL` | `.jxl` | :white_check_mark: | |
|
| `JPEG` | `.webp` `.jpg` `.jpe` `.insp` | :white_check_mark: | |
|
||||||
| `PNG` | `.webp` | :white_check_mark: | |
|
| `JPEG XL` | `.jxl` | :white_check_mark: | |
|
||||||
| `PSD` | `.psd` | :white_check_mark: | Adobe Photoshop |
|
| `PNG` | `.webp` | :white_check_mark: | |
|
||||||
| `RAW` | `.raw` | :white_check_mark: | |
|
| `PSD` | `.psd` | :white_check_mark: | Adobe Photoshop |
|
||||||
| `RW2` | `.rw2` | :white_check_mark: | |
|
| `RAW` | `.raw` | :white_check_mark: | |
|
||||||
| `SVG` | `.svg` | :white_check_mark: | |
|
| `RW2` | `.rw2` | :white_check_mark: | |
|
||||||
| `TIFF` | `.tif` `.tiff` | :white_check_mark: | |
|
| `SVG` | `.svg` | :white_check_mark: | |
|
||||||
| `WEBP` | `.webp` | :white_check_mark: | |
|
| `TIFF` | `.tif` `.tiff` | :white_check_mark: | |
|
||||||
|
| `WEBP` | `.webp` | :white_check_mark: | |
|
||||||
|
|
||||||
## Video formats
|
## Video formats
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,10 @@ SELECT * FROM "assets" WHERE "originalPath" = 'upload/library/admin/2023/2023-09
|
|||||||
SELECT * FROM "assets" WHERE "originalPath" LIKE 'upload/library/admin/2023/%';
|
SELECT * FROM "assets" WHERE "originalPath" LIKE 'upload/library/admin/2023/%';
|
||||||
```
|
```
|
||||||
|
|
||||||
|
```sql title="Find by ID"
|
||||||
|
SELECT * FROM "assets" WHERE "id" = '9f94e60f-65b6-47b7-ae44-a4df7b57f0e9';
|
||||||
|
```
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
You can calculate the checksum for a particular file by using the command `sha1sum <filename>`.
|
You can calculate the checksum for a particular file by using the command `sha1sum <filename>`.
|
||||||
:::
|
:::
|
||||||
|
|||||||
76
docs/docs/install/synology.md
Normal file
76
docs/docs/install/synology.md
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 85
|
||||||
|
---
|
||||||
|
|
||||||
|
# Synology [Community]
|
||||||
|
|
||||||
|
:::note
|
||||||
|
This is a community contribution and not officially supported by the Immich team, but included here for convenience.
|
||||||
|
|
||||||
|
Community support can be found in the dedicated channel on the [Discord Server](https://discord.immich.app/).
|
||||||
|
|
||||||
|
**Please report app issues to the corresponding [Github Repository](https://github.com/truenas/charts/tree/master/community/immich).**
|
||||||
|
:::
|
||||||
|
|
||||||
|
Immich can easily be installed on a Synology NAS using Container Manager within DSM. If you have not installed Container Manager already, you can install it in the Packages Center. Refer to the [Container Manager docs](https://kb.synology.com/en-us/DSM/help/ContainerManager/docker_desc?version=7) for more information on using Container Manager.
|
||||||
|
|
||||||
|
## Step 1 - Download the required files
|
||||||
|
|
||||||
|
Create a directory of your choice (e.g. `./immich-app`) to house Immich. In general, it's a best practice to have all Docker-based applications running under the `./docker` directory, so in this case, your directory structure will look like `./docker/immich-app`.
|
||||||
|
|
||||||
|
Now create a `./postgres` and `./library` directory as sub-directories of the `./docker/immich-app`.
|
||||||
|
|
||||||
|
When you're all done, you should have the following:
|
||||||
|
|
||||||
|
- `./docker/immich-app/postgres`
|
||||||
|
- `./docker/immich-app/library`
|
||||||
|
|
||||||
|
Download [`docker-compose.yml`](https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml) and [`example.env`](https://github.com/immich-app/immich/releases/latest/download/example.env) to your computer. Upload the files to the `./docker/immich-app` directory.
|
||||||
|
|
||||||
|
## Step 2 - Populate the .env file with custom values
|
||||||
|
|
||||||
|
Follow [Step 2 in Docker Compose](./docker-compose#step-2---populate-the-env-file-with-custom-values) for instructions on customizing the `.env` file, and then return back to this guide to continue.
|
||||||
|
|
||||||
|
## Step 3 - Create a new project in Container Manager
|
||||||
|
|
||||||
|
Open Container Manager, and select the "**Project**" action on the left navigation bar and then click "**Create**".
|
||||||
|

|
||||||
|
|
||||||
|
In the settings of your new project, set "**Project name**" to a name you'll remember, such as _immich-app_. When setting the "**Path**", select the `./docker/immich-app` directory you created earlier. Doing so will prompt a message to use the existing `docker-compose.yml` already present in the directory for your project. Click "**OK**" to continue.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
The following screen will give you the option to further customize your `docker-compose.yml` file, giving you a warning regarding the `start_interval` property. Under the `healthcheck` heading, remove the `start_interval: 30s` completely and click "**Next**".
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Skip the section asking to set-up a portal for Web Station, and then complete the wizard which will build and start the containers for your project.
|
||||||
|
|
||||||
|
Once your containers are successfully running, navigate to the "**Container**" section of Container Manager, right-click on the "**immich-server**" container, and choose the "**Details**".
|
||||||
|
|
||||||
|
Scroll to the bottom of the "**Details**" section, and find the `IP Address` of the container, located in the `Network` section. Take note of the container's IP address as you will need it for **Step 4**.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Step 4 - Configure Firewall Settings
|
||||||
|
|
||||||
|
Once your project completes the build process, your containers will start. In order to be able to access Immich from your browser, you need to configure the firewall settings for your Synology NAS.
|
||||||
|
|
||||||
|
Open "**Control Panel**" on your Synology NAS, and select "**Security**". Navigate to "**Firewall**"
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Click "**Edit Rules**" and add the following firewall rules:
|
||||||
|
|
||||||
|
- Add a "**Source IP**" rule for the IP address of your container that you obtained in Step 3 above
|
||||||
|
- Add a "**Ports**" rule for the port specified in the `docker-compose.yml`, which should be `2283`
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
Read the [Post Installation](/docs/install/post-install.mdx) steps or setup optional features below.
|
||||||
|
|
||||||
|
### Setting up optional features
|
||||||
|
|
||||||
|
- [External Libraries](/docs/features/libraries.md): Adding your existing photo library to Immich
|
||||||
|
- [Hardware Transcoding](/docs/features/hardware-transcoding.md): Speeding up video transcoding
|
||||||
|
- [Hardware-Accelerated Machine Learning](/docs/features/ml-hardware-acceleration.md): Speeding up various machine learning tasks in Immich
|
||||||
@@ -41,7 +41,7 @@ className="border rounded-xl"
|
|||||||
:::info Permissions
|
:::info Permissions
|
||||||
The **pgData** dataset must be owned by the user `netdata` (UID 999) for postgres to start. The other datasets must be owned by the user `root` (UID 0) or a group that includes the user `root` (UID 0) for immich to have the necessary permissions.
|
The **pgData** dataset must be owned by the user `netdata` (UID 999) for postgres to start. The other datasets must be owned by the user `root` (UID 0) or a group that includes the user `root` (UID 0) for immich to have the necessary permissions.
|
||||||
|
|
||||||
If the **library** dataset uses ACL it must have [ACL mode](https://www.truenas.com/docs/core/coretutorials/storage/pools/permissions/#access-control-lists) set to `Passthrough` if you plan on using a [storage template](/docs/administration/storage-template.mdx) and the dataset is configured for network sharing (its ACL type is set to `SMB/NFSv4`). When the template is applied and files need to be moved from **upload** to **library**, immich performs `chmod` internally and needs to be allowed to execute the command. [More info.](https://github.com/immich-app/immich/pull/13017)
|
If the **library** dataset uses ACL it must have [ACL mode](https://www.truenas.com/docs/core/coretutorials/storage/pools/permissions/#access-control-lists) set to `Passthrough` if you plan on using a [storage template](/docs/administration/storage-template.mdx) and the dataset is configured for network sharing (its ACL type is set to `SMB/NFSv4`). When the template is applied and files need to be moved from **upload** to **library**, Immich performs `chmod` internally and needs to be allowed to execute the command. [More info.](https://github.com/immich-app/immich/pull/13017)
|
||||||
:::
|
:::
|
||||||
|
|
||||||
## Installing the Immich Application
|
## Installing the Immich Application
|
||||||
@@ -160,6 +160,10 @@ The image above has example values.
|
|||||||
|
|
||||||
### Additional Storage [(External Libraries)](/docs/features/libraries)
|
### Additional Storage [(External Libraries)](/docs/features/libraries)
|
||||||
|
|
||||||
|
:::danger Advanced Users Only
|
||||||
|
This feature should only be used by advanced users. If this is your first time installing Immich, then DO NOT mount an external library until you have a working setup. Also, your mount path MUST be something unique and should NOT be your library or upload location or a Linux directory like `/lib`. The picture below shows a valid example.
|
||||||
|
:::
|
||||||
|
|
||||||
<img
|
<img
|
||||||
src={require('./img/truenas10.webp').default}
|
src={require('./img/truenas10.webp').default}
|
||||||
width="40%"
|
width="40%"
|
||||||
@@ -168,7 +172,7 @@ className="border rounded-xl"
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
You may configure [External Libraries](/docs/features/libraries) by mounting them using **Additional Storage**.
|
You may configure [External Libraries](/docs/features/libraries) by mounting them using **Additional Storage**.
|
||||||
The **Mount Path** is the loaction you will need to copy and paste into the External Library settings within Immich.
|
The **Mount Path** is the location you will need to copy and paste into the External Library settings within Immich.
|
||||||
The **Host Path** is the location on the TrueNAS SCALE server where your external library is located.
|
The **Host Path** is the location on the TrueNAS SCALE server where your external library is located.
|
||||||
|
|
||||||
<!-- A section for Labels would go here but I don't know what they do. -->
|
<!-- A section for Labels would go here but I don't know what they do. -->
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ alt="Select Plugins > Compose.Manager > Add New Stack > Label it Immich"
|
|||||||
</ul>
|
</ul>
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
5. Click "**Save Changes**", you will be promoted to edit stack UI labels, just leave this blank and click "**Ok**"
|
5. Click "**Save Changes**", you will be prompted to edit stack UI labels, just leave this blank and click "**Ok**"
|
||||||
6. Select the cog ⚙️ next to Immich, click "**Edit Stack**", then click "**Env File**"
|
6. Select the cog ⚙️ next to Immich, click "**Edit Stack**", then click "**Env File**"
|
||||||
7. Paste the entire contents of the [Immich example.env](https://github.com/immich-app/immich/releases/latest/download/example.env) file into the Unraid editor, then **before saving** edit the following:
|
7. Paste the entire contents of the [Immich example.env](https://github.com/immich-app/immich/releases/latest/download/example.env) file into the Unraid editor, then **before saving** edit the following:
|
||||||
|
|
||||||
|
|||||||
@@ -110,9 +110,9 @@ const config = {
|
|||||||
label: 'API',
|
label: 'API',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
to: '/blog',
|
href: 'https://immich.store',
|
||||||
position: 'right',
|
position: 'right',
|
||||||
label: 'Blog',
|
label: 'Merch',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
href: 'https://github.com/immich-app/immich',
|
href: 'https://github.com/immich-app/immich',
|
||||||
|
|||||||
12
docs/package-lock.json
generated
12
docs/package-lock.json
generated
@@ -14061,9 +14061,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/postcss": {
|
"node_modules/postcss": {
|
||||||
"version": "8.5.1",
|
"version": "8.5.2",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.2.tgz",
|
||||||
"integrity": "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==",
|
"integrity": "sha512-MjOadfU3Ys9KYoX0AdkBlFEF1Vx37uCCeN4ZHnmwm9FfpbsGWMZeBLMmmpY+6Ocqod7mkdZ0DT31OlbsFrLlkA==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@@ -15725,9 +15725,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/prettier": {
|
"node_modules/prettier": {
|
||||||
"version": "3.4.2",
|
"version": "3.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.1.tgz",
|
||||||
"integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==",
|
"integrity": "sha512-hPpFQvHwL3Qv5AdRvBFMhnKo4tYxp0ReXiPn2bxkiohEX6mBeBwEpBSQTkD458RaaDKQMYSp4hX4UtfUTA5wDw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"bin": {
|
"bin": {
|
||||||
|
|||||||
@@ -55,6 +55,6 @@
|
|||||||
"node": ">=20"
|
"node": ">=20"
|
||||||
},
|
},
|
||||||
"volta": {
|
"volta": {
|
||||||
"node": "22.13.1"
|
"node": "22.14.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,12 +44,12 @@ export default function VersionSwitcher(): JSX.Element {
|
|||||||
return (
|
return (
|
||||||
versions.length > 0 && (
|
versions.length > 0 && (
|
||||||
<DropdownNavbarItem
|
<DropdownNavbarItem
|
||||||
className="navbar__item"
|
className="version-switcher-34ab39"
|
||||||
label={label}
|
label={label}
|
||||||
mobile={windowSize === 'mobile'}
|
mobile={windowSize === 'mobile'}
|
||||||
items={versions.map(({ label, url }) => ({
|
items={versions.map(({ label, url }) => ({
|
||||||
label,
|
label,
|
||||||
to: url,
|
to: url + location.pathname + location.hash,
|
||||||
target: '_self',
|
target: '_self',
|
||||||
}))}
|
}))}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -75,6 +75,11 @@ div[class^='announcementBar_'] {
|
|||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* workaround for version switcher PR 15894 */
|
||||||
|
div[class*='navbar__items'] > li:has(a[class*='version-switcher-34ab39']) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
code {
|
code {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,6 +50,13 @@ function HomepageHeader() {
|
|||||||
>
|
>
|
||||||
Demo
|
Demo
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
|
<Link
|
||||||
|
className="flex place-items-center place-content-center py-3 px-8 border bg-immich-primary/10 dark:bg-gray-300 rounded-xl hover:no-underline text-immich-primary dark:text-immich-dark-bg font-bold uppercase"
|
||||||
|
to="https://immich.store"
|
||||||
|
>
|
||||||
|
Buy Merch
|
||||||
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="my-12 flex gap-1 font-medium place-items-center place-content-center text-immich-primary dark:text-immich-dark-primary">
|
<div className="my-12 flex gap-1 font-medium place-items-center place-content-center text-immich-primary dark:text-immich-dark-primary">
|
||||||
|
|||||||
12
docs/static/archived-versions.json
vendored
12
docs/static/archived-versions.json
vendored
@@ -1,4 +1,16 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"label": "v1.126.1",
|
||||||
|
"url": "https://v1.126.1.archive.immich.app"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.126.0",
|
||||||
|
"url": "https://v1.126.0.archive.immich.app"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "v1.125.7",
|
||||||
|
"url": "https://v1.125.7.archive.immich.app"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"label": "v1.125.6",
|
"label": "v1.125.6",
|
||||||
"url": "https://v1.125.6.archive.immich.app"
|
"url": "https://v1.125.6.archive.immich.app"
|
||||||
|
|||||||
BIN
docs/static/img/synology-container-manager-container-details.png
vendored
Normal file
BIN
docs/static/img/synology-container-manager-container-details.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 164 KiB |
BIN
docs/static/img/synology-container-manager-create-project.png
vendored
Normal file
BIN
docs/static/img/synology-container-manager-create-project.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 112 KiB |
BIN
docs/static/img/synology-container-manager-customize-docker-compose.png
vendored
Normal file
BIN
docs/static/img/synology-container-manager-customize-docker-compose.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 50 KiB |
BIN
docs/static/img/synology-container-manager-set-path.png
vendored
Normal file
BIN
docs/static/img/synology-container-manager-set-path.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 49 KiB |
BIN
docs/static/img/synology-firewall-rules.png
vendored
Normal file
BIN
docs/static/img/synology-firewall-rules.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 81 KiB |
@@ -1 +1 @@
|
|||||||
22.13.1
|
22.14.0
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ services:
|
|||||||
- 2285:2285
|
- 2285:2285
|
||||||
|
|
||||||
redis:
|
redis:
|
||||||
image: redis:6.2-alpine@sha256:905c4ee67b8e0aa955331960d2aa745781e6bd89afc44a8584bfd13bc890f0ae
|
image: redis:6.2-alpine@sha256:148bb5411c184abd288d9aaed139c98123eeb8824c5d3fce03cf721db58066d8
|
||||||
|
|
||||||
database:
|
database:
|
||||||
image: tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:90724186f0a3517cf6914295b5ab410db9ce23190a2d9d0b9dd6463e3fa298f0
|
image: tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:90724186f0a3517cf6914295b5ab410db9ce23190a2d9d0b9dd6463e3fa298f0
|
||||||
|
|||||||
868
e2e/package-lock.json
generated
868
e2e/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "immich-e2e",
|
"name": "immich-e2e",
|
||||||
"version": "1.125.6",
|
"version": "1.126.1",
|
||||||
"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.10.9",
|
"@types/node": "^22.13.2",
|
||||||
"@types/oidc-provider": "^8.5.1",
|
"@types/oidc-provider": "^8.5.1",
|
||||||
"@types/pg": "^8.11.0",
|
"@types/pg": "^8.11.0",
|
||||||
"@types/pngjs": "^6.0.4",
|
"@types/pngjs": "^6.0.4",
|
||||||
@@ -53,6 +53,6 @@
|
|||||||
"vitest": "^3.0.0"
|
"vitest": "^3.0.0"
|
||||||
},
|
},
|
||||||
"volta": {
|
"volta": {
|
||||||
"node": "22.13.1"
|
"node": "22.14.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
86
e2e/src/api/specs/jobs.e2e-spec.ts
Normal file
86
e2e/src/api/specs/jobs.e2e-spec.ts
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
import { JobCommand, JobName, LoginResponseDto } from '@immich/sdk';
|
||||||
|
import { readFile } from 'node:fs/promises';
|
||||||
|
import { basename } from 'node:path';
|
||||||
|
import { errorDto } from 'src/responses';
|
||||||
|
import { app, testAssetDir, utils } from 'src/utils';
|
||||||
|
import request from 'supertest';
|
||||||
|
import { afterEach, beforeAll, describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
|
describe('/jobs', () => {
|
||||||
|
let admin: LoginResponseDto;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
await utils.resetDatabase();
|
||||||
|
admin = await utils.adminSetup({ onboarding: false });
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('PUT /jobs', () => {
|
||||||
|
afterEach(async () => {
|
||||||
|
await utils.jobCommand(admin.accessToken, JobName.MetadataExtraction, {
|
||||||
|
command: JobCommand.Resume,
|
||||||
|
force: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should require authentication', async () => {
|
||||||
|
const { status, body } = await request(app).put('/jobs/metadataExtraction');
|
||||||
|
expect(status).toBe(401);
|
||||||
|
expect(body).toEqual(errorDto.unauthorized);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should queue metadata extraction for missing assets', async () => {
|
||||||
|
const path1 = `${testAssetDir}/formats/raw/Nikon/D700/philadelphia.nef`;
|
||||||
|
const path2 = `${testAssetDir}/formats/raw/Nikon/D80/glarus.nef`;
|
||||||
|
|
||||||
|
await utils.createAsset(admin.accessToken, {
|
||||||
|
assetData: { bytes: await readFile(path1), filename: basename(path1) },
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, 'metadataExtraction');
|
||||||
|
|
||||||
|
await utils.jobCommand(admin.accessToken, JobName.MetadataExtraction, {
|
||||||
|
command: JobCommand.Pause,
|
||||||
|
force: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { id } = await utils.createAsset(admin.accessToken, {
|
||||||
|
assetData: { bytes: await readFile(path2), filename: basename(path2) },
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, 'metadataExtraction');
|
||||||
|
|
||||||
|
{
|
||||||
|
const asset = await utils.getAssetInfo(admin.accessToken, id);
|
||||||
|
|
||||||
|
expect(asset.exifInfo).toBeDefined();
|
||||||
|
expect(asset.exifInfo?.make).toBeNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
await utils.jobCommand(admin.accessToken, JobName.MetadataExtraction, {
|
||||||
|
command: JobCommand.Empty,
|
||||||
|
force: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, 'metadataExtraction');
|
||||||
|
|
||||||
|
await utils.jobCommand(admin.accessToken, JobName.MetadataExtraction, {
|
||||||
|
command: JobCommand.Resume,
|
||||||
|
force: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.jobCommand(admin.accessToken, JobName.MetadataExtraction, {
|
||||||
|
command: JobCommand.Start,
|
||||||
|
force: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, 'metadataExtraction');
|
||||||
|
|
||||||
|
{
|
||||||
|
const asset = await utils.getAssetInfo(admin.accessToken, id);
|
||||||
|
|
||||||
|
expect(asset.exifInfo).toBeDefined();
|
||||||
|
expect(asset.exifInfo?.make).toBe('NIKON CORPORATION');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { LibraryResponseDto, LoginResponseDto, getAllLibraries, scanLibrary } from '@immich/sdk';
|
import { LibraryResponseDto, LoginResponseDto, getAllLibraries } from '@immich/sdk';
|
||||||
import { cpSync, existsSync, rmSync, unlinkSync } from 'node:fs';
|
import { cpSync, existsSync, rmSync, unlinkSync } from 'node:fs';
|
||||||
import { Socket } from 'socket.io-client';
|
import { Socket } from 'socket.io-client';
|
||||||
import { userDto, uuidDto } from 'src/fixtures';
|
import { userDto, uuidDto } from 'src/fixtures';
|
||||||
@@ -8,8 +8,6 @@ import request from 'supertest';
|
|||||||
import { utimes } from 'utimes';
|
import { utimes } from 'utimes';
|
||||||
import { afterAll, beforeAll, beforeEach, describe, expect, it } from 'vitest';
|
import { afterAll, beforeAll, beforeEach, describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
const scan = async (accessToken: string, id: string) => scanLibrary({ id }, { headers: asBearerAuth(accessToken) });
|
|
||||||
|
|
||||||
describe('/libraries', () => {
|
describe('/libraries', () => {
|
||||||
let admin: LoginResponseDto;
|
let admin: LoginResponseDto;
|
||||||
let user: LoginResponseDto;
|
let user: LoginResponseDto;
|
||||||
@@ -298,6 +296,8 @@ describe('/libraries', () => {
|
|||||||
expect(status).toBe(204);
|
expect(status).toBe(204);
|
||||||
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, 'sidecar');
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, 'metadataExtraction');
|
||||||
|
|
||||||
const { assets } = await utils.searchAssets(admin.accessToken, {
|
const { assets } = await utils.searchAssets(admin.accessToken, {
|
||||||
originalPath: `${testAssetDirInternal}/temp/directoryA/assetA.png`,
|
originalPath: `${testAssetDirInternal}/temp/directoryA/assetA.png`,
|
||||||
@@ -312,15 +312,7 @@ describe('/libraries', () => {
|
|||||||
importPaths: [`${testAssetDirInternal}/temp/directoryA`],
|
importPaths: [`${testAssetDirInternal}/temp/directoryA`],
|
||||||
});
|
});
|
||||||
|
|
||||||
const { status } = await request(app)
|
await utils.scan(admin.accessToken, library.id);
|
||||||
.post(`/libraries/${library.id}/scan`)
|
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`)
|
|
||||||
.send();
|
|
||||||
expect(status).toBe(204);
|
|
||||||
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'metadataExtraction');
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'thumbnailGeneration');
|
|
||||||
|
|
||||||
const { assets } = await utils.searchAssets(admin.accessToken, {
|
const { assets } = await utils.searchAssets(admin.accessToken, {
|
||||||
originalPath: `${testAssetDirInternal}/temp/directoryA/assetA.png`,
|
originalPath: `${testAssetDirInternal}/temp/directoryA/assetA.png`,
|
||||||
@@ -340,13 +332,7 @@ describe('/libraries', () => {
|
|||||||
exclusionPatterns: ['**/directoryA'],
|
exclusionPatterns: ['**/directoryA'],
|
||||||
});
|
});
|
||||||
|
|
||||||
const { status } = await request(app)
|
await utils.scan(admin.accessToken, library.id);
|
||||||
.post(`/libraries/${library.id}/scan`)
|
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`)
|
|
||||||
.send();
|
|
||||||
expect(status).toBe(204);
|
|
||||||
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
|
||||||
|
|
||||||
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
||||||
|
|
||||||
@@ -360,13 +346,7 @@ describe('/libraries', () => {
|
|||||||
importPaths: [`${testAssetDirInternal}/temp/directoryA`, `${testAssetDirInternal}/temp/directoryB`],
|
importPaths: [`${testAssetDirInternal}/temp/directoryA`, `${testAssetDirInternal}/temp/directoryB`],
|
||||||
});
|
});
|
||||||
|
|
||||||
const { status } = await request(app)
|
await utils.scan(admin.accessToken, library.id);
|
||||||
.post(`/libraries/${library.id}/scan`)
|
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`)
|
|
||||||
.send();
|
|
||||||
expect(status).toBe(204);
|
|
||||||
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
|
||||||
|
|
||||||
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
||||||
|
|
||||||
@@ -385,13 +365,7 @@ describe('/libraries', () => {
|
|||||||
utils.createImageFile(`${testAssetDir}/temp/folder, a/assetA.png`);
|
utils.createImageFile(`${testAssetDir}/temp/folder, a/assetA.png`);
|
||||||
utils.createImageFile(`${testAssetDir}/temp/folder, b/assetB.png`);
|
utils.createImageFile(`${testAssetDir}/temp/folder, b/assetB.png`);
|
||||||
|
|
||||||
const { status } = await request(app)
|
await utils.scan(admin.accessToken, library.id);
|
||||||
.post(`/libraries/${library.id}/scan`)
|
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`)
|
|
||||||
.send();
|
|
||||||
expect(status).toBe(204);
|
|
||||||
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
|
||||||
|
|
||||||
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
||||||
|
|
||||||
@@ -413,13 +387,7 @@ describe('/libraries', () => {
|
|||||||
utils.createImageFile(`${testAssetDir}/temp/folder{ a/assetA.png`);
|
utils.createImageFile(`${testAssetDir}/temp/folder{ a/assetA.png`);
|
||||||
utils.createImageFile(`${testAssetDir}/temp/folder} b/assetB.png`);
|
utils.createImageFile(`${testAssetDir}/temp/folder} b/assetB.png`);
|
||||||
|
|
||||||
const { status } = await request(app)
|
await utils.scan(admin.accessToken, library.id);
|
||||||
.post(`/libraries/${library.id}/scan`)
|
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`)
|
|
||||||
.send();
|
|
||||||
expect(status).toBe(204);
|
|
||||||
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
|
||||||
|
|
||||||
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
||||||
|
|
||||||
@@ -471,13 +439,7 @@ describe('/libraries', () => {
|
|||||||
utils.createImageFile(`${testAssetDir}/temp/folder${char}1/asset1.png`);
|
utils.createImageFile(`${testAssetDir}/temp/folder${char}1/asset1.png`);
|
||||||
utils.createImageFile(`${testAssetDir}/temp/folder${char}2/asset2.png`);
|
utils.createImageFile(`${testAssetDir}/temp/folder${char}2/asset2.png`);
|
||||||
|
|
||||||
const { status } = await request(app)
|
await utils.scan(admin.accessToken, library.id);
|
||||||
.post(`/libraries/${library.id}/scan`)
|
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`)
|
|
||||||
.send();
|
|
||||||
expect(status).toBe(204);
|
|
||||||
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
|
||||||
|
|
||||||
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
||||||
|
|
||||||
@@ -501,23 +463,12 @@ describe('/libraries', () => {
|
|||||||
utils.createImageFile(`${testAssetDir}/temp/reimport/asset.jpg`);
|
utils.createImageFile(`${testAssetDir}/temp/reimport/asset.jpg`);
|
||||||
await utimes(`${testAssetDir}/temp/reimport/asset.jpg`, 447_775_200_000);
|
await utimes(`${testAssetDir}/temp/reimport/asset.jpg`, 447_775_200_000);
|
||||||
|
|
||||||
await scan(admin.accessToken, library.id);
|
await utils.scan(admin.accessToken, library.id);
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'sidecar');
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'metadataExtraction');
|
|
||||||
|
|
||||||
cpSync(`${testAssetDir}/albums/nature/tanners_ridge.jpg`, `${testAssetDir}/temp/reimport/asset.jpg`);
|
cpSync(`${testAssetDir}/albums/nature/tanners_ridge.jpg`, `${testAssetDir}/temp/reimport/asset.jpg`);
|
||||||
await utimes(`${testAssetDir}/temp/reimport/asset.jpg`, 447_775_200_001);
|
await utimes(`${testAssetDir}/temp/reimport/asset.jpg`, 447_775_200_001);
|
||||||
|
|
||||||
const { status } = await request(app)
|
await utils.scan(admin.accessToken, library.id);
|
||||||
.post(`/libraries/${library.id}/scan`)
|
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`)
|
|
||||||
.send();
|
|
||||||
expect(status).toBe(204);
|
|
||||||
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'sidecar');
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'metadataExtraction');
|
|
||||||
|
|
||||||
const { assets } = await utils.searchAssets(admin.accessToken, {
|
const { assets } = await utils.searchAssets(admin.accessToken, {
|
||||||
libraryId: library.id,
|
libraryId: library.id,
|
||||||
@@ -548,21 +499,12 @@ describe('/libraries', () => {
|
|||||||
utils.createImageFile(`${testAssetDir}/temp/reimport/asset.jpg`);
|
utils.createImageFile(`${testAssetDir}/temp/reimport/asset.jpg`);
|
||||||
await utimes(`${testAssetDir}/temp/reimport/asset.jpg`, 447_775_200_000);
|
await utimes(`${testAssetDir}/temp/reimport/asset.jpg`, 447_775_200_000);
|
||||||
|
|
||||||
await scan(admin.accessToken, library.id);
|
await utils.scan(admin.accessToken, library.id);
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
|
||||||
|
|
||||||
cpSync(`${testAssetDir}/albums/nature/tanners_ridge.jpg`, `${testAssetDir}/temp/reimport/asset.jpg`);
|
cpSync(`${testAssetDir}/albums/nature/tanners_ridge.jpg`, `${testAssetDir}/temp/reimport/asset.jpg`);
|
||||||
await utimes(`${testAssetDir}/temp/reimport/asset.jpg`, 447_775_200_000);
|
await utimes(`${testAssetDir}/temp/reimport/asset.jpg`, 447_775_200_000);
|
||||||
|
|
||||||
const { status } = await request(app)
|
await utils.scan(admin.accessToken, library.id);
|
||||||
.post(`/libraries/${library.id}/scan`)
|
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`)
|
|
||||||
.send();
|
|
||||||
expect(status).toBe(204);
|
|
||||||
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'sidecar');
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'metadataExtraction');
|
|
||||||
|
|
||||||
const { assets } = await utils.searchAssets(admin.accessToken, {
|
const { assets } = await utils.searchAssets(admin.accessToken, {
|
||||||
libraryId: library.id,
|
libraryId: library.id,
|
||||||
@@ -592,21 +534,14 @@ describe('/libraries', () => {
|
|||||||
|
|
||||||
utils.createImageFile(`${testAssetDir}/temp/offline/offline.png`);
|
utils.createImageFile(`${testAssetDir}/temp/offline/offline.png`);
|
||||||
|
|
||||||
await scan(admin.accessToken, library.id);
|
await utils.scan(admin.accessToken, library.id);
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
|
||||||
|
|
||||||
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
||||||
expect(assets.count).toBe(1);
|
expect(assets.count).toBe(1);
|
||||||
|
|
||||||
utils.removeImageFile(`${testAssetDir}/temp/offline/offline.png`);
|
utils.removeImageFile(`${testAssetDir}/temp/offline/offline.png`);
|
||||||
|
|
||||||
const { status } = await request(app)
|
await utils.scan(admin.accessToken, library.id);
|
||||||
.post(`/libraries/${library.id}/scan`)
|
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`)
|
|
||||||
.send();
|
|
||||||
expect(status).toBe(204);
|
|
||||||
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
|
||||||
|
|
||||||
const trashedAsset = await utils.getAssetInfo(admin.accessToken, assets.items[0].id);
|
const trashedAsset = await utils.getAssetInfo(admin.accessToken, assets.items[0].id);
|
||||||
expect(trashedAsset.originalPath).toBe(`${testAssetDirInternal}/temp/offline/offline.png`);
|
expect(trashedAsset.originalPath).toBe(`${testAssetDirInternal}/temp/offline/offline.png`);
|
||||||
@@ -624,8 +559,7 @@ describe('/libraries', () => {
|
|||||||
importPaths: [`${testAssetDirInternal}/temp/offline`],
|
importPaths: [`${testAssetDirInternal}/temp/offline`],
|
||||||
});
|
});
|
||||||
|
|
||||||
await scan(admin.accessToken, library.id);
|
await utils.scan(admin.accessToken, library.id);
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
|
||||||
|
|
||||||
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
||||||
expect(assets.count).toBe(1);
|
expect(assets.count).toBe(1);
|
||||||
@@ -636,13 +570,7 @@ describe('/libraries', () => {
|
|||||||
importPaths: [`${testAssetDirInternal}/temp/another-path/`],
|
importPaths: [`${testAssetDirInternal}/temp/another-path/`],
|
||||||
});
|
});
|
||||||
|
|
||||||
const { status } = await request(app)
|
await utils.scan(admin.accessToken, library.id);
|
||||||
.post(`/libraries/${library.id}/scan`)
|
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`)
|
|
||||||
.send();
|
|
||||||
expect(status).toBe(204);
|
|
||||||
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
|
||||||
|
|
||||||
const trashedAsset = await utils.getAssetInfo(admin.accessToken, assets.items[0].id);
|
const trashedAsset = await utils.getAssetInfo(admin.accessToken, assets.items[0].id);
|
||||||
expect(trashedAsset.originalPath).toBe(`${testAssetDirInternal}/temp/offline/offline.png`);
|
expect(trashedAsset.originalPath).toBe(`${testAssetDirInternal}/temp/offline/offline.png`);
|
||||||
@@ -662,8 +590,7 @@ describe('/libraries', () => {
|
|||||||
importPaths: [`${testAssetDirInternal}/temp`],
|
importPaths: [`${testAssetDirInternal}/temp`],
|
||||||
});
|
});
|
||||||
|
|
||||||
await scan(admin.accessToken, library.id);
|
await utils.scan(admin.accessToken, library.id);
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
|
||||||
|
|
||||||
const { assets } = await utils.searchAssets(admin.accessToken, {
|
const { assets } = await utils.searchAssets(admin.accessToken, {
|
||||||
libraryId: library.id,
|
libraryId: library.id,
|
||||||
@@ -673,8 +600,7 @@ describe('/libraries', () => {
|
|||||||
|
|
||||||
await utils.updateLibrary(admin.accessToken, library.id, { exclusionPatterns: ['**/directoryB/**'] });
|
await utils.updateLibrary(admin.accessToken, library.id, { exclusionPatterns: ['**/directoryB/**'] });
|
||||||
|
|
||||||
await scan(admin.accessToken, library.id);
|
await utils.scan(admin.accessToken, library.id);
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
|
||||||
|
|
||||||
const trashedAsset = await utils.getAssetInfo(admin.accessToken, assets.items[0].id);
|
const trashedAsset = await utils.getAssetInfo(admin.accessToken, assets.items[0].id);
|
||||||
expect(trashedAsset.isTrashed).toBe(true);
|
expect(trashedAsset.isTrashed).toBe(true);
|
||||||
@@ -696,19 +622,12 @@ describe('/libraries', () => {
|
|||||||
importPaths: [`${testAssetDirInternal}/temp`],
|
importPaths: [`${testAssetDirInternal}/temp`],
|
||||||
});
|
});
|
||||||
|
|
||||||
await scan(admin.accessToken, library.id);
|
await utils.scan(admin.accessToken, library.id);
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
|
||||||
|
|
||||||
const { assets: assetsBefore } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
const { assets: assetsBefore } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
||||||
expect(assetsBefore.count).toBeGreaterThan(1);
|
expect(assetsBefore.count).toBeGreaterThan(1);
|
||||||
|
|
||||||
const { status } = await request(app)
|
await utils.scan(admin.accessToken, library.id);
|
||||||
.post(`/libraries/${library.id}/scan`)
|
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`)
|
|
||||||
.send();
|
|
||||||
expect(status).toBe(204);
|
|
||||||
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
|
||||||
|
|
||||||
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
||||||
|
|
||||||
@@ -725,11 +644,7 @@ describe('/libraries', () => {
|
|||||||
cpSync(`${testAssetDir}/metadata/xmp/dates/2000.xmp`, `${testAssetDir}/temp/xmp/glarus.xmp`);
|
cpSync(`${testAssetDir}/metadata/xmp/dates/2000.xmp`, `${testAssetDir}/temp/xmp/glarus.xmp`);
|
||||||
cpSync(`${testAssetDir}/formats/raw/Nikon/D80/glarus.nef`, `${testAssetDir}/temp/xmp/glarus.nef`);
|
cpSync(`${testAssetDir}/formats/raw/Nikon/D80/glarus.nef`, `${testAssetDir}/temp/xmp/glarus.nef`);
|
||||||
|
|
||||||
await scan(admin.accessToken, library.id);
|
await utils.scan(admin.accessToken, library.id);
|
||||||
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'sidecar');
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'metadataExtraction');
|
|
||||||
|
|
||||||
const { assets: newAssets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
const { assets: newAssets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
||||||
|
|
||||||
@@ -752,10 +667,7 @@ describe('/libraries', () => {
|
|||||||
cpSync(`${testAssetDir}/metadata/xmp/dates/2000.xmp`, `${testAssetDir}/temp/xmp/glarus.nef.xmp`);
|
cpSync(`${testAssetDir}/metadata/xmp/dates/2000.xmp`, `${testAssetDir}/temp/xmp/glarus.nef.xmp`);
|
||||||
cpSync(`${testAssetDir}/formats/raw/Nikon/D80/glarus.nef`, `${testAssetDir}/temp/xmp/glarus.nef`);
|
cpSync(`${testAssetDir}/formats/raw/Nikon/D80/glarus.nef`, `${testAssetDir}/temp/xmp/glarus.nef`);
|
||||||
|
|
||||||
await scan(admin.accessToken, library.id);
|
await utils.scan(admin.accessToken, library.id);
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'sidecar');
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'metadataExtraction');
|
|
||||||
|
|
||||||
const { assets: newAssets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
const { assets: newAssets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
||||||
|
|
||||||
@@ -779,10 +691,7 @@ describe('/libraries', () => {
|
|||||||
cpSync(`${testAssetDir}/metadata/xmp/dates/2010.xmp`, `${testAssetDir}/temp/xmp/glarus.xmp`);
|
cpSync(`${testAssetDir}/metadata/xmp/dates/2010.xmp`, `${testAssetDir}/temp/xmp/glarus.xmp`);
|
||||||
cpSync(`${testAssetDir}/formats/raw/Nikon/D80/glarus.nef`, `${testAssetDir}/temp/xmp/glarus.nef`);
|
cpSync(`${testAssetDir}/formats/raw/Nikon/D80/glarus.nef`, `${testAssetDir}/temp/xmp/glarus.nef`);
|
||||||
|
|
||||||
await scan(admin.accessToken, library.id);
|
await utils.scan(admin.accessToken, library.id);
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'sidecar');
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'metadataExtraction');
|
|
||||||
|
|
||||||
const { assets: newAssets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
const { assets: newAssets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
||||||
|
|
||||||
@@ -806,19 +715,13 @@ describe('/libraries', () => {
|
|||||||
cpSync(`${testAssetDir}/formats/raw/Nikon/D80/glarus.nef`, `${testAssetDir}/temp/xmp/glarus.nef`);
|
cpSync(`${testAssetDir}/formats/raw/Nikon/D80/glarus.nef`, `${testAssetDir}/temp/xmp/glarus.nef`);
|
||||||
await utimes(`${testAssetDir}/temp/xmp/glarus.nef`, 447_775_200_000);
|
await utimes(`${testAssetDir}/temp/xmp/glarus.nef`, 447_775_200_000);
|
||||||
|
|
||||||
await scan(admin.accessToken, library.id);
|
await utils.scan(admin.accessToken, library.id);
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'sidecar');
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'metadataExtraction');
|
|
||||||
|
|
||||||
cpSync(`${testAssetDir}/metadata/xmp/dates/2010.xmp`, `${testAssetDir}/temp/xmp/glarus.nef.xmp`);
|
cpSync(`${testAssetDir}/metadata/xmp/dates/2010.xmp`, `${testAssetDir}/temp/xmp/glarus.nef.xmp`);
|
||||||
unlinkSync(`${testAssetDir}/temp/xmp/glarus.xmp`);
|
unlinkSync(`${testAssetDir}/temp/xmp/glarus.xmp`);
|
||||||
await utimes(`${testAssetDir}/temp/xmp/glarus.nef`, 447_775_200_001);
|
await utimes(`${testAssetDir}/temp/xmp/glarus.nef`, 447_775_200_001);
|
||||||
|
|
||||||
await scan(admin.accessToken, library.id);
|
await utils.scan(admin.accessToken, library.id);
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'sidecar');
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'metadataExtraction');
|
|
||||||
|
|
||||||
const { assets: newAssets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
const { assets: newAssets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
||||||
|
|
||||||
@@ -841,18 +744,12 @@ describe('/libraries', () => {
|
|||||||
cpSync(`${testAssetDir}/formats/raw/Nikon/D80/glarus.nef`, `${testAssetDir}/temp/xmp/glarus.nef`);
|
cpSync(`${testAssetDir}/formats/raw/Nikon/D80/glarus.nef`, `${testAssetDir}/temp/xmp/glarus.nef`);
|
||||||
await utimes(`${testAssetDir}/temp/xmp/glarus.nef`, 447_775_200_000);
|
await utimes(`${testAssetDir}/temp/xmp/glarus.nef`, 447_775_200_000);
|
||||||
|
|
||||||
await scan(admin.accessToken, library.id);
|
await utils.scan(admin.accessToken, library.id);
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'sidecar');
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'metadataExtraction');
|
|
||||||
|
|
||||||
cpSync(`${testAssetDir}/metadata/xmp/dates/2000.xmp`, `${testAssetDir}/temp/xmp/glarus.xmp`);
|
cpSync(`${testAssetDir}/metadata/xmp/dates/2000.xmp`, `${testAssetDir}/temp/xmp/glarus.xmp`);
|
||||||
await utimes(`${testAssetDir}/temp/xmp/glarus.nef`, 447_775_200_001);
|
await utimes(`${testAssetDir}/temp/xmp/glarus.nef`, 447_775_200_001);
|
||||||
|
|
||||||
await scan(admin.accessToken, library.id);
|
await utils.scan(admin.accessToken, library.id);
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'sidecar');
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'metadataExtraction');
|
|
||||||
|
|
||||||
const { assets: newAssets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
const { assets: newAssets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
||||||
|
|
||||||
@@ -875,18 +772,12 @@ describe('/libraries', () => {
|
|||||||
cpSync(`${testAssetDir}/formats/raw/Nikon/D80/glarus.nef`, `${testAssetDir}/temp/xmp/glarus.nef`);
|
cpSync(`${testAssetDir}/formats/raw/Nikon/D80/glarus.nef`, `${testAssetDir}/temp/xmp/glarus.nef`);
|
||||||
await utimes(`${testAssetDir}/temp/xmp/glarus.nef`, 447_775_200_000);
|
await utimes(`${testAssetDir}/temp/xmp/glarus.nef`, 447_775_200_000);
|
||||||
|
|
||||||
await scan(admin.accessToken, library.id);
|
await utils.scan(admin.accessToken, library.id);
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'sidecar');
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'metadataExtraction');
|
|
||||||
|
|
||||||
cpSync(`${testAssetDir}/metadata/xmp/dates/2000.xmp`, `${testAssetDir}/temp/xmp/glarus.nef.xmp`);
|
cpSync(`${testAssetDir}/metadata/xmp/dates/2000.xmp`, `${testAssetDir}/temp/xmp/glarus.nef.xmp`);
|
||||||
await utimes(`${testAssetDir}/temp/xmp/glarus.nef`, 447_775_200_001);
|
await utimes(`${testAssetDir}/temp/xmp/glarus.nef`, 447_775_200_001);
|
||||||
|
|
||||||
await scan(admin.accessToken, library.id);
|
await utils.scan(admin.accessToken, library.id);
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'sidecar');
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'metadataExtraction');
|
|
||||||
|
|
||||||
const { assets: newAssets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
const { assets: newAssets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
||||||
|
|
||||||
@@ -910,19 +801,13 @@ describe('/libraries', () => {
|
|||||||
cpSync(`${testAssetDir}/formats/raw/Nikon/D80/glarus.nef`, `${testAssetDir}/temp/xmp/glarus.nef`);
|
cpSync(`${testAssetDir}/formats/raw/Nikon/D80/glarus.nef`, `${testAssetDir}/temp/xmp/glarus.nef`);
|
||||||
await utimes(`${testAssetDir}/temp/xmp/glarus.nef`, 447_775_200_000);
|
await utimes(`${testAssetDir}/temp/xmp/glarus.nef`, 447_775_200_000);
|
||||||
|
|
||||||
await scan(admin.accessToken, library.id);
|
await utils.scan(admin.accessToken, library.id);
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'sidecar');
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'metadataExtraction');
|
|
||||||
|
|
||||||
cpSync(`${testAssetDir}/metadata/xmp/dates/2010.xmp`, `${testAssetDir}/temp/xmp/glarus.xmp`);
|
cpSync(`${testAssetDir}/metadata/xmp/dates/2010.xmp`, `${testAssetDir}/temp/xmp/glarus.xmp`);
|
||||||
unlinkSync(`${testAssetDir}/temp/xmp/glarus.nef.xmp`);
|
unlinkSync(`${testAssetDir}/temp/xmp/glarus.nef.xmp`);
|
||||||
await utimes(`${testAssetDir}/temp/xmp/glarus.nef`, 447_775_200_001);
|
await utimes(`${testAssetDir}/temp/xmp/glarus.nef`, 447_775_200_001);
|
||||||
|
|
||||||
await scan(admin.accessToken, library.id);
|
await utils.scan(admin.accessToken, library.id);
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'sidecar');
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'metadataExtraction');
|
|
||||||
|
|
||||||
const { assets: newAssets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
const { assets: newAssets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
||||||
|
|
||||||
@@ -946,18 +831,12 @@ describe('/libraries', () => {
|
|||||||
cpSync(`${testAssetDir}/formats/raw/Nikon/D80/glarus.nef`, `${testAssetDir}/temp/xmp/glarus.nef`);
|
cpSync(`${testAssetDir}/formats/raw/Nikon/D80/glarus.nef`, `${testAssetDir}/temp/xmp/glarus.nef`);
|
||||||
await utimes(`${testAssetDir}/temp/xmp/glarus.nef`, 447_775_200_000);
|
await utimes(`${testAssetDir}/temp/xmp/glarus.nef`, 447_775_200_000);
|
||||||
|
|
||||||
await scan(admin.accessToken, library.id);
|
await utils.scan(admin.accessToken, library.id);
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'sidecar');
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'metadataExtraction');
|
|
||||||
|
|
||||||
unlinkSync(`${testAssetDir}/temp/xmp/glarus.nef.xmp`);
|
unlinkSync(`${testAssetDir}/temp/xmp/glarus.nef.xmp`);
|
||||||
await utimes(`${testAssetDir}/temp/xmp/glarus.nef`, 447_775_200_001);
|
await utimes(`${testAssetDir}/temp/xmp/glarus.nef`, 447_775_200_001);
|
||||||
|
|
||||||
await scan(admin.accessToken, library.id);
|
await utils.scan(admin.accessToken, library.id);
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'sidecar');
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'metadataExtraction');
|
|
||||||
|
|
||||||
const { assets: newAssets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
const { assets: newAssets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
||||||
|
|
||||||
@@ -981,18 +860,12 @@ describe('/libraries', () => {
|
|||||||
cpSync(`${testAssetDir}/formats/raw/Nikon/D80/glarus.nef`, `${testAssetDir}/temp/xmp/glarus.nef`);
|
cpSync(`${testAssetDir}/formats/raw/Nikon/D80/glarus.nef`, `${testAssetDir}/temp/xmp/glarus.nef`);
|
||||||
await utimes(`${testAssetDir}/temp/xmp/glarus.nef`, 447_775_200_000);
|
await utimes(`${testAssetDir}/temp/xmp/glarus.nef`, 447_775_200_000);
|
||||||
|
|
||||||
await scan(admin.accessToken, library.id);
|
await utils.scan(admin.accessToken, library.id);
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'sidecar');
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'metadataExtraction');
|
|
||||||
|
|
||||||
unlinkSync(`${testAssetDir}/temp/xmp/glarus.xmp`);
|
unlinkSync(`${testAssetDir}/temp/xmp/glarus.xmp`);
|
||||||
await utimes(`${testAssetDir}/temp/xmp/glarus.nef`, 447_775_200_001);
|
await utimes(`${testAssetDir}/temp/xmp/glarus.nef`, 447_775_200_001);
|
||||||
|
|
||||||
await scan(admin.accessToken, library.id);
|
await utils.scan(admin.accessToken, library.id);
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'sidecar');
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'metadataExtraction');
|
|
||||||
|
|
||||||
const { assets: newAssets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
const { assets: newAssets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
||||||
|
|
||||||
@@ -1015,22 +888,13 @@ describe('/libraries', () => {
|
|||||||
importPaths: [`${testAssetDirInternal}/temp/offline`],
|
importPaths: [`${testAssetDirInternal}/temp/offline`],
|
||||||
});
|
});
|
||||||
|
|
||||||
await scan(admin.accessToken, library.id);
|
await utils.scan(admin.accessToken, library.id);
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
|
||||||
|
|
||||||
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
||||||
|
|
||||||
utils.renameImageFile(`${testAssetDir}/temp/offline/offline.png`, `${testAssetDir}/temp/offline.png`);
|
utils.renameImageFile(`${testAssetDir}/temp/offline/offline.png`, `${testAssetDir}/temp/offline.png`);
|
||||||
|
|
||||||
{
|
await utils.scan(admin.accessToken, library.id);
|
||||||
const { status } = await request(app)
|
|
||||||
.post(`/libraries/${library.id}/scan`)
|
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`)
|
|
||||||
.send();
|
|
||||||
expect(status).toBe(204);
|
|
||||||
}
|
|
||||||
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
|
||||||
|
|
||||||
const offlineAsset = await utils.getAssetInfo(admin.accessToken, assets.items[0].id);
|
const offlineAsset = await utils.getAssetInfo(admin.accessToken, assets.items[0].id);
|
||||||
expect(offlineAsset.isTrashed).toBe(true);
|
expect(offlineAsset.isTrashed).toBe(true);
|
||||||
@@ -1044,15 +908,7 @@ describe('/libraries', () => {
|
|||||||
|
|
||||||
utils.renameImageFile(`${testAssetDir}/temp/offline.png`, `${testAssetDir}/temp/offline/offline.png`);
|
utils.renameImageFile(`${testAssetDir}/temp/offline.png`, `${testAssetDir}/temp/offline/offline.png`);
|
||||||
|
|
||||||
{
|
await utils.scan(admin.accessToken, library.id);
|
||||||
const { status } = await request(app)
|
|
||||||
.post(`/libraries/${library.id}/scan`)
|
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`)
|
|
||||||
.send();
|
|
||||||
expect(status).toBe(204);
|
|
||||||
}
|
|
||||||
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
|
||||||
|
|
||||||
const backOnlineAsset = await utils.getAssetInfo(admin.accessToken, assets.items[0].id);
|
const backOnlineAsset = await utils.getAssetInfo(admin.accessToken, assets.items[0].id);
|
||||||
|
|
||||||
@@ -1074,22 +930,13 @@ describe('/libraries', () => {
|
|||||||
importPaths: [`${testAssetDirInternal}/temp/offline`],
|
importPaths: [`${testAssetDirInternal}/temp/offline`],
|
||||||
});
|
});
|
||||||
|
|
||||||
await scan(admin.accessToken, library.id);
|
await utils.scan(admin.accessToken, library.id);
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
|
||||||
|
|
||||||
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
||||||
|
|
||||||
utils.renameImageFile(`${testAssetDir}/temp/offline/offline.png`, `${testAssetDir}/temp/offline.png`);
|
utils.renameImageFile(`${testAssetDir}/temp/offline/offline.png`, `${testAssetDir}/temp/offline.png`);
|
||||||
|
|
||||||
{
|
await utils.scan(admin.accessToken, library.id);
|
||||||
const { status } = await request(app)
|
|
||||||
.post(`/libraries/${library.id}/scan`)
|
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`)
|
|
||||||
.send();
|
|
||||||
expect(status).toBe(204);
|
|
||||||
}
|
|
||||||
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
|
||||||
|
|
||||||
{
|
{
|
||||||
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id, withDeleted: true });
|
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id, withDeleted: true });
|
||||||
@@ -1110,15 +957,7 @@ describe('/libraries', () => {
|
|||||||
importPaths: [`${testAssetDirInternal}/temp/another-path`],
|
importPaths: [`${testAssetDirInternal}/temp/another-path`],
|
||||||
});
|
});
|
||||||
|
|
||||||
{
|
await utils.scan(admin.accessToken, library.id);
|
||||||
const { status } = await request(app)
|
|
||||||
.post(`/libraries/${library.id}/scan`)
|
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`)
|
|
||||||
.send();
|
|
||||||
expect(status).toBe(204);
|
|
||||||
}
|
|
||||||
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
|
||||||
|
|
||||||
const stillOfflineAsset = await utils.getAssetInfo(admin.accessToken, assets.items[0].id);
|
const stillOfflineAsset = await utils.getAssetInfo(admin.accessToken, assets.items[0].id);
|
||||||
|
|
||||||
@@ -1142,22 +981,13 @@ describe('/libraries', () => {
|
|||||||
importPaths: [`${testAssetDirInternal}/temp/offline`],
|
importPaths: [`${testAssetDirInternal}/temp/offline`],
|
||||||
});
|
});
|
||||||
|
|
||||||
await scan(admin.accessToken, library.id);
|
await utils.scan(admin.accessToken, library.id);
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
|
||||||
|
|
||||||
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
||||||
|
|
||||||
utils.renameImageFile(`${testAssetDir}/temp/offline/offline.png`, `${testAssetDir}/temp/offline.png`);
|
utils.renameImageFile(`${testAssetDir}/temp/offline/offline.png`, `${testAssetDir}/temp/offline.png`);
|
||||||
|
|
||||||
{
|
await utils.scan(admin.accessToken, library.id);
|
||||||
const { status } = await request(app)
|
|
||||||
.post(`/libraries/${library.id}/scan`)
|
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`)
|
|
||||||
.send();
|
|
||||||
expect(status).toBe(204);
|
|
||||||
}
|
|
||||||
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
|
||||||
|
|
||||||
{
|
{
|
||||||
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id, withDeleted: true });
|
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id, withDeleted: true });
|
||||||
@@ -1174,15 +1004,7 @@ describe('/libraries', () => {
|
|||||||
|
|
||||||
await utils.updateLibrary(admin.accessToken, library.id, { exclusionPatterns: ['**/offline/**'] });
|
await utils.updateLibrary(admin.accessToken, library.id, { exclusionPatterns: ['**/offline/**'] });
|
||||||
|
|
||||||
{
|
await utils.scan(admin.accessToken, library.id);
|
||||||
const { status } = await request(app)
|
|
||||||
.post(`/libraries/${library.id}/scan`)
|
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`)
|
|
||||||
.send();
|
|
||||||
expect(status).toBe(204);
|
|
||||||
}
|
|
||||||
|
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
|
||||||
|
|
||||||
const stillOfflineAsset = await utils.getAssetInfo(admin.accessToken, assets.items[0].id);
|
const stillOfflineAsset = await utils.getAssetInfo(admin.accessToken, assets.items[0].id);
|
||||||
|
|
||||||
@@ -1302,8 +1124,7 @@ describe('/libraries', () => {
|
|||||||
importPaths: [`${testAssetDirInternal}/temp`],
|
importPaths: [`${testAssetDirInternal}/temp`],
|
||||||
});
|
});
|
||||||
|
|
||||||
await scan(admin.accessToken, library.id);
|
await utils.scan(admin.accessToken, library.id);
|
||||||
await utils.waitForQueueFinish(admin.accessToken, 'library');
|
|
||||||
|
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.delete(`/libraries/${library.id}`)
|
.delete(`/libraries/${library.id}`)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { LoginResponseDto, PersonResponseDto } from '@immich/sdk';
|
import { getPerson, LoginResponseDto, PersonResponseDto } from '@immich/sdk';
|
||||||
import { uuidDto } from 'src/fixtures';
|
import { uuidDto } from 'src/fixtures';
|
||||||
import { errorDto } from 'src/responses';
|
import { errorDto } from 'src/responses';
|
||||||
import { app, utils } from 'src/utils';
|
import { app, asBearerAuth, utils } from 'src/utils';
|
||||||
import request from 'supertest';
|
import request from 'supertest';
|
||||||
import { beforeAll, beforeEach, describe, expect, it } from 'vitest';
|
import { beforeAll, beforeEach, describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
@@ -195,6 +195,7 @@ describe('/people', () => {
|
|||||||
.send({
|
.send({
|
||||||
name: 'New Person',
|
name: 'New Person',
|
||||||
birthDate: '1990-01-01',
|
birthDate: '1990-01-01',
|
||||||
|
color: '#333',
|
||||||
});
|
});
|
||||||
expect(status).toBe(201);
|
expect(status).toBe(201);
|
||||||
expect(body).toMatchObject({
|
expect(body).toMatchObject({
|
||||||
@@ -203,6 +204,22 @@ describe('/people', () => {
|
|||||||
birthDate: '1990-01-01T00:00:00.000Z',
|
birthDate: '1990-01-01T00:00:00.000Z',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should create a favorite person', async () => {
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.post(`/people`)
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||||
|
.send({
|
||||||
|
name: 'New Favorite Person',
|
||||||
|
isFavorite: true,
|
||||||
|
});
|
||||||
|
expect(status).toBe(201);
|
||||||
|
expect(body).toMatchObject({
|
||||||
|
id: expect.any(String),
|
||||||
|
name: 'New Favorite Person',
|
||||||
|
isFavorite: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('PUT /people/:id', () => {
|
describe('PUT /people/:id', () => {
|
||||||
@@ -216,6 +233,7 @@ describe('/people', () => {
|
|||||||
{ key: 'name', type: 'string' },
|
{ key: 'name', type: 'string' },
|
||||||
{ key: 'featureFaceAssetId', type: 'string' },
|
{ key: 'featureFaceAssetId', type: 'string' },
|
||||||
{ key: 'isHidden', type: 'boolean value' },
|
{ key: 'isHidden', type: 'boolean value' },
|
||||||
|
{ key: 'isFavorite', type: 'boolean value' },
|
||||||
]) {
|
]) {
|
||||||
it(`should not allow null ${key}`, async () => {
|
it(`should not allow null ${key}`, async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
@@ -255,6 +273,42 @@ describe('/people', () => {
|
|||||||
expect(status).toBe(200);
|
expect(status).toBe(200);
|
||||||
expect(body).toMatchObject({ birthDate: null });
|
expect(body).toMatchObject({ birthDate: null });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should set a color', async () => {
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.put(`/people/${visiblePerson.id}`)
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||||
|
.send({ color: '#555' });
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(body).toMatchObject({ color: '#555' });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should clear a color', async () => {
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.put(`/people/${visiblePerson.id}`)
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||||
|
.send({ color: null });
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(body.color).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should mark a person as favorite', async () => {
|
||||||
|
const person = await utils.createPerson(admin.accessToken, {
|
||||||
|
name: 'visible_person',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(person.isFavorite).toBe(false);
|
||||||
|
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.put(`/people/${person.id}`)
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||||
|
.send({ isFavorite: true });
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(body).toMatchObject({ isFavorite: true });
|
||||||
|
|
||||||
|
const person2 = await getPerson({ id: person.id }, { headers: asBearerAuth(admin.accessToken) });
|
||||||
|
expect(person2).toMatchObject({ id: person.id, isFavorite: true });
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('POST /people/:id/merge', () => {
|
describe('POST /people/:id/merge', () => {
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ describe('/shared-links', () => {
|
|||||||
await deleteUserAdmin({ id: user2.userId, userAdminDeleteDto: {} }, { headers: asBearerAuth(admin.accessToken) });
|
await deleteUserAdmin({ id: user2.userId, userAdminDeleteDto: {} }, { headers: asBearerAuth(admin.accessToken) });
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /share/${key}', () => {
|
describe('GET /share/:key', () => {
|
||||||
it('should have correct asset count in meta tag for non-empty album', async () => {
|
it('should have correct asset count in meta tag for non-empty album', async () => {
|
||||||
const resp = await request(shareUrl).get(`/${linkWithMetadata.key}`);
|
const resp = await request(shareUrl).get(`/${linkWithMetadata.key}`);
|
||||||
expect(resp.status).toBe(200);
|
expect(resp.status).toBe(200);
|
||||||
@@ -139,7 +139,10 @@ describe('/shared-links', () => {
|
|||||||
expect(body).toEqual(
|
expect(body).toEqual(
|
||||||
expect.arrayContaining([
|
expect.arrayContaining([
|
||||||
expect.objectContaining({ id: linkWithAlbum.id }),
|
expect.objectContaining({ id: linkWithAlbum.id }),
|
||||||
expect.objectContaining({ id: linkWithAssets.id }),
|
expect.objectContaining({
|
||||||
|
id: linkWithAssets.id,
|
||||||
|
assets: expect.arrayContaining([expect.objectContaining({ id: asset1.id })]),
|
||||||
|
}),
|
||||||
expect.objectContaining({ id: linkWithPassword.id }),
|
expect.objectContaining({ id: linkWithPassword.id }),
|
||||||
expect.objectContaining({ id: linkWithMetadata.id }),
|
expect.objectContaining({ id: linkWithMetadata.id }),
|
||||||
expect.objectContaining({ id: linkWithoutMetadata.id }),
|
expect.objectContaining({ id: linkWithoutMetadata.id }),
|
||||||
@@ -147,6 +150,30 @@ describe('/shared-links', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should filter on albumId', async () => {
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.get(`/shared-links?albumId=${album.id}`)
|
||||||
|
.set('Authorization', `Bearer ${user1.accessToken}`);
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(body).toHaveLength(2);
|
||||||
|
expect(body).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({ id: linkWithAlbum.id }),
|
||||||
|
expect.objectContaining({ id: linkWithPassword.id }),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should find 0 albums', async () => {
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.get(`/shared-links?albumId=${uuidDto.notFound}`)
|
||||||
|
.set('Authorization', `Bearer ${user1.accessToken}`);
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(body).toHaveLength(0);
|
||||||
|
});
|
||||||
|
|
||||||
it('should not get shared links created by other users', async () => {
|
it('should not get shared links created by other users', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get('/shared-links')
|
.get('/shared-links')
|
||||||
@@ -177,6 +204,12 @@ describe('/shared-links', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should increment the view count', async () => {
|
||||||
|
const request1 = await request(app).get('/shared-links/me').query({ key: linkWithAlbum.key });
|
||||||
|
const request2 = await request(app).get('/shared-links/me').query({ key: linkWithAlbum.key });
|
||||||
|
expect(request2.body.viewCount).toBe(request1.body.viewCount + 1);
|
||||||
|
});
|
||||||
|
|
||||||
it('should return unauthorized for incorrect shared link', async () => {
|
it('should return unauthorized for incorrect shared link', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get('/shared-links/me')
|
.get('/shared-links/me')
|
||||||
|
|||||||
@@ -356,5 +356,24 @@ describe('/admin/users', () => {
|
|||||||
expect(status).toBe(403);
|
expect(status).toBe(403);
|
||||||
expect(body).toEqual(errorDto.forbidden);
|
expect(body).toEqual(errorDto.forbidden);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should restore a user', async () => {
|
||||||
|
const user = await utils.userSetup(admin.accessToken, createUserDto.create('restore'));
|
||||||
|
|
||||||
|
await deleteUserAdmin({ id: user.userId, userAdminDeleteDto: {} }, { headers: asBearerAuth(admin.accessToken) });
|
||||||
|
|
||||||
|
const { status, body } = await request(app)
|
||||||
|
.post(`/admin/users/${user.userId}/restore`)
|
||||||
|
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(body).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: user.userId,
|
||||||
|
email: user.userEmail,
|
||||||
|
status: 'active',
|
||||||
|
deletedAt: null,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import {
|
|||||||
CheckExistingAssetsDto,
|
CheckExistingAssetsDto,
|
||||||
CreateAlbumDto,
|
CreateAlbumDto,
|
||||||
CreateLibraryDto,
|
CreateLibraryDto,
|
||||||
|
JobCommandDto,
|
||||||
|
JobName,
|
||||||
MetadataSearchDto,
|
MetadataSearchDto,
|
||||||
Permission,
|
Permission,
|
||||||
PersonCreateDto,
|
PersonCreateDto,
|
||||||
@@ -28,7 +30,9 @@ import {
|
|||||||
getAssetInfo,
|
getAssetInfo,
|
||||||
getConfigDefaults,
|
getConfigDefaults,
|
||||||
login,
|
login,
|
||||||
|
scanLibrary,
|
||||||
searchAssets,
|
searchAssets,
|
||||||
|
sendJobCommand,
|
||||||
setBaseUrl,
|
setBaseUrl,
|
||||||
signUpAdmin,
|
signUpAdmin,
|
||||||
tagAssets,
|
tagAssets,
|
||||||
@@ -475,6 +479,9 @@ export const utils = {
|
|||||||
tagAssets: (accessToken: string, tagId: string, assetIds: string[]) =>
|
tagAssets: (accessToken: string, tagId: string, assetIds: string[]) =>
|
||||||
tagAssets({ id: tagId, bulkIdsDto: { ids: assetIds } }, { headers: asBearerAuth(accessToken) }),
|
tagAssets({ id: tagId, bulkIdsDto: { ids: assetIds } }, { headers: asBearerAuth(accessToken) }),
|
||||||
|
|
||||||
|
jobCommand: async (accessToken: string, jobName: JobName, jobCommandDto: JobCommandDto) =>
|
||||||
|
sendJobCommand({ id: jobName, jobCommandDto }, { headers: asBearerAuth(accessToken) }),
|
||||||
|
|
||||||
setAuthCookies: async (context: BrowserContext, accessToken: string, domain = '127.0.0.1') =>
|
setAuthCookies: async (context: BrowserContext, accessToken: string, domain = '127.0.0.1') =>
|
||||||
await context.addCookies([
|
await context.addCookies([
|
||||||
{
|
{
|
||||||
@@ -547,6 +554,14 @@ export const utils = {
|
|||||||
await immichCli(['login', app, `${key.secret}`]);
|
await immichCli(['login', app, `${key.secret}`]);
|
||||||
return key.secret;
|
return key.secret;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
scan: async (accessToken: string, id: string) => {
|
||||||
|
await scanLibrary({ id }, { headers: asBearerAuth(accessToken) });
|
||||||
|
|
||||||
|
await utils.waitForQueueFinish(accessToken, 'library');
|
||||||
|
await utils.waitForQueueFinish(accessToken, 'sidecar');
|
||||||
|
await utils.waitForQueueFinish(accessToken, 'metadataExtraction');
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
utils.initSdk();
|
utils.initSdk();
|
||||||
|
|||||||
12
i18n/en.json
12
i18n/en.json
@@ -1,4 +1,6 @@
|
|||||||
{
|
{
|
||||||
|
"search_by_description_example": "Hiking day in Sapa",
|
||||||
|
"search_by_description": "Search by description",
|
||||||
"about": "About",
|
"about": "About",
|
||||||
"account": "Account",
|
"account": "Account",
|
||||||
"account_settings": "Account Settings",
|
"account_settings": "Account Settings",
|
||||||
@@ -434,6 +436,7 @@
|
|||||||
"back_close_deselect": "Back, close, or deselect",
|
"back_close_deselect": "Back, close, or deselect",
|
||||||
"backward": "Backward",
|
"backward": "Backward",
|
||||||
"birthdate_saved": "Date of birth saved successfully",
|
"birthdate_saved": "Date of birth saved successfully",
|
||||||
|
"show_shared_links": "Show shared links",
|
||||||
"birthdate_set_description": "Date of birth is used to calculate the age of this person at the time of a photo.",
|
"birthdate_set_description": "Date of birth is used to calculate the age of this person at the time of a photo.",
|
||||||
"blurred_background": "Blurred background",
|
"blurred_background": "Blurred background",
|
||||||
"bugs_and_feature_requests": "Bugs & Feature Requests",
|
"bugs_and_feature_requests": "Bugs & Feature Requests",
|
||||||
@@ -766,8 +769,10 @@
|
|||||||
"go_to_search": "Go to search",
|
"go_to_search": "Go to search",
|
||||||
"go_to_folder": "Go to folder",
|
"go_to_folder": "Go to folder",
|
||||||
"group_albums_by": "Group albums by...",
|
"group_albums_by": "Group albums by...",
|
||||||
|
"group_country": "Group by country",
|
||||||
"group_no": "No grouping",
|
"group_no": "No grouping",
|
||||||
"group_owner": "Group by owner",
|
"group_owner": "Group by owner",
|
||||||
|
"group_places_by": "Group places by...",
|
||||||
"group_year": "Group by year",
|
"group_year": "Group by year",
|
||||||
"has_quota": "Has quota",
|
"has_quota": "Has quota",
|
||||||
"hi_user": "Hi {name} ({email})",
|
"hi_user": "Hi {name} ({email})",
|
||||||
@@ -800,6 +805,7 @@
|
|||||||
"include_shared_albums": "Include shared albums",
|
"include_shared_albums": "Include shared albums",
|
||||||
"include_shared_partner_assets": "Include shared partner assets",
|
"include_shared_partner_assets": "Include shared partner assets",
|
||||||
"individual_share": "Individual share",
|
"individual_share": "Individual share",
|
||||||
|
"individual_shares": "Individual shares",
|
||||||
"info": "Info",
|
"info": "Info",
|
||||||
"interval": {
|
"interval": {
|
||||||
"day_at_onepm": "Every day at 1pm",
|
"day_at_onepm": "Every day at 1pm",
|
||||||
@@ -810,6 +816,7 @@
|
|||||||
"invite_people": "Invite People",
|
"invite_people": "Invite People",
|
||||||
"invite_to_album": "Invite to album",
|
"invite_to_album": "Invite to album",
|
||||||
"items_count": "{count, plural, one {# item} other {# items}}",
|
"items_count": "{count, plural, one {# item} other {# items}}",
|
||||||
|
"views_count": "{count, plural, one {# view} other {# views}}",
|
||||||
"jobs": "Jobs",
|
"jobs": "Jobs",
|
||||||
"keep": "Keep",
|
"keep": "Keep",
|
||||||
"keep_all": "Keep All",
|
"keep_all": "Keep All",
|
||||||
@@ -985,6 +992,7 @@
|
|||||||
"pick_a_location": "Pick a location",
|
"pick_a_location": "Pick a location",
|
||||||
"place": "Place",
|
"place": "Place",
|
||||||
"places": "Places",
|
"places": "Places",
|
||||||
|
"places_count": "{count, plural, one {{count, number} Place} other {{count, number} Places}}",
|
||||||
"play": "Play",
|
"play": "Play",
|
||||||
"play_memories": "Play memories",
|
"play_memories": "Play memories",
|
||||||
"play_motion_photo": "Play Motion Photo",
|
"play_motion_photo": "Play Motion Photo",
|
||||||
@@ -1167,6 +1175,7 @@
|
|||||||
"shared_from_partner": "Photos from {partner}",
|
"shared_from_partner": "Photos from {partner}",
|
||||||
"shared_link_options": "Shared link options",
|
"shared_link_options": "Shared link options",
|
||||||
"shared_links": "Shared links",
|
"shared_links": "Shared links",
|
||||||
|
"shared_links_description": "Share photos and videos with a link",
|
||||||
"shared_photos_and_videos_count": "{assetCount, plural, other {# shared photos & videos.}}",
|
"shared_photos_and_videos_count": "{assetCount, plural, other {# shared photos & videos.}}",
|
||||||
"shared_with_partner": "Shared with {partner}",
|
"shared_with_partner": "Shared with {partner}",
|
||||||
"sharing": "Sharing",
|
"sharing": "Sharing",
|
||||||
@@ -1276,6 +1285,7 @@
|
|||||||
"unfavorite": "Unfavorite",
|
"unfavorite": "Unfavorite",
|
||||||
"unhide_person": "Unhide person",
|
"unhide_person": "Unhide person",
|
||||||
"unknown": "Unknown",
|
"unknown": "Unknown",
|
||||||
|
"unknown_country": "Unknown Country",
|
||||||
"unknown_year": "Unknown Year",
|
"unknown_year": "Unknown Year",
|
||||||
"unlimited": "Unlimited",
|
"unlimited": "Unlimited",
|
||||||
"unlink_motion_video": "Unlink motion video",
|
"unlink_motion_video": "Unlink motion video",
|
||||||
@@ -1350,4 +1360,4 @@
|
|||||||
"yes": "Yes",
|
"yes": "Yes",
|
||||||
"you_dont_have_any_shared_links": "You don't have any shared links",
|
"you_dont_have_any_shared_links": "You don't have any shared links",
|
||||||
"zoom_image": "Zoom Image"
|
"zoom_image": "Zoom Image"
|
||||||
}
|
}
|
||||||
|
|||||||
874
i18n/fa.json
874
i18n/fa.json
@@ -312,157 +312,157 @@
|
|||||||
"admin_password": "رمز عبور مدیر",
|
"admin_password": "رمز عبور مدیر",
|
||||||
"administration": "مدیریت",
|
"administration": "مدیریت",
|
||||||
"advanced": "پیشرفته",
|
"advanced": "پیشرفته",
|
||||||
"album_added": "",
|
"album_added": "آلبوم اضافه شد",
|
||||||
"album_added_notification_setting_description": "",
|
"album_added_notification_setting_description": "",
|
||||||
"album_cover_updated": "",
|
"album_cover_updated": "جلد آلبوم بهروزرسانی شد",
|
||||||
"album_info_updated": "",
|
"album_info_updated": "اطلاعات آلبوم بهروزرسانی شد",
|
||||||
"album_name": "",
|
"album_name": "نام آلبوم",
|
||||||
"album_options": "",
|
"album_options": "گزینههای آلبوم",
|
||||||
"album_updated": "",
|
"album_updated": "آلبوم بهروزرسانی شد",
|
||||||
"album_updated_setting_description": "",
|
"album_updated_setting_description": "",
|
||||||
"albums": "",
|
"albums": "آلبومها",
|
||||||
"albums_count": "",
|
"albums_count": "",
|
||||||
"all": "",
|
"all": "همه",
|
||||||
"all_people": "",
|
"all_people": "همه افراد",
|
||||||
"allow_dark_mode": "",
|
"allow_dark_mode": "اجازه دادن به حالت تاریک",
|
||||||
"allow_edits": "",
|
"allow_edits": "اجازه ویرایش",
|
||||||
"api_key": "",
|
"api_key": "کلید API",
|
||||||
"api_keys": "",
|
"api_keys": "کلیدهای API",
|
||||||
"app_settings": "",
|
"app_settings": "تنظیمات برنامه",
|
||||||
"appears_in": "",
|
"appears_in": "ظاهر میشود در",
|
||||||
"archive": "",
|
"archive": "بایگانی",
|
||||||
"archive_or_unarchive_photo": "",
|
"archive_or_unarchive_photo": "",
|
||||||
"archive_size": "",
|
"archive_size": "اندازه بایگانی",
|
||||||
"archive_size_description": "",
|
"archive_size_description": "",
|
||||||
"asset_offline": "",
|
"asset_offline": "محتوا آفلاین",
|
||||||
"assets": "",
|
"assets": "محتواها",
|
||||||
"authorized_devices": "",
|
"authorized_devices": "دستگاههای مجاز",
|
||||||
"back": "",
|
"back": "بازگشت",
|
||||||
"backward": "",
|
"backward": "عقب",
|
||||||
"blurred_background": "",
|
"blurred_background": "پسزمینه محو",
|
||||||
"bulk_delete_duplicates_confirmation": "",
|
"bulk_delete_duplicates_confirmation": "",
|
||||||
"bulk_keep_duplicates_confirmation": "",
|
"bulk_keep_duplicates_confirmation": "",
|
||||||
"bulk_trash_duplicates_confirmation": "",
|
"bulk_trash_duplicates_confirmation": "",
|
||||||
"camera": "",
|
"camera": "دوربین",
|
||||||
"camera_brand": "",
|
"camera_brand": "برند دوربین",
|
||||||
"camera_model": "",
|
"camera_model": "مدل دوربین",
|
||||||
"cancel": "",
|
"cancel": "لغو",
|
||||||
"cancel_search": "",
|
"cancel_search": "لغو جستجو",
|
||||||
"cannot_merge_people": "",
|
"cannot_merge_people": "نمیتوان افراد را ادغام کرد",
|
||||||
"cannot_update_the_description": "",
|
"cannot_update_the_description": "نمیتوان توضیحات را بهروزرسانی کرد",
|
||||||
"change_date": "",
|
"change_date": "تغییر تاریخ",
|
||||||
"change_expiration_time": "",
|
"change_expiration_time": "تغییر زمان انقضا",
|
||||||
"change_location": "",
|
"change_location": "تغییر مکان",
|
||||||
"change_name": "",
|
"change_name": "تغییر نام",
|
||||||
"change_name_successfully": "",
|
"change_name_successfully": "نام با موفقیت تغییر یافت",
|
||||||
"change_password": "",
|
"change_password": "تغییر رمز عبور",
|
||||||
"change_your_password": "",
|
"change_your_password": "رمز عبور خود را تغییر دهید",
|
||||||
"changed_visibility_successfully": "",
|
"changed_visibility_successfully": "",
|
||||||
"check_all": "",
|
"check_all": "انتخاب همه",
|
||||||
"check_logs": "",
|
"check_logs": "بررسی لاگها",
|
||||||
"choose_matching_people_to_merge": "",
|
"choose_matching_people_to_merge": "",
|
||||||
"city": "",
|
"city": "شهر",
|
||||||
"clear": "",
|
"clear": "پاک کردن",
|
||||||
"clear_all": "",
|
"clear_all": "پاک کردن همه",
|
||||||
"clear_message": "",
|
"clear_message": "پاک کردن پیام",
|
||||||
"clear_value": "",
|
"clear_value": "پاک کردن مقدار",
|
||||||
"close": "",
|
"close": "بستن",
|
||||||
"collapse_all": "",
|
"collapse_all": "جمع کردن همه",
|
||||||
"color_theme": "",
|
"color_theme": "تم رنگ",
|
||||||
"comment_options": "",
|
"comment_options": "گزینههای نظر",
|
||||||
"comments_are_disabled": "",
|
"comments_are_disabled": "نظرات غیرفعال هستند",
|
||||||
"confirm": "",
|
"confirm": "تأیید",
|
||||||
"confirm_admin_password": "",
|
"confirm_admin_password": "تأیید رمز عبور مدیر",
|
||||||
"confirm_delete_shared_link": "",
|
"confirm_delete_shared_link": "",
|
||||||
"confirm_password": "",
|
"confirm_password": "تأیید رمز عبور",
|
||||||
"contain": "",
|
"contain": "شامل",
|
||||||
"context": "",
|
"context": "زمینه",
|
||||||
"continue": "",
|
"continue": "ادامه",
|
||||||
"copied_image_to_clipboard": "",
|
"copied_image_to_clipboard": "تصویر به کلیپبورد کپی شد.",
|
||||||
"copied_to_clipboard": "",
|
"copied_to_clipboard": "به کلیپبورد کپی شد!",
|
||||||
"copy_error": "",
|
"copy_error": "خطا در کپی",
|
||||||
"copy_file_path": "",
|
"copy_file_path": "کپی مسیر فایل",
|
||||||
"copy_image": "",
|
"copy_image": "کپی تصویر",
|
||||||
"copy_link": "",
|
"copy_link": "کپی لینک",
|
||||||
"copy_link_to_clipboard": "",
|
"copy_link_to_clipboard": "کپی لینک به کلیپبورد",
|
||||||
"copy_password": "",
|
"copy_password": "کپی رمز عبور",
|
||||||
"copy_to_clipboard": "",
|
"copy_to_clipboard": "کپی به کلیپبورد",
|
||||||
"country": "",
|
"country": "کشور",
|
||||||
"cover": "",
|
"cover": "جلد",
|
||||||
"covers": "",
|
"covers": "جلدها",
|
||||||
"create": "",
|
"create": "ایجاد",
|
||||||
"create_album": "",
|
"create_album": "ایجاد آلبوم",
|
||||||
"create_library": "",
|
"create_library": "ایجاد کتابخانه",
|
||||||
"create_link": "",
|
"create_link": "ایجاد لینک",
|
||||||
"create_link_to_share": "",
|
"create_link_to_share": "ایجاد لینک برای اشتراکگذاری",
|
||||||
"create_new_person": "",
|
"create_new_person": "ایجاد فرد جدید",
|
||||||
"create_new_user": "",
|
"create_new_user": "ایجاد کاربر جدید",
|
||||||
"create_user": "",
|
"create_user": "ایجاد کاربر",
|
||||||
"created": "",
|
"created": "ایجاد شد",
|
||||||
"current_device": "",
|
"current_device": "دستگاه فعلی",
|
||||||
"custom_locale": "",
|
"custom_locale": "",
|
||||||
"custom_locale_description": "",
|
"custom_locale_description": "",
|
||||||
"dark": "",
|
"dark": "تاریک",
|
||||||
"date_after": "",
|
"date_after": "تاریخ پس از",
|
||||||
"date_and_time": "",
|
"date_and_time": "تاریخ و زمان",
|
||||||
"date_before": "",
|
"date_before": "تاریخ قبل از",
|
||||||
"date_range": "",
|
"date_range": "بازه زمانی",
|
||||||
"day": "",
|
"day": "روز",
|
||||||
"deduplicate_all": "",
|
"deduplicate_all": "حذف تکراریها به صورت کامل",
|
||||||
"default_locale": "",
|
"default_locale": "",
|
||||||
"default_locale_description": "",
|
"default_locale_description": "",
|
||||||
"delete": "",
|
"delete": "حذف",
|
||||||
"delete_album": "",
|
"delete_album": "حذف آلبوم",
|
||||||
"delete_api_key_prompt": "",
|
"delete_api_key_prompt": "",
|
||||||
"delete_duplicates_confirmation": "",
|
"delete_duplicates_confirmation": "",
|
||||||
"delete_key": "",
|
"delete_key": "حذف کلید",
|
||||||
"delete_library": "",
|
"delete_library": "حذف کتابخانه",
|
||||||
"delete_link": "",
|
"delete_link": "حذف لینک",
|
||||||
"delete_shared_link": "",
|
"delete_shared_link": "حذف لینک اشتراکی",
|
||||||
"delete_user": "",
|
"delete_user": "حذف کاربر",
|
||||||
"deleted_shared_link": "",
|
"deleted_shared_link": "لینک اشتراکی حذف شد",
|
||||||
"description": "",
|
"description": "توضیحات",
|
||||||
"details": "",
|
"details": "جزئیات",
|
||||||
"direction": "",
|
"direction": "جهت",
|
||||||
"disabled": "",
|
"disabled": "غیرفعال",
|
||||||
"disallow_edits": "",
|
"disallow_edits": "عدم اجازه ویرایش",
|
||||||
"discover": "",
|
"discover": "کشف کردن",
|
||||||
"dismiss_all_errors": "",
|
"dismiss_all_errors": "رد تمام خطاها",
|
||||||
"dismiss_error": "",
|
"dismiss_error": "رد خطا",
|
||||||
"display_options": "",
|
"display_options": "گزینههای نمایش",
|
||||||
"display_order": "",
|
"display_order": "ترتیب نمایش",
|
||||||
"display_original_photos": "",
|
"display_original_photos": "نمایش عکسهای اصلی",
|
||||||
"display_original_photos_setting_description": "",
|
"display_original_photos_setting_description": "",
|
||||||
"done": "",
|
"done": "انجام شد",
|
||||||
"download": "",
|
"download": "دانلود",
|
||||||
"download_settings": "",
|
"download_settings": "تنظیمات دانلود",
|
||||||
"download_settings_description": "",
|
"download_settings_description": "مدیریت تنظیمات مرتبط با دانلود محتوا",
|
||||||
"downloading": "",
|
"downloading": "در حال دانلود",
|
||||||
"duplicates": "",
|
"duplicates": "تکراریها",
|
||||||
"duplicates_description": "",
|
"duplicates_description": "",
|
||||||
"duration": "",
|
"duration": "مدت زمان",
|
||||||
"edit_album": "",
|
"edit_album": "ویرایش آلبوم",
|
||||||
"edit_avatar": "",
|
"edit_avatar": "ویرایش آواتار",
|
||||||
"edit_date": "",
|
"edit_date": "ویرایش تاریخ",
|
||||||
"edit_date_and_time": "",
|
"edit_date_and_time": "ویرایش تاریخ و زمان",
|
||||||
"edit_exclusion_pattern": "",
|
"edit_exclusion_pattern": "ویرایش الگوی استثناء",
|
||||||
"edit_faces": "",
|
"edit_faces": "ویرایش چهرهها",
|
||||||
"edit_import_path": "",
|
"edit_import_path": "",
|
||||||
"edit_import_paths": "",
|
"edit_import_paths": "",
|
||||||
"edit_key": "",
|
"edit_key": "ویرایش کلید",
|
||||||
"edit_link": "",
|
"edit_link": "ویرایش لینک",
|
||||||
"edit_location": "",
|
"edit_location": "ویرایش مکان",
|
||||||
"edit_name": "",
|
"edit_name": "ویرایش نام",
|
||||||
"edit_people": "",
|
"edit_people": "ویرایش افراد",
|
||||||
"edit_title": "",
|
"edit_title": "ویرایش عنوان",
|
||||||
"edit_user": "",
|
"edit_user": "ویرایش کاربر",
|
||||||
"edited": "",
|
"edited": "ویرایش شد",
|
||||||
"editor": "",
|
"editor": "ویرایشگر",
|
||||||
"email": "",
|
"email": "ایمیل",
|
||||||
"empty_trash": "",
|
"empty_trash": "خالی کردن سطل زباله",
|
||||||
"end_date": "",
|
"end_date": "تاریخ پایان",
|
||||||
"error": "",
|
"error": "خطا",
|
||||||
"error_loading_image": "",
|
"error_loading_image": "خطا در بارگذاری تصویر",
|
||||||
"errors": {
|
"errors": {
|
||||||
"exclusion_pattern_already_exists": "",
|
"exclusion_pattern_already_exists": "",
|
||||||
"import_path_already_exists": "",
|
"import_path_already_exists": "",
|
||||||
@@ -530,400 +530,400 @@
|
|||||||
"unable_to_update_timeline_display_status": "",
|
"unable_to_update_timeline_display_status": "",
|
||||||
"unable_to_update_user": ""
|
"unable_to_update_user": ""
|
||||||
},
|
},
|
||||||
"exit_slideshow": "",
|
"exit_slideshow": "خروج از نمایش اسلاید",
|
||||||
"expand_all": "",
|
"expand_all": "باز کردن همه",
|
||||||
"expire_after": "",
|
"expire_after": "منقضی شدن بعد از",
|
||||||
"expired": "",
|
"expired": "منقضی شده",
|
||||||
"explore": "",
|
"explore": "کاوش کردن",
|
||||||
"export": "",
|
"export": "صادر کردن",
|
||||||
"export_as_json": "",
|
"export_as_json": "صادر کردن بهصورت JSON",
|
||||||
"extension": "",
|
"extension": "پسوند",
|
||||||
"external": "",
|
"external": "خارجی",
|
||||||
"external_libraries": "",
|
"external_libraries": "کتابخانههای خارجی",
|
||||||
"favorite": "",
|
"favorite": "علاقهمندی",
|
||||||
"favorite_or_unfavorite_photo": "",
|
"favorite_or_unfavorite_photo": "",
|
||||||
"favorites": "",
|
"favorites": "علاقهمندیها",
|
||||||
"feature_photo_updated": "",
|
"feature_photo_updated": "",
|
||||||
"file_name": "",
|
"file_name": "نام فایل",
|
||||||
"file_name_or_extension": "",
|
"file_name_or_extension": "نام فایل یا پسوند",
|
||||||
"filename": "",
|
"filename": "نام فایل",
|
||||||
"filetype": "",
|
"filetype": "نوع فایل",
|
||||||
"filter_people": "",
|
"filter_people": "فیلتر افراد",
|
||||||
"find_them_fast": "",
|
"find_them_fast": "",
|
||||||
"fix_incorrect_match": "",
|
"fix_incorrect_match": "رفع تطابق نادرست",
|
||||||
"forward": "",
|
"forward": "جلو",
|
||||||
"general": "",
|
"general": "عمومی",
|
||||||
"get_help": "",
|
"get_help": "دریافت کمک",
|
||||||
"getting_started": "",
|
"getting_started": "شروع به کار",
|
||||||
"go_back": "",
|
"go_back": "بازگشت",
|
||||||
"go_to_search": "",
|
"go_to_search": "رفتن به جستجو",
|
||||||
"group_albums_by": "",
|
"group_albums_by": "گروهبندی آلبومها براساس...",
|
||||||
"has_quota": "",
|
"has_quota": "دارای سهمیه",
|
||||||
"hide_gallery": "",
|
"hide_gallery": "پنهان کردن گالری",
|
||||||
"hide_password": "",
|
"hide_password": "پنهان کردن رمز عبور",
|
||||||
"hide_person": "",
|
"hide_person": "پنهان کردن فرد",
|
||||||
"host": "",
|
"host": "میزبان",
|
||||||
"hour": "",
|
"hour": "ساعت",
|
||||||
"image": "",
|
"image": "تصویر",
|
||||||
"immich_logo": "",
|
"immich_logo": "لوگوی Immich",
|
||||||
"immich_web_interface": "",
|
"immich_web_interface": "رابط وب Immich",
|
||||||
"import_from_json": "",
|
"import_from_json": "وارد کردن از JSON",
|
||||||
"import_path": "",
|
"import_path": "مسیر وارد کردن",
|
||||||
"in_albums": "",
|
"in_albums": "",
|
||||||
"in_archive": "",
|
"in_archive": "در بایگانی",
|
||||||
"include_archived": "",
|
"include_archived": "شامل بایگانی شدهها",
|
||||||
"include_shared_albums": "",
|
"include_shared_albums": "شامل آلبومهای اشتراکی",
|
||||||
"include_shared_partner_assets": "",
|
"include_shared_partner_assets": "",
|
||||||
"individual_share": "",
|
"individual_share": "اشتراک فردی",
|
||||||
"info": "",
|
"info": "اطلاعات",
|
||||||
"interval": {
|
"interval": {
|
||||||
"day_at_onepm": "",
|
"day_at_onepm": "",
|
||||||
"hours": "",
|
"hours": "",
|
||||||
"night_at_midnight": "",
|
"night_at_midnight": "",
|
||||||
"night_at_twoam": ""
|
"night_at_twoam": ""
|
||||||
},
|
},
|
||||||
"invite_people": "",
|
"invite_people": "دعوت افراد",
|
||||||
"invite_to_album": "",
|
"invite_to_album": "دعوت به آلبوم",
|
||||||
"jobs": "",
|
"jobs": "وظایف",
|
||||||
"keep": "",
|
"keep": "نگه داشتن",
|
||||||
"keep_all": "",
|
"keep_all": "نگه داشتن همه",
|
||||||
"keyboard_shortcuts": "",
|
"keyboard_shortcuts": "میانبرهای صفحهکلید",
|
||||||
"language": "",
|
"language": "زبان",
|
||||||
"language_setting_description": "",
|
"language_setting_description": "انتخاب زبان دلخواه شما",
|
||||||
"last_seen": "",
|
"last_seen": "آخرین مشاهده",
|
||||||
"leave": "",
|
"leave": "ترک کردن",
|
||||||
"let_others_respond": "",
|
"let_others_respond": "اجازه به دیگران برای پاسخگویی",
|
||||||
"level": "",
|
"level": "سطح",
|
||||||
"library": "",
|
"library": "کتابخانه",
|
||||||
"library_options": "",
|
"library_options": "گزینههای کتابخانه",
|
||||||
"light": "",
|
"light": "روشن",
|
||||||
"link_options": "",
|
"link_options": "گزینههای لینک",
|
||||||
"link_to_oauth": "",
|
"link_to_oauth": "اتصال به OAuth",
|
||||||
"linked_oauth_account": "",
|
"linked_oauth_account": "حساب OAuth متصل شده",
|
||||||
"list": "",
|
"list": "لیست",
|
||||||
"loading": "",
|
"loading": "در حال بارگذاری",
|
||||||
"loading_search_results_failed": "",
|
"loading_search_results_failed": "بارگذاری نتایج جستجو ناموفق بود",
|
||||||
"log_out": "",
|
"log_out": "خروج از سیستم",
|
||||||
"log_out_all_devices": "",
|
"log_out_all_devices": "خروج از همه دستگاهها",
|
||||||
"login_has_been_disabled": "",
|
"login_has_been_disabled": "ورود غیرفعال شده است.",
|
||||||
"look": "",
|
"look": "نگاه کردن",
|
||||||
"loop_videos": "",
|
"loop_videos": "پخش مداوم ویدئوها",
|
||||||
"loop_videos_description": "",
|
"loop_videos_description": "",
|
||||||
"make": "",
|
"make": "ساختن",
|
||||||
"manage_shared_links": "",
|
"manage_shared_links": "مدیریت لینکهای اشتراکی",
|
||||||
"manage_sharing_with_partners": "",
|
"manage_sharing_with_partners": "",
|
||||||
"manage_the_app_settings": "",
|
"manage_the_app_settings": "مدیریت تنظیمات برنامه",
|
||||||
"manage_your_account": "",
|
"manage_your_account": "مدیریت حساب کاربری شما",
|
||||||
"manage_your_api_keys": "",
|
"manage_your_api_keys": "مدیریت کلیدهای API شما",
|
||||||
"manage_your_devices": "",
|
"manage_your_devices": "مدیریت دستگاههای متصل",
|
||||||
"manage_your_oauth_connection": "",
|
"manage_your_oauth_connection": "مدیریت اتصال OAuth شما",
|
||||||
"map": "",
|
"map": "نقشه",
|
||||||
"map_marker_with_image": "",
|
"map_marker_with_image": "",
|
||||||
"map_settings": "",
|
"map_settings": "تنظیمات نقشه",
|
||||||
"matches": "",
|
"matches": "تطابقها",
|
||||||
"media_type": "",
|
"media_type": "نوع رسانه",
|
||||||
"memories": "",
|
"memories": "خاطرات",
|
||||||
"memories_setting_description": "",
|
"memories_setting_description": "",
|
||||||
"memory": "",
|
"memory": "خاطره",
|
||||||
"menu": "",
|
"menu": "منو",
|
||||||
"merge": "",
|
"merge": "ادغام",
|
||||||
"merge_people": "",
|
"merge_people": "ادغام افراد",
|
||||||
"merge_people_limit": "",
|
"merge_people_limit": "",
|
||||||
"merge_people_prompt": "",
|
"merge_people_prompt": "",
|
||||||
"merge_people_successfully": "",
|
"merge_people_successfully": "ادغام افراد با موفقیت انجام شد",
|
||||||
"minimize": "",
|
"minimize": "کوچک کردن",
|
||||||
"minute": "",
|
"minute": "دقیقه",
|
||||||
"missing": "",
|
"missing": "گمشده",
|
||||||
"model": "",
|
"model": "مدل",
|
||||||
"month": "",
|
"month": "ماه",
|
||||||
"more": "",
|
"more": "بیشتر",
|
||||||
"moved_to_trash": "",
|
"moved_to_trash": "به سطل زباله منتقل شد",
|
||||||
"my_albums": "",
|
"my_albums": "آلبومهای من",
|
||||||
"name": "",
|
"name": "نام",
|
||||||
"name_or_nickname": "",
|
"name_or_nickname": "نام یا لقب",
|
||||||
"never": "",
|
"never": "هرگز",
|
||||||
"new_api_key": "",
|
"new_api_key": "کلید API جدید",
|
||||||
"new_password": "",
|
"new_password": "رمز عبور جدید",
|
||||||
"new_person": "",
|
"new_person": "فرد جدید",
|
||||||
"new_user_created": "",
|
"new_user_created": "کاربر جدید ایجاد شد",
|
||||||
"newest_first": "",
|
"newest_first": "جدیدترین ابتدا",
|
||||||
"next": "",
|
"next": "بعدی",
|
||||||
"next_memory": "",
|
"next_memory": "خاطره بعدی",
|
||||||
"no": "",
|
"no": "خیر",
|
||||||
"no_albums_message": "",
|
"no_albums_message": "",
|
||||||
"no_archived_assets_message": "",
|
"no_archived_assets_message": "",
|
||||||
"no_assets_message": "",
|
"no_assets_message": "",
|
||||||
"no_duplicates_found": "",
|
"no_duplicates_found": "هیچ تکراری یافت نشد.",
|
||||||
"no_exif_info_available": "",
|
"no_exif_info_available": "اطلاعات EXIF موجود نیست",
|
||||||
"no_explore_results_message": "",
|
"no_explore_results_message": "",
|
||||||
"no_favorites_message": "",
|
"no_favorites_message": "",
|
||||||
"no_libraries_message": "",
|
"no_libraries_message": "",
|
||||||
"no_name": "",
|
"no_name": "بدون نام",
|
||||||
"no_places": "",
|
"no_places": "مکانی یافت نشد",
|
||||||
"no_results": "",
|
"no_results": "نتیجهای یافت نشد",
|
||||||
"no_shared_albums_message": "",
|
"no_shared_albums_message": "",
|
||||||
"not_in_any_album": "",
|
"not_in_any_album": "در هیچ آلبومی نیست",
|
||||||
"note_apply_storage_label_to_previously_uploaded assets": "",
|
"note_apply_storage_label_to_previously_uploaded assets": "",
|
||||||
"note_unlimited_quota": "",
|
"note_unlimited_quota": "",
|
||||||
"notes": "",
|
"notes": "یادداشتها",
|
||||||
"notification_toggle_setting_description": "",
|
"notification_toggle_setting_description": "اعلانهای ایمیلی را فعال کنید",
|
||||||
"notifications": "",
|
"notifications": "اعلانها",
|
||||||
"notifications_setting_description": "",
|
"notifications_setting_description": "مدیریت اعلانها",
|
||||||
"oauth": "",
|
"oauth": "OAuth",
|
||||||
"offline": "",
|
"offline": "آفلاین",
|
||||||
"offline_paths": "",
|
"offline_paths": "مسیرهای آفلاین",
|
||||||
"offline_paths_description": "",
|
"offline_paths_description": "",
|
||||||
"ok": "",
|
"ok": "تأیید",
|
||||||
"oldest_first": "",
|
"oldest_first": "قدیمیترین ابتدا",
|
||||||
"online": "",
|
"online": "آنلاین",
|
||||||
"only_favorites": "",
|
"only_favorites": "فقط علاقهمندیها",
|
||||||
"open_the_search_filters": "",
|
"open_the_search_filters": "باز کردن فیلترهای جستجو",
|
||||||
"options": "",
|
"options": "گزینهها",
|
||||||
"organize_your_library": "",
|
"organize_your_library": "کتابخانه خود را سازماندهی کنید",
|
||||||
"other": "",
|
"other": "دیگر",
|
||||||
"other_devices": "",
|
"other_devices": "دستگاههای دیگر",
|
||||||
"other_variables": "",
|
"other_variables": "متغیرهای دیگر",
|
||||||
"owned": "",
|
"owned": "مالکیت",
|
||||||
"owner": "",
|
"owner": "مالک",
|
||||||
"partner": "",
|
"partner": "شریک",
|
||||||
"partner_can_access": "",
|
"partner_can_access": "{partner} میتواند دسترسی داشته باشد",
|
||||||
"partner_can_access_assets": "",
|
"partner_can_access_assets": "",
|
||||||
"partner_can_access_location": "",
|
"partner_can_access_location": "مکانهایی که عکسهای شما گرفته شدهاند",
|
||||||
"partner_sharing": "",
|
"partner_sharing": "اشتراکگذاری با شریک",
|
||||||
"partners": "",
|
"partners": "شرکا",
|
||||||
"password": "",
|
"password": "رمز عبور",
|
||||||
"password_does_not_match": "",
|
"password_does_not_match": "رمز عبور مطابقت ندارد",
|
||||||
"password_required": "",
|
"password_required": "رمز عبور مورد نیاز است",
|
||||||
"password_reset_success": "",
|
"password_reset_success": "بازنشانی رمز عبور موفقیتآمیز بود",
|
||||||
"past_durations": {
|
"past_durations": {
|
||||||
"days": "",
|
"days": "",
|
||||||
"hours": "",
|
"hours": "",
|
||||||
"years": ""
|
"years": ""
|
||||||
},
|
},
|
||||||
"path": "",
|
"path": "مسیر",
|
||||||
"pattern": "",
|
"pattern": "الگو",
|
||||||
"pause": "",
|
"pause": "توقف",
|
||||||
"pause_memories": "",
|
"pause_memories": "توقف خاطرات",
|
||||||
"paused": "",
|
"paused": "متوقف شده",
|
||||||
"pending": "",
|
"pending": "در انتظار",
|
||||||
"people": "",
|
"people": "افراد",
|
||||||
"people_sidebar_description": "",
|
"people_sidebar_description": "",
|
||||||
"permanent_deletion_warning": "",
|
"permanent_deletion_warning": "هشدار حذف دائمی",
|
||||||
"permanent_deletion_warning_setting_description": "",
|
"permanent_deletion_warning_setting_description": "نمایش هشدار هنگام حذف دائمی محتواها",
|
||||||
"permanently_delete": "",
|
"permanently_delete": "حذف دائمی",
|
||||||
"permanently_deleted_asset": "",
|
"permanently_deleted_asset": "محتوای حذف شده دائمی",
|
||||||
"person": "",
|
"person": "فرد",
|
||||||
"photos": "",
|
"photos": "عکسها",
|
||||||
"photos_count": "",
|
"photos_count": "",
|
||||||
"photos_from_previous_years": "",
|
"photos_from_previous_years": "عکسهای سالهای گذشته",
|
||||||
"pick_a_location": "",
|
"pick_a_location": "یک مکان انتخاب کنید",
|
||||||
"place": "",
|
"place": "مکان",
|
||||||
"places": "",
|
"places": "مکانها",
|
||||||
"play": "",
|
"play": "پخش",
|
||||||
"play_memories": "",
|
"play_memories": "پخش خاطرات",
|
||||||
"play_motion_photo": "",
|
"play_motion_photo": "پخش عکس متحرک",
|
||||||
"play_or_pause_video": "",
|
"play_or_pause_video": "پخش یا توقف ویدیو",
|
||||||
"port": "",
|
"port": "پورت",
|
||||||
"preset": "",
|
"preset": "پیشفرض",
|
||||||
"preview": "",
|
"preview": "پیشنمایش",
|
||||||
"previous": "",
|
"previous": "قبلی",
|
||||||
"previous_memory": "",
|
"previous_memory": "خاطره قبلی",
|
||||||
"previous_or_next_photo": "",
|
"previous_or_next_photo": "عکس قبلی یا بعدی",
|
||||||
"primary": "",
|
"primary": "اصلی",
|
||||||
"profile_picture_set": "",
|
"profile_picture_set": "تصویر پروفایل تنظیم شد.",
|
||||||
"public_share": "",
|
"public_share": "اشتراک عمومی",
|
||||||
"reaction_options": "",
|
"reaction_options": "گزینههای واکنش",
|
||||||
"read_changelog": "",
|
"read_changelog": "مطالعه تغییرات نسخه",
|
||||||
"recent": "",
|
"recent": "اخیر",
|
||||||
"recent_searches": "",
|
"recent_searches": "جستجوهای اخیر",
|
||||||
"refresh": "",
|
"refresh": "تازه سازی",
|
||||||
"refreshed": "",
|
"refreshed": "تازه سازی شد",
|
||||||
"refreshes_every_file": "",
|
"refreshes_every_file": "",
|
||||||
"remove": "",
|
"remove": "حذف",
|
||||||
"remove_deleted_assets": "",
|
"remove_deleted_assets": "حذف محتواهای حذفشده",
|
||||||
"remove_from_album": "",
|
"remove_from_album": "حذف از آلبوم",
|
||||||
"remove_from_favorites": "",
|
"remove_from_favorites": "حذف از علاقهمندیها",
|
||||||
"remove_from_shared_link": "",
|
"remove_from_shared_link": "",
|
||||||
"removed_api_key": "",
|
"removed_api_key": "",
|
||||||
"rename": "",
|
"rename": "تغییر نام",
|
||||||
"repair": "",
|
"repair": "تعمیر",
|
||||||
"repair_no_results_message": "",
|
"repair_no_results_message": "",
|
||||||
"replace_with_upload": "",
|
"replace_with_upload": "جایگزینی با آپلود",
|
||||||
"require_password": "",
|
"require_password": "",
|
||||||
"require_user_to_change_password_on_first_login": "",
|
"require_user_to_change_password_on_first_login": "",
|
||||||
"reset": "",
|
"reset": "بازنشانی",
|
||||||
"reset_password": "",
|
"reset_password": "بازنشانی رمز عبور",
|
||||||
"reset_people_visibility": "",
|
"reset_people_visibility": "",
|
||||||
"resolved_all_duplicates": "",
|
"resolved_all_duplicates": "",
|
||||||
"restore": "",
|
"restore": "بازیابی",
|
||||||
"restore_all": "",
|
"restore_all": "بازیابی همه",
|
||||||
"restore_user": "",
|
"restore_user": "بازیابی کاربر",
|
||||||
"resume": "",
|
"resume": "ادامه",
|
||||||
"retry_upload": "",
|
"retry_upload": "",
|
||||||
"review_duplicates": "",
|
"review_duplicates": "بررسی تکراریها",
|
||||||
"role": "",
|
"role": "نقش",
|
||||||
"save": "",
|
"save": "ذخیره",
|
||||||
"saved_api_key": "",
|
"saved_api_key": "",
|
||||||
"saved_profile": "",
|
"saved_profile": "پروفایل ذخیره شد",
|
||||||
"saved_settings": "",
|
"saved_settings": "تنظیمات ذخیره شد",
|
||||||
"say_something": "",
|
"say_something": "چیزی بگویید",
|
||||||
"scan_all_libraries": "",
|
"scan_all_libraries": "اسکن همه کتابخانهها",
|
||||||
"scan_settings": "",
|
"scan_settings": "تنظیمات اسکن",
|
||||||
"scanning_for_album": "",
|
"scanning_for_album": "",
|
||||||
"search": "",
|
"search": "جستجو",
|
||||||
"search_albums": "",
|
"search_albums": "جستجوی آلبومها",
|
||||||
"search_by_context": "",
|
"search_by_context": "جستجو براساس زمینه",
|
||||||
"search_camera_make": "",
|
"search_camera_make": "جستجوی برند دوربین...",
|
||||||
"search_camera_model": "",
|
"search_camera_model": "جستجوی مدل دوربین...",
|
||||||
"search_city": "",
|
"search_city": "جستجوی شهر...",
|
||||||
"search_country": "",
|
"search_country": "جستجوی کشور...",
|
||||||
"search_for_existing_person": "",
|
"search_for_existing_person": "جستجوی فرد موجود",
|
||||||
"search_people": "",
|
"search_people": "جستجوی افراد",
|
||||||
"search_places": "",
|
"search_places": "جستجوی مکانها",
|
||||||
"search_state": "",
|
"search_state": "جستجوی ایالت...",
|
||||||
"search_timezone": "",
|
"search_timezone": "جستجوی منطقه زمانی...",
|
||||||
"search_type": "",
|
"search_type": "نوع جستجو",
|
||||||
"search_your_photos": "",
|
"search_your_photos": "",
|
||||||
"searching_locales": "",
|
"searching_locales": "",
|
||||||
"second": "",
|
"second": "ثانیه",
|
||||||
"select_album_cover": "",
|
"select_album_cover": "انتخاب جلد آلبوم",
|
||||||
"select_all": "",
|
"select_all": "انتخاب همه",
|
||||||
"select_avatar_color": "",
|
"select_avatar_color": "انتخاب رنگ آواتار",
|
||||||
"select_face": "",
|
"select_face": "انتخاب چهره",
|
||||||
"select_featured_photo": "",
|
"select_featured_photo": "انتخاب عکس ویژه",
|
||||||
"select_keep_all": "",
|
"select_keep_all": "انتخاب نگهداری همه",
|
||||||
"select_library_owner": "",
|
"select_library_owner": "انتخاب مالک کتابخانه",
|
||||||
"select_new_face": "",
|
"select_new_face": "انتخاب چهره جدید",
|
||||||
"select_photos": "",
|
"select_photos": "انتخاب عکسها",
|
||||||
"select_trash_all": "",
|
"select_trash_all": "",
|
||||||
"selected": "",
|
"selected": "انتخاب شده",
|
||||||
"send_message": "",
|
"send_message": "ارسال پیام",
|
||||||
"send_welcome_email": "",
|
"send_welcome_email": "ارسال ایمیل خوشآمدگویی",
|
||||||
"server_stats": "",
|
"server_stats": "آمار سرور",
|
||||||
"set": "",
|
"set": "تنظیم",
|
||||||
"set_as_album_cover": "",
|
"set_as_album_cover": "",
|
||||||
"set_as_profile_picture": "",
|
"set_as_profile_picture": "",
|
||||||
"set_date_of_birth": "",
|
"set_date_of_birth": "تنظیم تاریخ تولد",
|
||||||
"set_profile_picture": "",
|
"set_profile_picture": "تنظیم تصویر پروفایل",
|
||||||
"set_slideshow_to_fullscreen": "",
|
"set_slideshow_to_fullscreen": "",
|
||||||
"settings": "",
|
"settings": "تنظیمات",
|
||||||
"settings_saved": "",
|
"settings_saved": "تنظیمات ذخیره شد",
|
||||||
"share": "",
|
"share": "اشتراکگذاری",
|
||||||
"shared": "",
|
"shared": "مشترک",
|
||||||
"shared_by": "",
|
"shared_by": "مشترک توسط",
|
||||||
"shared_by_you": "",
|
"shared_by_you": "",
|
||||||
"shared_from_partner": "",
|
"shared_from_partner": "عکسها از {partner}",
|
||||||
"shared_links": "",
|
"shared_links": "لینکهای اشتراکی",
|
||||||
"shared_photos_and_videos_count": "",
|
"shared_photos_and_videos_count": "",
|
||||||
"shared_with_partner": "",
|
"shared_with_partner": "مشترک با {partner}",
|
||||||
"sharing": "",
|
"sharing": "اشتراکگذاری",
|
||||||
"sharing_sidebar_description": "",
|
"sharing_sidebar_description": "",
|
||||||
"show_album_options": "",
|
"show_album_options": "نمایش گزینههای آلبوم",
|
||||||
"show_and_hide_people": "",
|
"show_and_hide_people": "",
|
||||||
"show_file_location": "",
|
"show_file_location": "نمایش مسیر فایل",
|
||||||
"show_gallery": "",
|
"show_gallery": "نمایش گالری",
|
||||||
"show_hidden_people": "",
|
"show_hidden_people": "نمایش افراد پنهان",
|
||||||
"show_in_timeline": "",
|
"show_in_timeline": "",
|
||||||
"show_in_timeline_setting_description": "",
|
"show_in_timeline_setting_description": "",
|
||||||
"show_keyboard_shortcuts": "",
|
"show_keyboard_shortcuts": "",
|
||||||
"show_metadata": "",
|
"show_metadata": "نمایش اطلاعات متا",
|
||||||
"show_or_hide_info": "",
|
"show_or_hide_info": "",
|
||||||
"show_password": "",
|
"show_password": "نمایش رمز عبور",
|
||||||
"show_person_options": "",
|
"show_person_options": "",
|
||||||
"show_progress_bar": "",
|
"show_progress_bar": "نمایش نوار پیشرفت",
|
||||||
"show_search_options": "",
|
"show_search_options": "نمایش گزینههای جستجو",
|
||||||
"shuffle": "",
|
"shuffle": "تصادفی",
|
||||||
"sign_out": "",
|
"sign_out": "خروج",
|
||||||
"sign_up": "",
|
"sign_up": "ثبتنام",
|
||||||
"size": "",
|
"size": "اندازه",
|
||||||
"skip_to_content": "",
|
"skip_to_content": "رفتن به محتوا",
|
||||||
"slideshow": "",
|
"slideshow": "نمایش اسلاید",
|
||||||
"slideshow_settings": "",
|
"slideshow_settings": "تنظیمات نمایش اسلاید",
|
||||||
"sort_albums_by": "",
|
"sort_albums_by": "",
|
||||||
"stack": "",
|
"stack": "پشته",
|
||||||
"stack_selected_photos": "",
|
"stack_selected_photos": "",
|
||||||
"stacktrace": "",
|
"stacktrace": "",
|
||||||
"start": "",
|
"start": "شروع",
|
||||||
"start_date": "",
|
"start_date": "تاریخ شروع",
|
||||||
"state": "",
|
"state": "ایالت",
|
||||||
"status": "",
|
"status": "وضعیت",
|
||||||
"stop_motion_photo": "",
|
"stop_motion_photo": "توقف عکس متحرک",
|
||||||
"stop_photo_sharing": "",
|
"stop_photo_sharing": "",
|
||||||
"stop_photo_sharing_description": "",
|
"stop_photo_sharing_description": "",
|
||||||
"stop_sharing_photos_with_user": "",
|
"stop_sharing_photos_with_user": "",
|
||||||
"storage": "",
|
"storage": "فضای ذخیرهسازی",
|
||||||
"storage_label": "",
|
"storage_label": "برچسب فضای ذخیرهسازی",
|
||||||
"storage_usage": "",
|
"storage_usage": "",
|
||||||
"submit": "",
|
"submit": "ارسال",
|
||||||
"suggestions": "",
|
"suggestions": "پیشنهادات",
|
||||||
"sunrise_on_the_beach": "",
|
"sunrise_on_the_beach": "",
|
||||||
"swap_merge_direction": "",
|
"swap_merge_direction": "تغییر جهت ادغام",
|
||||||
"sync": "",
|
"sync": "همگامسازی",
|
||||||
"template": "",
|
"template": "الگو",
|
||||||
"theme": "",
|
"theme": "تم",
|
||||||
"theme_selection": "",
|
"theme_selection": "انتخاب تم",
|
||||||
"theme_selection_description": "",
|
"theme_selection_description": "",
|
||||||
"time_based_memories": "",
|
"time_based_memories": "",
|
||||||
"timezone": "",
|
"timezone": "منطقه زمانی",
|
||||||
"to_archive": "",
|
"to_archive": "بایگانی",
|
||||||
"to_favorite": "",
|
"to_favorite": "به علاقهمندیها",
|
||||||
"to_trash": "",
|
"to_trash": "",
|
||||||
"toggle_settings": "",
|
"toggle_settings": "تغییر تنظیمات",
|
||||||
"toggle_theme": "",
|
"toggle_theme": "تغییر تم تاریک",
|
||||||
"total_usage": "",
|
"total_usage": "استفاده کلی",
|
||||||
"trash": "",
|
"trash": "سطل زباله",
|
||||||
"trash_all": "",
|
"trash_all": "",
|
||||||
"trash_count": "",
|
"trash_count": "",
|
||||||
"trash_no_results_message": "",
|
"trash_no_results_message": "",
|
||||||
"trashed_items_will_be_permanently_deleted_after": "",
|
"trashed_items_will_be_permanently_deleted_after": "",
|
||||||
"type": "",
|
"type": "نوع",
|
||||||
"unarchive": "",
|
"unarchive": "",
|
||||||
"unfavorite": "",
|
"unfavorite": "حذف از علاقهمندیها",
|
||||||
"unhide_person": "",
|
"unhide_person": "آشکار کردن فرد",
|
||||||
"unknown": "",
|
"unknown": "ناشناخته",
|
||||||
"unknown_year": "",
|
"unknown_year": "سال نامشخص",
|
||||||
"unlimited": "",
|
"unlimited": "نامحدود",
|
||||||
"unlink_oauth": "",
|
"unlink_oauth": "لغو اتصال OAuth",
|
||||||
"unlinked_oauth_account": "",
|
"unlinked_oauth_account": "",
|
||||||
"unnamed_album": "",
|
"unnamed_album": "آلبوم بدون نام",
|
||||||
"unnamed_share": "",
|
"unnamed_share": "اشتراک بدون نام",
|
||||||
"unselect_all": "",
|
"unselect_all": "لغو انتخاب همه",
|
||||||
"unstack": "",
|
"unstack": "",
|
||||||
"untracked_files": "",
|
"untracked_files": "",
|
||||||
"untracked_files_decription": "",
|
"untracked_files_decription": "",
|
||||||
"up_next": "",
|
"up_next": "مورد بعدی",
|
||||||
"updated_password": "",
|
"updated_password": "",
|
||||||
"upload": "",
|
"upload": "آپلود",
|
||||||
"upload_concurrency": "",
|
"upload_concurrency": "تعداد آپلود همزمان",
|
||||||
"url": "",
|
"url": "آدرس",
|
||||||
"usage": "",
|
"usage": "استفاده",
|
||||||
"user": "",
|
"user": "کاربر",
|
||||||
"user_id": "",
|
"user_id": "شناسه کاربر",
|
||||||
"user_usage_detail": "",
|
"user_usage_detail": "جزئیات استفاده کاربر",
|
||||||
"username": "",
|
"username": "نام کاربری",
|
||||||
"users": "",
|
"users": "کاربران",
|
||||||
"utilities": "",
|
"utilities": "ابزارها",
|
||||||
"validate": "",
|
"validate": "اعتبارسنجی",
|
||||||
"variables": "",
|
"variables": "متغیرها",
|
||||||
"version": "",
|
"version": "نسخه",
|
||||||
"version_announcement_message": "",
|
"version_announcement_message": "",
|
||||||
"video": "",
|
"video": "ویدیو",
|
||||||
"video_hover_setting": "",
|
"video_hover_setting": "",
|
||||||
"video_hover_setting_description": "",
|
"video_hover_setting_description": "",
|
||||||
"videos": "",
|
"videos": "ویدیوها",
|
||||||
"videos_count": "",
|
"videos_count": "",
|
||||||
"view": "",
|
"view": "مشاهده",
|
||||||
"view_all": "",
|
"view_all": "مشاهده همه",
|
||||||
"view_all_users": "",
|
"view_all_users": "مشاهده همه کاربران",
|
||||||
"view_links": "",
|
"view_links": "مشاهده لینکها",
|
||||||
"view_next_asset": "",
|
"view_next_asset": "مشاهده محتوای بعدی",
|
||||||
"view_previous_asset": "",
|
"view_previous_asset": "مشاهده محتوای قبلی",
|
||||||
"waiting": "",
|
"waiting": "در انتظار",
|
||||||
"week": "",
|
"week": "هفته",
|
||||||
"welcome": "",
|
"welcome": "خوش آمدید",
|
||||||
"welcome_to_immich": "",
|
"welcome_to_immich": "",
|
||||||
"year": "",
|
"year": "سال",
|
||||||
"yes": "",
|
"yes": "بله",
|
||||||
"you_dont_have_any_shared_links": "",
|
"you_dont_have_any_shared_links": "",
|
||||||
"zoom_image": "بزرگنمایی تصویر"
|
"zoom_image": "بزرگنمایی تصویر"
|
||||||
}
|
}
|
||||||
|
|||||||
24
i18n/it.json
24
i18n/it.json
@@ -10,14 +10,14 @@
|
|||||||
"activity_changed": "L'attività è {enabled, select, true {abilitata} other {disabilitata}}",
|
"activity_changed": "L'attività è {enabled, select, true {abilitata} other {disabilitata}}",
|
||||||
"add": "Aggiungi",
|
"add": "Aggiungi",
|
||||||
"add_a_description": "Aggiungi una descrizione",
|
"add_a_description": "Aggiungi una descrizione",
|
||||||
"add_a_location": "Aggiungi un luogo",
|
"add_a_location": "Aggiungi una posizione",
|
||||||
"add_a_name": "Aggiungi un nome",
|
"add_a_name": "Aggiungi un nome",
|
||||||
"add_a_title": "Aggiungi un titolo",
|
"add_a_title": "Aggiungi un titolo",
|
||||||
"add_exclusion_pattern": "Aggiungi un pattern di esclusione",
|
"add_exclusion_pattern": "Aggiungi un pattern di esclusione",
|
||||||
"add_import_path": "Aggiungi un percorso di importazione",
|
"add_import_path": "Aggiungi un percorso di importazione",
|
||||||
"add_location": "Aggiungi posizione",
|
"add_location": "Aggiungi posizione",
|
||||||
"add_more_users": "Aggiungi altri utenti",
|
"add_more_users": "Aggiungi altri utenti",
|
||||||
"add_partner": "Aggiungi un partner",
|
"add_partner": "Aggiungi partner",
|
||||||
"add_path": "Aggiungi percorso",
|
"add_path": "Aggiungi percorso",
|
||||||
"add_photos": "Aggiungi foto",
|
"add_photos": "Aggiungi foto",
|
||||||
"add_to": "Aggiungi a...",
|
"add_to": "Aggiungi a...",
|
||||||
@@ -374,11 +374,11 @@
|
|||||||
"album_name": "Nome Album",
|
"album_name": "Nome Album",
|
||||||
"album_options": "Impostazioni Album",
|
"album_options": "Impostazioni Album",
|
||||||
"album_remove_user": "Rimuovi l'utente?",
|
"album_remove_user": "Rimuovi l'utente?",
|
||||||
"album_remove_user_confirmation": "Sicuro di voler cancellare l'utente {user}?",
|
"album_remove_user_confirmation": "Sicuro di voler rimuovere l'utente {user}?",
|
||||||
"album_share_no_users": "Sembra che tu abbia condiviso questo album con tutti gli utenti oppure non hai nessun utente con cui condividere.",
|
"album_share_no_users": "Sembra che tu abbia condiviso questo album con tutti gli utenti oppure non hai nessun utente con cui condividere.",
|
||||||
"album_updated": "Album aggiornato",
|
"album_updated": "Album aggiornato",
|
||||||
"album_updated_setting_description": "Ricevi una notifica email quando un album condiviso ha nuovi asset",
|
"album_updated_setting_description": "Ricevi una notifica email quando un album condiviso ha nuovi media",
|
||||||
"album_user_left": "Abbandona {album}",
|
"album_user_left": "{album} abbandonato",
|
||||||
"album_user_removed": "Utente {user} rimosso",
|
"album_user_removed": "Utente {user} rimosso",
|
||||||
"album_with_link_access": "Permetti a chiunque possieda il link di visualizzare le foto e le persone dell'album.",
|
"album_with_link_access": "Permetti a chiunque possieda il link di visualizzare le foto e le persone dell'album.",
|
||||||
"albums": "Album",
|
"albums": "Album",
|
||||||
@@ -391,10 +391,10 @@
|
|||||||
"allow_edits": "Permetti Modifiche",
|
"allow_edits": "Permetti Modifiche",
|
||||||
"allow_public_user_to_download": "Permetti agli utenti pubblici di scaricare",
|
"allow_public_user_to_download": "Permetti agli utenti pubblici di scaricare",
|
||||||
"allow_public_user_to_upload": "Permetti agli utenti pubblici di caricare",
|
"allow_public_user_to_upload": "Permetti agli utenti pubblici di caricare",
|
||||||
"anti_clockwise": "Senso Anti-Orario",
|
"anti_clockwise": "Senso anti-orario",
|
||||||
"api_key": "Chiave API",
|
"api_key": "Chiave API",
|
||||||
"api_key_description": "Il campo verrà mostrato solo una volta. Abbi cura di copiarlo prima di chiudere la finestra.",
|
"api_key_description": "Il valore verrà mostrato solo una volta. Assicurati di copiarlo prima di chiudere la finestra.",
|
||||||
"api_key_empty": "Il Nome dell'API Key non può essere vuoto",
|
"api_key_empty": "Il nome della chiave API non può essere vuoto",
|
||||||
"api_keys": "Chiavi API",
|
"api_keys": "Chiavi API",
|
||||||
"app_settings": "Impostazioni Applicazione",
|
"app_settings": "Impostazioni Applicazione",
|
||||||
"appears_in": "Compare in",
|
"appears_in": "Compare in",
|
||||||
@@ -407,14 +407,14 @@
|
|||||||
"are_you_sure_to_do_this": "Sei sicuro di voler procedere?",
|
"are_you_sure_to_do_this": "Sei sicuro di voler procedere?",
|
||||||
"asset_added_to_album": "Aggiunto all'album",
|
"asset_added_to_album": "Aggiunto all'album",
|
||||||
"asset_adding_to_album": "In aggiunta all'album...",
|
"asset_adding_to_album": "In aggiunta all'album...",
|
||||||
"asset_description_updated": "La descrizione del media non è stata aggiornata",
|
"asset_description_updated": "La descrizione del media è stata aggiornata",
|
||||||
"asset_filename_is_offline": "Il media {filename} è offline",
|
"asset_filename_is_offline": "Il media {filename} è offline",
|
||||||
"asset_has_unassigned_faces": "Il media ha dei volti non categorizzati",
|
"asset_has_unassigned_faces": "Il media ha dei volti non categorizzati",
|
||||||
"asset_hashing": "Hashing in corso ...",
|
"asset_hashing": "Hashing in corso ...",
|
||||||
"asset_offline": "Risorsa Offline",
|
"asset_offline": "Risorsa Offline",
|
||||||
"asset_offline_description": "Questo media non è stato trovato nel disco. Contatta il tuo amministratore di Immich per assistenza.",
|
"asset_offline_description": "Questo media non è stato trovato nel disco. Contatta il tuo amministratore di Immich per assistenza.",
|
||||||
"asset_skipped": "Saltato",
|
"asset_skipped": "Saltato",
|
||||||
"asset_skipped_in_trash": "In cestino",
|
"asset_skipped_in_trash": "Nel cestino",
|
||||||
"asset_uploaded": "Caricato",
|
"asset_uploaded": "Caricato",
|
||||||
"asset_uploading": "Caricamento...",
|
"asset_uploading": "Caricamento...",
|
||||||
"assets": "Risorse",
|
"assets": "Risorse",
|
||||||
@@ -434,7 +434,7 @@
|
|||||||
"back_close_deselect": "Indietro, chiudi o deseleziona",
|
"back_close_deselect": "Indietro, chiudi o deseleziona",
|
||||||
"backward": "Indietro",
|
"backward": "Indietro",
|
||||||
"birthdate_saved": "Data di nascita salvata con successo",
|
"birthdate_saved": "Data di nascita salvata con successo",
|
||||||
"birthdate_set_description": "La data di nascita è usata per calcolare l'età di questa persona nel momento dello scatto della foto.",
|
"birthdate_set_description": "La data di nascita è usata per calcolare l'età di questa persona al momento dello scatto della foto.",
|
||||||
"blurred_background": "Sfondo sfocato",
|
"blurred_background": "Sfondo sfocato",
|
||||||
"bugs_and_feature_requests": "Bug & Richieste di nuove funzionalità",
|
"bugs_and_feature_requests": "Bug & Richieste di nuove funzionalità",
|
||||||
"build": "Compilazione",
|
"build": "Compilazione",
|
||||||
@@ -442,7 +442,7 @@
|
|||||||
"bulk_delete_duplicates_confirmation": "Sei sicuro di voler cancellare {count, plural, one {# asset duplicato} other {# assets duplicati}}? Questa operazione manterrà l'asset più pesante di ogni gruppo e cancellerà permanentemente tutti gli altri duplicati. Non puoi annullare questa operazione!",
|
"bulk_delete_duplicates_confirmation": "Sei sicuro di voler cancellare {count, plural, one {# asset duplicato} other {# assets duplicati}}? Questa operazione manterrà l'asset più pesante di ogni gruppo e cancellerà permanentemente tutti gli altri duplicati. Non puoi annullare questa operazione!",
|
||||||
"bulk_keep_duplicates_confirmation": "Sei sicuro di voler tenere {count, plural, one {# asset duplicato} other {# assets duplicati}}? Questa operazione risolverà tutti i gruppi duplicati senza cancellare nulla.",
|
"bulk_keep_duplicates_confirmation": "Sei sicuro di voler tenere {count, plural, one {# asset duplicato} other {# assets duplicati}}? Questa operazione risolverà tutti i gruppi duplicati senza cancellare nulla.",
|
||||||
"bulk_trash_duplicates_confirmation": "Sei davvero sicuro di voler cancellare {count, plural, one {# asset duplicato} other {# assets duplicati}}? Questa operazione manterrà l'asset più pesante di ogni gruppo e cancellerà permanentemente tutti gli altri duplicati.",
|
"bulk_trash_duplicates_confirmation": "Sei davvero sicuro di voler cancellare {count, plural, one {# asset duplicato} other {# assets duplicati}}? Questa operazione manterrà l'asset più pesante di ogni gruppo e cancellerà permanentemente tutti gli altri duplicati.",
|
||||||
"buy": "Acquistare Immich",
|
"buy": "Acquista Immich",
|
||||||
"camera": "Fotocamera",
|
"camera": "Fotocamera",
|
||||||
"camera_brand": "Marca fotocamera",
|
"camera_brand": "Marca fotocamera",
|
||||||
"camera_model": "Modello fotocamera",
|
"camera_model": "Modello fotocamera",
|
||||||
|
|||||||
216
i18n/th.json
216
i18n/th.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"about": "เกี่ยวกับ",
|
"about": "เกี่ยวกับ",
|
||||||
"account": "บัญชี",
|
"account": "บัญชีผู้ใช้",
|
||||||
"account_settings": "การตั้งค่าบัญชี",
|
"account_settings": "การตั้งค่าบัญชี",
|
||||||
"acknowledge": "รับทราบ",
|
"acknowledge": "รับทราบ",
|
||||||
"action": "การดำเนินการ",
|
"action": "การดำเนินการ",
|
||||||
@@ -155,7 +155,7 @@
|
|||||||
"migration_job_description": "ย้ายภาพตัวอย่างสื่อและใบหน้าไปยังโครงสร้างโฟลเดอร์ล่าสุด",
|
"migration_job_description": "ย้ายภาพตัวอย่างสื่อและใบหน้าไปยังโครงสร้างโฟลเดอร์ล่าสุด",
|
||||||
"no_paths_added": "ไม่ได้เพิ่มพาธ",
|
"no_paths_added": "ไม่ได้เพิ่มพาธ",
|
||||||
"no_pattern_added": "ไม่ได้เพิ่มรูปแบบ",
|
"no_pattern_added": "ไม่ได้เพิ่มรูปแบบ",
|
||||||
"note_apply_storage_label_previous_assets": "หมายเหตุ: หากจะแปะฉลากจัดเก็บใส่สื่อที่อัพโหลดก่อนหน้านี้ ให้",
|
"note_apply_storage_label_previous_assets": "หากต้องการใช้ Storage Label กับไฟล์ที่อัปโหลดก่อนหน้านี้ ให้รันคำสั่งนี้",
|
||||||
"note_cannot_be_changed_later": "หมายเหตุ: ไม่สามารถเปลี่ยนภายหลังได้!",
|
"note_cannot_be_changed_later": "หมายเหตุ: ไม่สามารถเปลี่ยนภายหลังได้!",
|
||||||
"note_unlimited_quota": "หมายเหตุ: ใส่เลข 0 สําหรับโควต้าไม่จํากัด",
|
"note_unlimited_quota": "หมายเหตุ: ใส่เลข 0 สําหรับโควต้าไม่จํากัด",
|
||||||
"notification_email_from_address": "จากที่อยู่",
|
"notification_email_from_address": "จากที่อยู่",
|
||||||
@@ -193,8 +193,8 @@
|
|||||||
"oauth_settings_description": "จัดการการตั้งค่าล็อกอินผ่าน OAuth",
|
"oauth_settings_description": "จัดการการตั้งค่าล็อกอินผ่าน OAuth",
|
||||||
"oauth_settings_more_details": "สำหรับรายละเอียดเพิ่มเติม ให้อ้างถึง<link>เอกสาร</link>",
|
"oauth_settings_more_details": "สำหรับรายละเอียดเพิ่มเติม ให้อ้างถึง<link>เอกสาร</link>",
|
||||||
"oauth_signing_algorithm": "อัลกอริทึมการลงนาม",
|
"oauth_signing_algorithm": "อัลกอริทึมการลงนาม",
|
||||||
"oauth_storage_label_claim": "สิทธิ์ที่ใช้อ้างถึงฉลากการจัดเก็บ",
|
"oauth_storage_label_claim": "สิทธิ์ที่ใช้อ้างถึงป้ายกำกับการจัดเก็บ",
|
||||||
"oauth_storage_label_claim_description": "ตั้งฉลากการจัดเก็บของผู้ใช้งานตามสิทธิ์ที่ใช้อ้างถึงโดยอัตโนมัติ",
|
"oauth_storage_label_claim_description": "ตั้งป้ายกำกับการจัดเก็บของผู้ใช้งานตามสิทธิ์ที่ใช้อ้างถึงโดยอัตโนมัติ",
|
||||||
"oauth_storage_quota_claim": "สิทธิ์ที่ใช้อ้างถึงโควต้าพื้นที่จัดเก็บ",
|
"oauth_storage_quota_claim": "สิทธิ์ที่ใช้อ้างถึงโควต้าพื้นที่จัดเก็บ",
|
||||||
"oauth_storage_quota_claim_description": "ตั้งโควต้าพื้นที่จัดเก็บของผู้ใช้งานตามสิทธิ์ที่ใช้อ้างถึงโดยอัตโนมัติ",
|
"oauth_storage_quota_claim_description": "ตั้งโควต้าพื้นที่จัดเก็บของผู้ใช้งานตามสิทธิ์ที่ใช้อ้างถึงโดยอัตโนมัติ",
|
||||||
"oauth_storage_quota_default": "โควต้าพื้นที่เก็บข้อมูลเริ่มต้น (GiB)",
|
"oauth_storage_quota_default": "โควต้าพื้นที่เก็บข้อมูลเริ่มต้น (GiB)",
|
||||||
@@ -235,7 +235,7 @@
|
|||||||
"storage_template_hash_verification_enabled": "ตรวจสอบ hash ไม่ผ่าน",
|
"storage_template_hash_verification_enabled": "ตรวจสอบ hash ไม่ผ่าน",
|
||||||
"storage_template_hash_verification_enabled_description": "เปิดใช้งานการตรวจสอบ hash ห้ามปิดใช้งานเว้นแต่คุณจะเข้าใจผลกระทบ",
|
"storage_template_hash_verification_enabled_description": "เปิดใช้งานการตรวจสอบ hash ห้ามปิดใช้งานเว้นแต่คุณจะเข้าใจผลกระทบ",
|
||||||
"storage_template_migration": "การย้ายเทมเพลตที่เก็บข้อมูล",
|
"storage_template_migration": "การย้ายเทมเพลตที่เก็บข้อมูล",
|
||||||
"storage_template_migration_description": "ใช้<link>{template}</link>ปัจจุบันกับสื่อที่อัพโหลดก่อนหน้านี้",
|
"storage_template_migration_description": "ใช้<link>{template}</link>ปัจจุบันกับสื่อที่อัปโหลดก่อนหน้านี้",
|
||||||
"storage_template_migration_job": "",
|
"storage_template_migration_job": "",
|
||||||
"storage_template_path_length": "ขีดจำกัดของความยาวพาธโดยประมาณ: <b>{length, number}</b>/{limit, number}",
|
"storage_template_path_length": "ขีดจำกัดของความยาวพาธโดยประมาณ: <b>{length, number}</b>/{limit, number}",
|
||||||
"storage_template_settings": "เทมเพลตการจัดเก็บข้อมูล",
|
"storage_template_settings": "เทมเพลตการจัดเก็บข้อมูล",
|
||||||
@@ -313,6 +313,9 @@
|
|||||||
"user_delete_delay_settings_description": "จํานวนวันหลังจากที่เอาออกเพื่อลบบัญชีผู้ใช้และสื่อถาวร งานลบบัญชีผู้ใช้ทํางานทุกเที่ยงคืนเพื่อตรวจสอบผู้ใช้ที่พร้อมที่จะถูกลบข้อมูลแล้ว การตั้งค่าครั้งนี้จะมีผลครั้งต่อไป",
|
"user_delete_delay_settings_description": "จํานวนวันหลังจากที่เอาออกเพื่อลบบัญชีผู้ใช้และสื่อถาวร งานลบบัญชีผู้ใช้ทํางานทุกเที่ยงคืนเพื่อตรวจสอบผู้ใช้ที่พร้อมที่จะถูกลบข้อมูลแล้ว การตั้งค่าครั้งนี้จะมีผลครั้งต่อไป",
|
||||||
"user_delete_immediately": "บัญชีและสื่อของ <b>{user}</b> จะอยู่ในคิวสำหรับการลบถาวร <b>โดยทันที</b>",
|
"user_delete_immediately": "บัญชีและสื่อของ <b>{user}</b> จะอยู่ในคิวสำหรับการลบถาวร <b>โดยทันที</b>",
|
||||||
"user_settings": "การตั้งค่าผู้ใช้",
|
"user_settings": "การตั้งค่าผู้ใช้",
|
||||||
|
"user_management": "การจัดการผู้ใช้",
|
||||||
|
"user_password_has_been_reset": "รหัสผ่านของผู้ใช้ <b>{user}</b> ถูกตั้งค่าใหม่แล้ว",
|
||||||
|
"user_password_reset_description": "รหัสผ่านของผู้ใช้จะถูกตั้งค่าใหม่และส่งไปยังอีเมลที่ลงทะเบียน",
|
||||||
"user_settings_description": "จัดการการตั้งค่าผู้ใช้",
|
"user_settings_description": "จัดการการตั้งค่าผู้ใช้",
|
||||||
"version_check_enabled_description": "เช็ค GitHub เป็นระยะ ๆ เพื่อตรวจสอบรุ่นใหม่",
|
"version_check_enabled_description": "เช็ค GitHub เป็นระยะ ๆ เพื่อตรวจสอบรุ่นใหม่",
|
||||||
"version_check_settings": "ตรวจสอบรุ่น",
|
"version_check_settings": "ตรวจสอบรุ่น",
|
||||||
@@ -347,12 +350,14 @@
|
|||||||
"allow_public_user_to_download": "อนุญาตให้ผู้ใช้สาธารณะดาวน์โหลดได้",
|
"allow_public_user_to_download": "อนุญาตให้ผู้ใช้สาธารณะดาวน์โหลดได้",
|
||||||
"allow_public_user_to_upload": "อนุญาตให้ผู้ใช้สาธารณะอัปโหลดได้",
|
"allow_public_user_to_upload": "อนุญาตให้ผู้ใช้สาธารณะอัปโหลดได้",
|
||||||
"anti_clockwise": "ทวนเข็มนาฬิกา",
|
"anti_clockwise": "ทวนเข็มนาฬิกา",
|
||||||
"api_key": "กุญแจ API",
|
"api_key": "API key",
|
||||||
"api_keys": "กุญแจ API",
|
"api_keys": "API Keys",
|
||||||
"app_settings": "การตั้งค่าแอป",
|
"app_settings": "การตั้งค่าแอป",
|
||||||
"appears_in": "อยู่ใน",
|
"appears_in": "อยู่ใน",
|
||||||
"archive": "เก็บถาวร",
|
"archive": "เก็บถาวร",
|
||||||
"archive_or_unarchive_photo": "เก็บ/ไม่เก็บภาพถาวร",
|
"archive_or_unarchive_photo": "เก็บ/ไม่เก็บภาพถาวร",
|
||||||
|
"archive_size": "ขนาดเก็บถาวร",
|
||||||
|
"archive_size_description": "ตั้งค่าขนาดสูงสุดสำหรับการดาวน์โหลด (GiB)",
|
||||||
"are_these_the_same_person": "เป็นคนเดียวกันหรือไม่?",
|
"are_these_the_same_person": "เป็นคนเดียวกันหรือไม่?",
|
||||||
"are_you_sure_to_do_this": "คุณแน่ใจว่าต้องการทำสิ่งนี้หรือไม่?",
|
"are_you_sure_to_do_this": "คุณแน่ใจว่าต้องการทำสิ่งนี้หรือไม่?",
|
||||||
"asset_added_to_album": "เพิ่มไปยังอัลบั้มแล้ว",
|
"asset_added_to_album": "เพิ่มไปยังอัลบั้มแล้ว",
|
||||||
@@ -380,19 +385,19 @@
|
|||||||
"change_name": "เปลี่ยนชื่อ",
|
"change_name": "เปลี่ยนชื่อ",
|
||||||
"change_name_successfully": "เปลี่ยนชื่อเรียบร้อยแล้ว",
|
"change_name_successfully": "เปลี่ยนชื่อเรียบร้อยแล้ว",
|
||||||
"change_password": "เปลี่ยนรหัสผ่าน",
|
"change_password": "เปลี่ยนรหัสผ่าน",
|
||||||
"change_your_password": "",
|
"change_your_password": "เปลี่ยนรหัสผ่านของคุณ",
|
||||||
"changed_visibility_successfully": "",
|
"changed_visibility_successfully": "เปลี่ยนการมองเห็นเรียบร้อยแล้ว",
|
||||||
"check_logs": "",
|
"check_logs": "ตรวจสอบบันทึก",
|
||||||
"city": "เมือง",
|
"city": "เมือง",
|
||||||
"clear": "ล้าง",
|
"clear": "ล้าง",
|
||||||
"clear_all": "",
|
"clear_all": "ล้างทั้งหมด",
|
||||||
"clear_message": "",
|
"clear_message": "ล้างข้อความ",
|
||||||
"clear_value": "",
|
"clear_value": "ล้างค่า",
|
||||||
"close": "ปิด",
|
"close": "ปิด",
|
||||||
"collapse": "ย่อ",
|
"collapse": "ย่อ",
|
||||||
"collapse_all": "ย่อทั้งหมด",
|
"collapse_all": "ย่อทั้งหมด",
|
||||||
"color": "สี",
|
"color": "สี",
|
||||||
"color_theme": "",
|
"color_theme": "สีธีม",
|
||||||
"comment_deleted": "ลบความคิดเห็นแล้ว",
|
"comment_deleted": "ลบความคิดเห็นแล้ว",
|
||||||
"comment_options": "",
|
"comment_options": "",
|
||||||
"comments_and_likes": "ความคิดเห็นและการถูกใจ",
|
"comments_and_likes": "ความคิดเห็นและการถูกใจ",
|
||||||
@@ -450,13 +455,17 @@
|
|||||||
"discover": "ค้นพบ",
|
"discover": "ค้นพบ",
|
||||||
"dismiss_all_errors": "ปฏิเสธข้อผิดพลาดทั้งหมด",
|
"dismiss_all_errors": "ปฏิเสธข้อผิดพลาดทั้งหมด",
|
||||||
"dismiss_error": "ปฏิเสธข้อผิดพลาด",
|
"dismiss_error": "ปฏิเสธข้อผิดพลาด",
|
||||||
"display_options": "",
|
"display_options": "ตัวเลือกการแสดง",
|
||||||
"display_order": "",
|
"display_order": "ลำดับการแสดงผล",
|
||||||
"display_original_photos": "",
|
"display_original_photos": "แสดงภาพต้นฉบับ",
|
||||||
"display_original_photos_setting_description": "เมื่อดูสื่อให้แสดงภาพต้นฉบับแทนภาพตัวอย่างเมื่อไฟล์สื่อเปิดได้บนเว็บ อาจทําให้แสดง ภาพได้ช้าลง",
|
"display_original_photos_setting_description": "การตั้งค่าแสดงผลรูปภาพต้นฉบับ เมื่อเปิดรูปภาพ การตั้งค่านี้อาจจะทำให้การแสดงภาพได้ช้าลง",
|
||||||
"done": "เสร็จ",
|
"done": "ดำเนินการสำเร็จ",
|
||||||
"download": "ดาวน์โหลด",
|
"download": "ดาวน์โหลด",
|
||||||
|
"download_include_embedded_motion_videos": "รวมวิดีโอที่ฝังอยู่ในภาพเคลื่อนไหว",
|
||||||
|
"download_include_embedded_motion_videos_description": "รวมวิดีโอที่ฝังอยู่ในภาพเคลื่อนไหวเมื่อดาวน์โหลดอัลบั้ม",
|
||||||
"downloading": "กำลังดาวน์โหลด",
|
"downloading": "กำลังดาวน์โหลด",
|
||||||
|
"download_settings": "การตั้งค่าการดาวน์โหลด",
|
||||||
|
"download_settings_description": "จัดการการตั้งค่าการดาวน์โหลด",
|
||||||
"duration": "ระยะเวลา",
|
"duration": "ระยะเวลา",
|
||||||
"edit_album": "แก้ไขอัลบั้ม",
|
"edit_album": "แก้ไขอัลบั้ม",
|
||||||
"edit_avatar": "แก้ไขตัวละคร",
|
"edit_avatar": "แก้ไขตัวละคร",
|
||||||
@@ -483,6 +492,38 @@
|
|||||||
"error": "เกิดข้อผิดพลาด",
|
"error": "เกิดข้อผิดพลาด",
|
||||||
"error_loading_image": "เกิดข้อผิดพลาดระหว่างโหลดภาพ",
|
"error_loading_image": "เกิดข้อผิดพลาดระหว่างโหลดภาพ",
|
||||||
"errors": {
|
"errors": {
|
||||||
|
"cannot_navigate_next_asset": "ไม่สามารถเปลี่ยนเส้นทางได้",
|
||||||
|
"cannot_navigate_previous_asset": "ไม่สามารถเปลี่ยนเส้นทางก่อนหน้าได้",
|
||||||
|
"cant_apply_changes": "เกิดข้อผิดพลาดในการเปลี่ยนแปลง",
|
||||||
|
"cant_change_activity": "Can't {enabled, select, true {disable} other {enable}} activity",
|
||||||
|
"cant_change_asset_favorite": "ไม่สามารถเปลี่ยนสื่อที่ชื่นชอบได้",
|
||||||
|
"cant_change_metadata_assets_count": "Can't change metadata of {count, plural, one {# asset} other {# assets}}",
|
||||||
|
"cant_get_faces": "เกิดข้อผิดพลาดในการเรียกดูใบหน้า",
|
||||||
|
"cant_get_number_of_comments": "ไม่สามารถเรียกดูจำนวนความคิดเห็นได้",
|
||||||
|
"cant_search_people": "ไม่สามารถค้นหาบุคคลคนได้",
|
||||||
|
"cant_search_places": "ไม่สามารถค้นหาสถานที่ได้",
|
||||||
|
"cleared_jobs": "ล้างงาน: {job} สำเร็จ",
|
||||||
|
"error_adding_assets_to_album": "เกิดข้อผิดพลาดในการเพิ่มสื่อไปยังอัลบั้ม",
|
||||||
|
"error_adding_users_to_album": "เกิดข้อผิดพลาดในการเพิ่มผู้ใช้ไปยังอัลบั้ม",
|
||||||
|
"error_deleting_shared_user": "เกิดข้อผิดพลาดในการลบผู้ใช้ที่แชร์",
|
||||||
|
"error_downloading": "ไม่สามารถดาวน์โหลด {filename} ได้",
|
||||||
|
"error_hiding_buy_button": "Error hiding buy button",
|
||||||
|
"error_removing_assets_from_album": "เกิดข้อผิดพลาดในการลบสื่อจากอัลบั้ม",
|
||||||
|
"error_selecting_all_assets": "เกิดข้อผิดพลาดในการเลือกสื่อทั้งหมด",
|
||||||
|
"exclusion_pattern_already_exists": "ข้อยกเว้นนี้มีอยู่แล้ว",
|
||||||
|
"failed_job_command": "คำสั่ง {command} ผิดพลาด สำหรับ: {job}",
|
||||||
|
"failed_to_create_album": "ไม่สามารถสร้างอัลบั้มได้",
|
||||||
|
"failed_to_create_shared_link": "ไม่สามารถสร้างลิงก์ที่แชร์ได้",
|
||||||
|
"failed_to_edit_shared_link": "ไม่สามารถแก้ไขลิงก์ที่แชร์ได้",
|
||||||
|
"failed_to_get_people": "ไม่สามารถเรียกดูบุคคลได้",
|
||||||
|
"failed_to_keep_this_delete_others": "ไม่สามารถเก็บหรือลบได้",
|
||||||
|
"failed_to_load_asset": "ไม่สามารถโหลดสื่อได้",
|
||||||
|
"failed_to_load_assets": "ไม่สามารถโหลดสื่อได้",
|
||||||
|
"failed_to_load_people": "ไม่สามารถโหลดบุคคลได้",
|
||||||
|
"failed_to_remove_product_key": "Failed to remove product key",
|
||||||
|
"failed_to_stack_assets": "Failed to stack assets",
|
||||||
|
"failed_to_unstack_assets": "Failed to un-stack assets",
|
||||||
|
"incorrect_email_or_password": "อีเมลหรือรหัสผ่านไม่ถูกต้อง",
|
||||||
"import_path_already_exists": "พาธนำเข้านี้มีอยู่แล้ว",
|
"import_path_already_exists": "พาธนำเข้านี้มีอยู่แล้ว",
|
||||||
"unable_to_add_album_users": "ไม่สามารถเพิ่มผู้ใช้ไปยังอัลบั้มได้",
|
"unable_to_add_album_users": "ไม่สามารถเพิ่มผู้ใช้ไปยังอัลบั้มได้",
|
||||||
"unable_to_add_comment": "ไม่สามารถเพิ่มความเห็นได้",
|
"unable_to_add_comment": "ไม่สามารถเพิ่มความเห็นได้",
|
||||||
@@ -490,7 +531,7 @@
|
|||||||
"unable_to_change_album_user_role": "ไม่สามารถเปลี่ยนบทบาทผู้ใช้ในอัลบั้มได้",
|
"unable_to_change_album_user_role": "ไม่สามารถเปลี่ยนบทบาทผู้ใช้ในอัลบั้มได้",
|
||||||
"unable_to_change_date": "ไม่สามารถเปลี่ยนวันที่ได้",
|
"unable_to_change_date": "ไม่สามารถเปลี่ยนวันที่ได้",
|
||||||
"unable_to_change_location": "ไม่สามารถเปลี่ยนตําแหน่งได้",
|
"unable_to_change_location": "ไม่สามารถเปลี่ยนตําแหน่งได้",
|
||||||
"unable_to_create_admin_account": "",
|
"unable_to_create_admin_account": "ไม่สามารถสร้างบัญชีผู้ดูแลระบบได้",
|
||||||
"unable_to_create_library": "ไม่สามารถสร้างคลังภาพได้",
|
"unable_to_create_library": "ไม่สามารถสร้างคลังภาพได้",
|
||||||
"unable_to_create_user": "ไม่สามารถสร้างผู้ใช้ได้",
|
"unable_to_create_user": "ไม่สามารถสร้างผู้ใช้ได้",
|
||||||
"unable_to_delete_album": "ไม่สามารถลบอัลบั้มได้",
|
"unable_to_delete_album": "ไม่สามารถลบอัลบั้มได้",
|
||||||
@@ -531,8 +572,8 @@
|
|||||||
"unable_to_update_settings": "ไม่สามารถอัพเดทการตั้งค่าได้",
|
"unable_to_update_settings": "ไม่สามารถอัพเดทการตั้งค่าได้",
|
||||||
"unable_to_update_user": "ไม่สามารถอัพเดทผู้ใช้ได้"
|
"unable_to_update_user": "ไม่สามารถอัพเดทผู้ใช้ได้"
|
||||||
},
|
},
|
||||||
"exit_slideshow": "",
|
"exit_slideshow": "ออกจากการนำเสนอ",
|
||||||
"expand_all": "",
|
"expand_all": "ขยายทั้งหมด",
|
||||||
"expire_after": "หมดอายุหลังจาก",
|
"expire_after": "หมดอายุหลังจาก",
|
||||||
"expired": "หมดอายุแล้ว",
|
"expired": "หมดอายุแล้ว",
|
||||||
"explore": "สํารวจ",
|
"explore": "สํารวจ",
|
||||||
@@ -542,22 +583,25 @@
|
|||||||
"favorite_or_unfavorite_photo": "โปรดหรือไม่โปรดภาพ",
|
"favorite_or_unfavorite_photo": "โปรดหรือไม่โปรดภาพ",
|
||||||
"favorites": "รายการโปรด",
|
"favorites": "รายการโปรด",
|
||||||
"feature_photo_updated": "อัพเดทภาพเด่นแล้ว",
|
"feature_photo_updated": "อัพเดทภาพเด่นแล้ว",
|
||||||
"file_name": "",
|
"file_name": "ชื่อไฟล์",
|
||||||
"file_name_or_extension": "",
|
"file_name_or_extension": "นามสกุลหรือชื่อไฟล์",
|
||||||
"filename": "ชื่อไฟล์",
|
"filename": "ชื่อไฟล์",
|
||||||
"filetype": "ชนิดไฟล์",
|
"filetype": "ชนิดไฟล์",
|
||||||
"filter_people": "กรองผู้คน",
|
"filter_people": "กรองผู้คน",
|
||||||
"fix_incorrect_match": "",
|
"fix_incorrect_match": "แก้ไขการจับคู่ที่ไม่ถูกต้อง",
|
||||||
"forward": "ไปข้างหน้า",
|
"forward": "ไปข้างหน้า",
|
||||||
"general": "ทั่วไป",
|
"general": "ทั่วไป",
|
||||||
"get_help": "",
|
"get_help": "ขอความช่วยเหลือ",
|
||||||
"getting_started": "",
|
"getting_started": "เริ่มต้นใช้งาน",
|
||||||
"go_back": "",
|
"go_back": "กลับ",
|
||||||
"go_to_search": "",
|
"go_to_search": "กลับไปยังการค้นหา",
|
||||||
"group_albums_by": "",
|
"group_albums_by": "จัดกลุ่มอัลบั้มตาม",
|
||||||
"has_quota": "",
|
"group_no": "ไม่จัดกลุ่ม",
|
||||||
|
"group_owner": "จัดกลุ่มโดยเจ้าของ",
|
||||||
|
"group_year": "จัดกลุ่มตามปี",
|
||||||
|
"has_quota": "เหลือพื้นที่",
|
||||||
"hide_gallery": "ซ่อนคลังภาพ",
|
"hide_gallery": "ซ่อนคลังภาพ",
|
||||||
"hide_password": "",
|
"hide_password": "ซ่อนรหัสผ่าน",
|
||||||
"hide_person": "ซ่อนบุคคล",
|
"hide_person": "ซ่อนบุคคล",
|
||||||
"host": "โฮสต์",
|
"host": "โฮสต์",
|
||||||
"hour": "ชั่วโมง",
|
"hour": "ชั่วโมง",
|
||||||
@@ -654,7 +698,7 @@
|
|||||||
"no_assets_message": "กดเพื่อใส่ภาพคุณภาพแรก",
|
"no_assets_message": "กดเพื่อใส่ภาพคุณภาพแรก",
|
||||||
"no_duplicates_found": "ไม่พบรายการที่ซ้ำกัน",
|
"no_duplicates_found": "ไม่พบรายการที่ซ้ำกัน",
|
||||||
"no_exif_info_available": "ไม่มีข้อมูล exif",
|
"no_exif_info_available": "ไม่มีข้อมูล exif",
|
||||||
"no_explore_results_message": "",
|
"no_explore_results_message": "ไม่พบผลลัพธ์ ลองใช้คำค้นหาอื่น ๆ",
|
||||||
"no_favorites_message": "เพิ่มรายการโปรดเพื่อค้นหาภาพและวิดีโอที่ดีที่สุดของคุณอย่างรวดเร็ว",
|
"no_favorites_message": "เพิ่มรายการโปรดเพื่อค้นหาภาพและวิดีโอที่ดีที่สุดของคุณอย่างรวดเร็ว",
|
||||||
"no_libraries_message": "สร้างคลังภาพภายนอกเพื่อดูภาพถ่ายและวิดีโอต่าง ๆ ของคุณ",
|
"no_libraries_message": "สร้างคลังภาพภายนอกเพื่อดูภาพถ่ายและวิดีโอต่าง ๆ ของคุณ",
|
||||||
"no_name": "ไม่มีชื่อ",
|
"no_name": "ไม่มีชื่อ",
|
||||||
@@ -670,9 +714,9 @@
|
|||||||
"oauth": "OAuth",
|
"oauth": "OAuth",
|
||||||
"official_immich_resources": "แหล่งข้อมูล Immich อย่างเป็นทางการ",
|
"official_immich_resources": "แหล่งข้อมูล Immich อย่างเป็นทางการ",
|
||||||
"offline": "ออฟไลน์",
|
"offline": "ออฟไลน์",
|
||||||
"ok": "โอเค",
|
"ok": "ตกลง",
|
||||||
"oldest_first": "เก่าสุดก่อน",
|
"oldest_first": "เรียงเก่าสุดก่อน",
|
||||||
"onboarding_welcome_user": "ยินดีต้อนรับ {user}",
|
"onboarding_welcome_user": "ยินดีต้อนรับคุณ {user}",
|
||||||
"online": "ออนไลน์",
|
"online": "ออนไลน์",
|
||||||
"only_favorites": "รายการโปรดเท่านั้น",
|
"only_favorites": "รายการโปรดเท่านั้น",
|
||||||
"open_in_openstreetmap": "เปิดใน OpenStreetMap",
|
"open_in_openstreetmap": "เปิดใน OpenStreetMap",
|
||||||
@@ -702,9 +746,9 @@
|
|||||||
"years": "{years, plural, one {ปี} other {# ปี}}ที่ผ่านมา"
|
"years": "{years, plural, one {ปี} other {# ปี}}ที่ผ่านมา"
|
||||||
},
|
},
|
||||||
"path": "",
|
"path": "",
|
||||||
"pattern": "",
|
"pattern": "รูปแบบ",
|
||||||
"pause": "หยุด",
|
"pause": "หยุด",
|
||||||
"pause_memories": "",
|
"pause_memories": "หยุดดูความทรงจํา",
|
||||||
"paused": "หยุด",
|
"paused": "หยุด",
|
||||||
"pending": "กำลังรอ",
|
"pending": "กำลังรอ",
|
||||||
"people": "ผู้คน",
|
"people": "ผู้คน",
|
||||||
@@ -723,7 +767,7 @@
|
|||||||
"play_motion_photo": "เล่นภาพวัตถุเคลื่อนไหว",
|
"play_motion_photo": "เล่นภาพวัตถุเคลื่อนไหว",
|
||||||
"play_or_pause_video": "เล่นหรือหยุดวิดีโอ",
|
"play_or_pause_video": "เล่นหรือหยุดวิดีโอ",
|
||||||
"port": "พอร์ต",
|
"port": "พอร์ต",
|
||||||
"preset": "",
|
"preset": "พรีเซ็ต",
|
||||||
"preview": "ตัวอย่าง",
|
"preview": "ตัวอย่าง",
|
||||||
"previous": "ก่อนหน้า",
|
"previous": "ก่อนหน้า",
|
||||||
"previous_memory": "ความทรงจําก่อนหน้า",
|
"previous_memory": "ความทรงจําก่อนหน้า",
|
||||||
@@ -739,61 +783,65 @@
|
|||||||
"refreshed": "รีเฟรช",
|
"refreshed": "รีเฟรช",
|
||||||
"refreshes_every_file": "รีเฟรชทุกไฟล์",
|
"refreshes_every_file": "รีเฟรชทุกไฟล์",
|
||||||
"remove": "ลบ",
|
"remove": "ลบ",
|
||||||
"remove_deleted_assets": "",
|
"remove_deleted_assets": "ลบสื่อที่ถูกลบ",
|
||||||
"remove_from_album": "ลบออกจากอัลบั้ม",
|
"remove_from_album": "ลบออกจากอัลบั้ม",
|
||||||
"remove_from_favorites": "เอาออกจากรายการโปรด",
|
"remove_from_favorites": "เอาออกจากรายการโปรด",
|
||||||
"remove_from_shared_link": "ลบออกจากลิงก์ที่แชร์",
|
"remove_from_shared_link": "ลบออกจากลิงก์ที่แชร์",
|
||||||
"repair": "ซ่อม",
|
"repair": "ซ่อม",
|
||||||
"repair_no_results_message": "",
|
"repair_no_results_message": "ไม่สามารถซ่อมแซมได้",
|
||||||
"replace_with_upload": "",
|
"replace_with_upload": "อัปโหลดทับรูปหรือวิดีโอนี้",
|
||||||
"require_password": "ต้องการรหัสผ่าน",
|
"require_password": "ต้องการรหัสผ่าน",
|
||||||
"reset": "รีเซ็ต",
|
"reset": "รีเซ็ต",
|
||||||
"reset_password": "ตั้งค่ารหัสผ่านใหม่",
|
"reset_password": "ตั้งค่ารหัสผ่านใหม่",
|
||||||
"reset_people_visibility": "ปรับการมองเห็นใหม่",
|
"reset_people_visibility": "ปรับการมองเห็นใหม่",
|
||||||
"restore": "เรียกคืน",
|
"restore": "เรียกคืน",
|
||||||
|
"restore_all": "เรียกคืนทั้งหมด",
|
||||||
"restore_user": "เรียกคืนผู้ใช้",
|
"restore_user": "เรียกคืนผู้ใช้",
|
||||||
"retry_upload": "ลองอัพโหลดใหม่",
|
"retry_upload": "ลองอัปโหลดใหม่",
|
||||||
"review_duplicates": "",
|
"review_duplicates": "ตรวจสอบรายการที่ซ้ำกัน",
|
||||||
"role": "บทบาท",
|
"role": "บทบาท",
|
||||||
"save": "บันทึก",
|
"save": "บันทึก",
|
||||||
"saved_profile": "โพรไฟล์ที่บันทึกไว้",
|
"saved_profile": "แก้ไขโปรไฟล์สำเร็จ",
|
||||||
"saved_settings": "การตั้งค่าที่บันทึกไว้",
|
"saved_settings": "บันทึกการตั้งค่าสำเร็จ",
|
||||||
"say_something": "พูดอะไรสักอย่าง",
|
"say_something": "พูดอะไรสักอย่าง",
|
||||||
"scan_all_libraries": "สแกนคลังภาพทั้งหมด",
|
"scan_all_libraries": "สแกนคลังภาพทั้งหมด",
|
||||||
"scan_settings": "ตั้งค่าการสแกน",
|
"scan_settings": "ตั้งค่าการสแกน",
|
||||||
"search": "ค้นหา",
|
"search": "ค้นหา",
|
||||||
"search_albums": "",
|
"search_albums": "ค้นหาอัลบั้ม",
|
||||||
"search_by_context": "",
|
"search_by_context": "ค้นหาตามบริบท",
|
||||||
"search_camera_make": "",
|
"search_camera_make": "",
|
||||||
"search_camera_model": "",
|
"search_camera_model": "",
|
||||||
"search_city": "",
|
"search_city": "ค้นหาตามเมือง",
|
||||||
"search_country": "",
|
"search_country": "ค้นหาตามประเทศ",
|
||||||
"search_for_existing_person": "",
|
"search_for_existing_person": "ค้นหาบุคคลที่มีอยู่",
|
||||||
|
"search_no_people": "ไม่พบบุคคลคน",
|
||||||
|
"search_no_people_named": "ไม่พบ \"{name}\"",
|
||||||
|
"search_options": "ตัวเลือกการค้นหา",
|
||||||
"search_people": "ค้นหาผู้คน",
|
"search_people": "ค้นหาผู้คน",
|
||||||
"search_places": "",
|
"search_places": "ค้นหาสถานที่",
|
||||||
"search_state": "",
|
"search_state": "ค้นหาตามรัฐ",
|
||||||
"search_timezone": "",
|
"search_timezone": "ค้นหาตามวันที่และเวลา",
|
||||||
"search_type": "",
|
"search_type": "ค้นหาตามประเภท",
|
||||||
"search_your_photos": "ค้นหารูปภาพของคุณ",
|
"search_your_photos": "ค้นหารูปภาพของคุณ",
|
||||||
"searching_locales": "",
|
"searching_locales": "ค้นหาตามภูมิภาค",
|
||||||
"second": "วินาที",
|
"second": "วินาที",
|
||||||
"select_album_cover": "",
|
"select_album_cover": "เลือกภาพปกอัลบั้ม",
|
||||||
"select_all": "",
|
"select_all": "เลือกทั้งหมด",
|
||||||
"select_avatar_color": "",
|
"select_avatar_color": "เลือกสีพื้นหลังของรูปโปรไฟล์",
|
||||||
"select_face": "",
|
"select_face": "เลือกใบหน้า",
|
||||||
"select_featured_photo": "เลือกภาพเด่น",
|
"select_featured_photo": "เลือกภาพเด่น",
|
||||||
"select_library_owner": "เลือกเจ้าของคลังภาพ",
|
"select_library_owner": "เลือกเจ้าของคลังภาพ",
|
||||||
"select_new_face": "",
|
"select_new_face": "เลือกใบหน้าใหม่",
|
||||||
"select_photos": "เลือกรูปภาพ",
|
"select_photos": "เลือกรูปภาพ",
|
||||||
"selected": "เลือก",
|
"selected": "เลือก",
|
||||||
"send_message": "",
|
"send_message": "ส่งข้อความ",
|
||||||
"server_stats": "",
|
"server_stats": "สถิติเซิร์ฟเวอร์",
|
||||||
"set": "",
|
"set": "",
|
||||||
"set_as_album_cover": "",
|
"set_as_album_cover": "ตั้งเป็นภาพปกอัลบั้ม",
|
||||||
"set_as_profile_picture": "",
|
"set_as_profile_picture": "ตั้งเป็นรูปโปรไฟล์",
|
||||||
"set_date_of_birth": "",
|
"set_date_of_birth": "ตั้งวันเกิด",
|
||||||
"set_profile_picture": "",
|
"set_profile_picture": "ตั้งรูปโปรไฟล์",
|
||||||
"set_slideshow_to_fullscreen": "",
|
"set_slideshow_to_fullscreen": "ตั้งค่าการนำเสนอเต็มจอ",
|
||||||
"settings": "ตั้งค่า",
|
"settings": "ตั้งค่า",
|
||||||
"settings_saved": "บันทึกการตั้งค่าแล้ว",
|
"settings_saved": "บันทึกการตั้งค่าแล้ว",
|
||||||
"share": "แชร์",
|
"share": "แชร์",
|
||||||
@@ -817,12 +865,21 @@
|
|||||||
"show_progress_bar": "แสดงความคืบหน้า แถบ",
|
"show_progress_bar": "แสดงความคืบหน้า แถบ",
|
||||||
"show_search_options": "แสดงตัวเลือกการค้นหา",
|
"show_search_options": "แสดงตัวเลือกการค้นหา",
|
||||||
"shuffle": "สับเปลี่ยน",
|
"shuffle": "สับเปลี่ยน",
|
||||||
|
"sidebar": "แถบด้านข้าง",
|
||||||
|
"sidebar_display_description": "เปิดหรือปิดแถบด้านข้าง",
|
||||||
|
"sign_out": "ออกจากระบบ",
|
||||||
"sign_up": "ลงทะเบียน",
|
"sign_up": "ลงทะเบียน",
|
||||||
"size": "ขนาด",
|
"size": "ขนาด",
|
||||||
"skip_to_content": "ข้ามไปยังเนื้อหา",
|
"skip_to_content": "ข้ามไปยังเนื้อหา",
|
||||||
"slideshow": "สไลด์",
|
"slideshow": "สไลด์",
|
||||||
"slideshow_settings": "ตั้งค่าสไลด์",
|
"slideshow_settings": "ตั้งค่าสไลด์",
|
||||||
"sort_albums_by": "เรียงอัลบั้มโดย...",
|
"sort_created": "จัดเรียงตามวันที่สร้าง",
|
||||||
|
"sort_items": "จัดเรียงรายการ",
|
||||||
|
"sort_modified": "จัดเรียงตามวันที่แก้ไข",
|
||||||
|
"sort_oldest": "จัดเรียงตามเก่าสุด",
|
||||||
|
"sort_people_by_similarity": "จุดเรียงบุคคลตามความคล้ายคลึง",
|
||||||
|
"sort_recent": "จัดเรียงใหม่ล่าสุด",
|
||||||
|
"sort_albums_by": "จัดเรียงอัลบั้มโดย...",
|
||||||
"stack": "ซ้อน",
|
"stack": "ซ้อน",
|
||||||
"stack_selected_photos": "",
|
"stack_selected_photos": "",
|
||||||
"stacktrace": "",
|
"stacktrace": "",
|
||||||
@@ -831,25 +888,30 @@
|
|||||||
"status": "สถานะ",
|
"status": "สถานะ",
|
||||||
"stop_motion_photo": "ภาพวัตถุเคลื่อนไหว",
|
"stop_motion_photo": "ภาพวัตถุเคลื่อนไหว",
|
||||||
"stop_photo_sharing": "หยุดแชร์รูปภาพ?",
|
"stop_photo_sharing": "หยุดแชร์รูปภาพ?",
|
||||||
"storage": "ที่จัดเก็บ",
|
"storage": "พื้นที่จัดเก็บ",
|
||||||
"storage_label": "ฉลากจัดเก็บ",
|
"storage_label": "เนื้อที่จัดเก็บ",
|
||||||
|
"storage_usage": "ใช้ไป {used} จาก {available} ",
|
||||||
"submit": "ส่ง",
|
"submit": "ส่ง",
|
||||||
"suggestions": "ข้อเสนอแนะ",
|
"suggestions": "ข้อเสนอแนะ",
|
||||||
"sunrise_on_the_beach": "พระอาทิตย์ขึ้นบนชายหาด",
|
"sunrise_on_the_beach": "พระอาทิตย์ขึ้นบนชายหาด",
|
||||||
"swap_merge_direction": "สลับด้านรวม",
|
"swap_merge_direction": "สลับด้านรวม",
|
||||||
"sync": "ซิงค์",
|
"sync": "ซิงค์",
|
||||||
"template": "แม่แบบ",
|
"template": "แทมแพลค",
|
||||||
"theme": "ธีม",
|
"theme": "ธีม",
|
||||||
"theme_selection": "การเลือกธีม",
|
"theme_selection": "การเลือกธีม",
|
||||||
"theme_selection_description": "ตั้งค่าธีมให้สว่างหรือมืดโดยอัตโนมัติ อิงจากค่าของเบราว์เซอร์ของคุณ",
|
"theme_selection_description": "ตั้งค่าธีมให้สว่างหรือมืดโดยอัตโนมัติ อิงจากค่าของเบราว์เซอร์ของคุณ",
|
||||||
"time_based_memories": "ความทรงจําตามเวลา",
|
"time_based_memories": "ความทรงจําตามเวลา",
|
||||||
"timezone": "เขตเวลา",
|
"timezone": "เขตเวลา",
|
||||||
|
"timeline": "Timeline",
|
||||||
|
"to_archive": "จัดเก็บถาวร",
|
||||||
|
"to_change_password": "Change password",
|
||||||
"toggle_settings": "สลับการตั้งค่า",
|
"toggle_settings": "สลับการตั้งค่า",
|
||||||
"toggle_theme": "สลับธีม",
|
"toggle_theme": "สลับธีม",
|
||||||
"total_usage": "การใช้งานรวม",
|
"total_usage": "การใช้งานรวม",
|
||||||
"trash": "ขยะ",
|
"trash": "ถังขยะ",
|
||||||
"trash_all": "ทิ้งทั้งหมด",
|
"trash_all": "ทิ้งทั้งหมด",
|
||||||
"trash_no_results_message": "รูปและวีดีโอที่ถูกทิ้งจะมาโผล่ที่นี่",
|
"trash_no_results_message": "รูปภาพหรือวิดีโอที่ถูกลบจะอยู่ที่นี่",
|
||||||
|
"trashed_items_will_be_permanently_deleted_after": "รายการที่ถูกลบจะถูกลบทิ้งภายใน {days, plural, one {# วัน} other {# วัน}}.",
|
||||||
"type": "ประเภท",
|
"type": "ประเภท",
|
||||||
"unarchive": "นำออกจากที่เก็บถาวร",
|
"unarchive": "นำออกจากที่เก็บถาวร",
|
||||||
"unfavorite": "นำออกจากรายการโปรด",
|
"unfavorite": "นำออกจากรายการโปรด",
|
||||||
@@ -862,8 +924,8 @@
|
|||||||
"unstack": "หยุดซ้อน",
|
"unstack": "หยุดซ้อน",
|
||||||
"up_next": "ต่อไป",
|
"up_next": "ต่อไป",
|
||||||
"updated_password": "รหัสผ่านเปลี่ยนแล้ว",
|
"updated_password": "รหัสผ่านเปลี่ยนแล้ว",
|
||||||
"upload": "อัพโหลด",
|
"upload": "อัปโหลด",
|
||||||
"upload_concurrency": "อัพโหลดพร้อมกัน",
|
"upload_concurrency": "อัปโหลดพร้อมกัน",
|
||||||
"url": "URL",
|
"url": "URL",
|
||||||
"usage": "การใช้งาน",
|
"usage": "การใช้งาน",
|
||||||
"user": "ผู้ใช้",
|
"user": "ผู้ใช้",
|
||||||
@@ -873,7 +935,7 @@
|
|||||||
"user_usage_stats_description": "ดูสถิติการใช้งานบัญชี",
|
"user_usage_stats_description": "ดูสถิติการใช้งานบัญชี",
|
||||||
"username": "ชื่อผู้ใช้",
|
"username": "ชื่อผู้ใช้",
|
||||||
"users": "ผู้ใช้",
|
"users": "ผู้ใช้",
|
||||||
"utilities": "",
|
"utilities": "เครื่องมือ",
|
||||||
"validate": "ตรวจสอบ",
|
"validate": "ตรวจสอบ",
|
||||||
"variables": "ตัวแปร",
|
"variables": "ตัวแปร",
|
||||||
"version": "รุ่น",
|
"version": "รุ่น",
|
||||||
|
|||||||
@@ -51,6 +51,10 @@ start_docker_compose() {
|
|||||||
show_friendly_message() {
|
show_friendly_message() {
|
||||||
local ip_address
|
local ip_address
|
||||||
ip_address=$(hostname -I | awk '{print $1}')
|
ip_address=$(hostname -I | awk '{print $1}')
|
||||||
|
# If length of ip_address is 0, then we are on a Mac
|
||||||
|
if [ ${#ip_address} -eq 0 ]; then
|
||||||
|
ip_address=$(ipconfig getifaddr en0)
|
||||||
|
fi
|
||||||
cat <<EOF
|
cat <<EOF
|
||||||
Successfully deployed Immich!
|
Successfully deployed Immich!
|
||||||
You can access the website or the mobile app at http://$ip_address:2283
|
You can access the website or the mobile app at http://$ip_address:2283
|
||||||
|
|||||||
@@ -96,8 +96,8 @@ download:
|
|||||||
locale_code: ro-RO
|
locale_code: ro-RO
|
||||||
- file: mobile/assets/i18n/id-ID.json
|
- file: mobile/assets/i18n/id-ID.json
|
||||||
locale_code: id-ID
|
locale_code: id-ID
|
||||||
- file: mobile/assets/i18n/gl.json
|
- file: mobile/assets/i18n/gl-ES.json
|
||||||
locale_code: gl
|
locale_code: gl-ES
|
||||||
- file: mobile/assets/i18n/ga.json
|
- file: mobile/assets/i18n/ga.json
|
||||||
locale_code: ga
|
locale_code: ga
|
||||||
- file: mobile/assets/i18n/tr-TR.json
|
- file: mobile/assets/i18n/tr-TR.json
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
ARG DEVICE=cpu
|
ARG DEVICE=cpu
|
||||||
|
|
||||||
FROM python:3.11-bookworm@sha256:adb581d8ed80edd03efd4dcad66db115b9ce8de8522b01720b9f3e6146f0884c AS builder-cpu
|
FROM python:3.11-bookworm@sha256:14b4620f59a90f163dfa6bd252b68743f9a41d494a9fde935f9d7669d98094bb AS builder-cpu
|
||||||
|
|
||||||
FROM builder-cpu AS builder-openvino
|
FROM builder-cpu AS builder-openvino
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ RUN python3 -m venv /opt/venv
|
|||||||
COPY poetry.lock pyproject.toml ./
|
COPY poetry.lock pyproject.toml ./
|
||||||
RUN poetry install --sync --no-interaction --no-ansi --no-root --with ${DEVICE} --without dev
|
RUN poetry install --sync --no-interaction --no-ansi --no-root --with ${DEVICE} --without dev
|
||||||
|
|
||||||
FROM python:3.11-slim-bookworm@sha256:6ed5bff4d7d377e2a27d9285553b8c21cfccc4f00881de1b24c9bc8d90016e82 AS prod-cpu
|
FROM python:3.11-slim-bookworm@sha256:42420f737ba91d509fc60d5ed65ed0492678a90c561e1fa08786ae8ba8b52eda AS prod-cpu
|
||||||
|
|
||||||
FROM prod-cpu AS prod-openvino
|
FROM prod-cpu AS prod-openvino
|
||||||
|
|
||||||
@@ -106,6 +106,22 @@ COPY --from=builder /opt/venv /opt/venv
|
|||||||
COPY ann/ann.py /usr/src/ann/ann.py
|
COPY ann/ann.py /usr/src/ann/ann.py
|
||||||
COPY start.sh log_conf.json gunicorn_conf.py ./
|
COPY start.sh log_conf.json gunicorn_conf.py ./
|
||||||
COPY app .
|
COPY app .
|
||||||
|
|
||||||
|
ARG BUILD_ID
|
||||||
|
ARG BUILD_IMAGE
|
||||||
|
ARG BUILD_SOURCE_REF
|
||||||
|
ARG BUILD_SOURCE_COMMIT
|
||||||
|
|
||||||
|
ENV IMMICH_BUILD=${BUILD_ID}
|
||||||
|
ENV IMMICH_BUILD_URL=https://github.com/immich-app/immich/actions/runs/${BUILD_ID}
|
||||||
|
ENV IMMICH_BUILD_IMAGE=${BUILD_IMAGE}
|
||||||
|
ENV IMMICH_BUILD_IMAGE_URL=https://github.com/immich-app/immich/pkgs/container/immich-machine-learning
|
||||||
|
ENV IMMICH_REPOSITORY=immich-app/immich
|
||||||
|
ENV IMMICH_REPOSITORY_URL=https://github.com/immich-app/immich
|
||||||
|
ENV IMMICH_SOURCE_REF=${BUILD_SOURCE_REF}
|
||||||
|
ENV IMMICH_SOURCE_COMMIT=${BUILD_SOURCE_COMMIT}
|
||||||
|
ENV IMMICH_SOURCE_URL=https://github.com/immich-app/immich/commit/${BUILD_SOURCE_COMMIT}
|
||||||
|
|
||||||
ENTRYPOINT ["tini", "--"]
|
ENTRYPOINT ["tini", "--"]
|
||||||
CMD ["./start.sh"]
|
CMD ["./start.sh"]
|
||||||
|
|
||||||
|
|||||||
314
machine-learning/poetry.lock
generated
314
machine-learning/poetry.lock
generated
@@ -758,23 +758,23 @@ test = ["pytest (>=6)"]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fastapi"
|
name = "fastapi"
|
||||||
version = "0.115.6"
|
version = "0.115.8"
|
||||||
description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
|
description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "fastapi-0.115.6-py3-none-any.whl", hash = "sha256:e9240b29e36fa8f4bb7290316988e90c381e5092e0cbe84e7818cc3713bcf305"},
|
{file = "fastapi-0.115.8-py3-none-any.whl", hash = "sha256:753a96dd7e036b34eeef8babdfcfe3f28ff79648f86551eb36bfc1b0bf4a8cbf"},
|
||||||
{file = "fastapi-0.115.6.tar.gz", hash = "sha256:9ec46f7addc14ea472958a96aae5b5de65f39721a46aaf5705c480d9a8b76654"},
|
{file = "fastapi-0.115.8.tar.gz", hash = "sha256:0ce9111231720190473e222cdf0f07f7206ad7e53ea02beb1d2dc36e2f0741e9"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0"
|
pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0"
|
||||||
starlette = ">=0.40.0,<0.42.0"
|
starlette = ">=0.40.0,<0.46.0"
|
||||||
typing-extensions = ">=4.8.0"
|
typing-extensions = ">=4.8.0"
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
all = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"]
|
all = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=3.1.5)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.18)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"]
|
||||||
standard = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "jinja2 (>=2.11.2)", "python-multipart (>=0.0.7)", "uvicorn[standard] (>=0.12.0)"]
|
standard = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "jinja2 (>=3.1.5)", "python-multipart (>=0.0.18)", "uvicorn[standard] (>=0.12.0)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "filelock"
|
name = "filelock"
|
||||||
@@ -1331,13 +1331,13 @@ zstd = ["zstandard (>=0.18.0)"]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "huggingface-hub"
|
name = "huggingface-hub"
|
||||||
version = "0.27.1"
|
version = "0.28.1"
|
||||||
description = "Client library to download and publish models, datasets and other repos on the huggingface.co hub"
|
description = "Client library to download and publish models, datasets and other repos on the huggingface.co hub"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8.0"
|
python-versions = ">=3.8.0"
|
||||||
files = [
|
files = [
|
||||||
{file = "huggingface_hub-0.27.1-py3-none-any.whl", hash = "sha256:1c5155ca7d60b60c2e2fc38cbb3ffb7f7c3adf48f824015b219af9061771daec"},
|
{file = "huggingface_hub-0.28.1-py3-none-any.whl", hash = "sha256:aa6b9a3ffdae939b72c464dbb0d7f99f56e649b55c3d52406f49e0a5a620c0a7"},
|
||||||
{file = "huggingface_hub-0.27.1.tar.gz", hash = "sha256:c004463ca870283909d715d20f066ebd6968c2207dae9393fdffb3c1d4d8f98b"},
|
{file = "huggingface_hub-0.28.1.tar.gz", hash = "sha256:893471090c98e3b6efbdfdacafe4052b20b84d59866fb6f54c33d9af18c303ae"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@@ -1350,13 +1350,13 @@ tqdm = ">=4.42.1"
|
|||||||
typing-extensions = ">=3.7.4.3"
|
typing-extensions = ">=3.7.4.3"
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
all = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio (>=4.0.0)", "jedi", "libcst (==1.4.0)", "mypy (==1.5.1)", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.5.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"]
|
all = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio (>=4.0.0)", "jedi", "libcst (==1.4.0)", "mypy (==1.5.1)", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.9.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"]
|
||||||
cli = ["InquirerPy (==0.3.4)"]
|
cli = ["InquirerPy (==0.3.4)"]
|
||||||
dev = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio (>=4.0.0)", "jedi", "libcst (==1.4.0)", "mypy (==1.5.1)", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.5.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"]
|
dev = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio (>=4.0.0)", "jedi", "libcst (==1.4.0)", "mypy (==1.5.1)", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.9.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"]
|
||||||
fastai = ["fastai (>=2.4)", "fastcore (>=1.3.27)", "toml"]
|
fastai = ["fastai (>=2.4)", "fastcore (>=1.3.27)", "toml"]
|
||||||
hf-transfer = ["hf-transfer (>=0.1.4)"]
|
hf-transfer = ["hf-transfer (>=0.1.4)"]
|
||||||
inference = ["aiohttp"]
|
inference = ["aiohttp"]
|
||||||
quality = ["libcst (==1.4.0)", "mypy (==1.5.1)", "ruff (>=0.5.0)"]
|
quality = ["libcst (==1.4.0)", "mypy (==1.5.1)", "ruff (>=0.9.0)"]
|
||||||
tensorflow = ["graphviz", "pydot", "tensorflow"]
|
tensorflow = ["graphviz", "pydot", "tensorflow"]
|
||||||
tensorflow-testing = ["keras (<3.0)", "tensorflow"]
|
tensorflow-testing = ["keras (<3.0)", "tensorflow"]
|
||||||
testing = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio (>=4.0.0)", "jedi", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "soundfile", "urllib3 (<2.0)"]
|
testing = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio (>=4.0.0)", "jedi", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "soundfile", "urllib3 (<2.0)"]
|
||||||
@@ -1625,13 +1625,13 @@ test = ["pytest (>=7.4)", "pytest-cov (>=4.1)"]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "locust"
|
name = "locust"
|
||||||
version = "2.32.6"
|
version = "2.32.9"
|
||||||
description = "Developer-friendly load testing framework"
|
description = "Developer-friendly load testing framework"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.9"
|
python-versions = ">=3.9"
|
||||||
files = [
|
files = [
|
||||||
{file = "locust-2.32.6-py3-none-any.whl", hash = "sha256:d5c0e4f73134415d250087034431cf3ea42ca695d3dee7f10812287cacb6c4ef"},
|
{file = "locust-2.32.9-py3-none-any.whl", hash = "sha256:d9447c26d2bbaec5a0ace7cadefa1a31820ed392234257b309965a43d5e8d26f"},
|
||||||
{file = "locust-2.32.6.tar.gz", hash = "sha256:6600cc308398e724764aacc56ccddf6cfcd0127c4c92dedd5c4979dd37ef5b15"},
|
{file = "locust-2.32.9.tar.gz", hash = "sha256:4c297afa5cdc3de15dfa79279576e5f33c1d69dd70006b51d079dcbd212201cc"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@@ -1649,8 +1649,8 @@ psutil = ">=5.9.1"
|
|||||||
pywin32 = {version = "*", markers = "sys_platform == \"win32\""}
|
pywin32 = {version = "*", markers = "sys_platform == \"win32\""}
|
||||||
pyzmq = ">=25.0.0"
|
pyzmq = ">=25.0.0"
|
||||||
requests = [
|
requests = [
|
||||||
{version = ">=2.32.2", markers = "python_full_version > \"3.11.0\""},
|
|
||||||
{version = ">=2.26.0", markers = "python_full_version <= \"3.11.0\""},
|
{version = ">=2.26.0", markers = "python_full_version <= \"3.11.0\""},
|
||||||
|
{version = ">=2.32.2", markers = "python_full_version > \"3.11.0\""},
|
||||||
]
|
]
|
||||||
setuptools = ">=70.0.0"
|
setuptools = ">=70.0.0"
|
||||||
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
|
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
|
||||||
@@ -1893,49 +1893,43 @@ files = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mypy"
|
name = "mypy"
|
||||||
version = "1.14.1"
|
version = "1.15.0"
|
||||||
description = "Optional static typing for Python"
|
description = "Optional static typing for Python"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.9"
|
||||||
files = [
|
files = [
|
||||||
{file = "mypy-1.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:52686e37cf13d559f668aa398dd7ddf1f92c5d613e4f8cb262be2fb4fedb0fcb"},
|
{file = "mypy-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:979e4e1a006511dacf628e36fadfecbcc0160a8af6ca7dad2f5025529e082c13"},
|
||||||
{file = "mypy-1.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1fb545ca340537d4b45d3eecdb3def05e913299ca72c290326be19b3804b39c0"},
|
{file = "mypy-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c4bb0e1bd29f7d34efcccd71cf733580191e9a264a2202b0239da95984c5b559"},
|
||||||
{file = "mypy-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:90716d8b2d1f4cd503309788e51366f07c56635a3309b0f6a32547eaaa36a64d"},
|
{file = "mypy-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be68172e9fd9ad8fb876c6389f16d1c1b5f100ffa779f77b1fb2176fcc9ab95b"},
|
||||||
{file = "mypy-1.14.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ae753f5c9fef278bcf12e1a564351764f2a6da579d4a81347e1d5a15819997b"},
|
{file = "mypy-1.15.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c7be1e46525adfa0d97681432ee9fcd61a3964c2446795714699a998d193f1a3"},
|
||||||
{file = "mypy-1.14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e0fe0f5feaafcb04505bcf439e991c6d8f1bf8b15f12b05feeed96e9e7bf1427"},
|
{file = "mypy-1.15.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2e2c2e6d3593f6451b18588848e66260ff62ccca522dd231cd4dd59b0160668b"},
|
||||||
{file = "mypy-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:7d54bd85b925e501c555a3227f3ec0cfc54ee8b6930bd6141ec872d1c572f81f"},
|
{file = "mypy-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:6983aae8b2f653e098edb77f893f7b6aca69f6cffb19b2cc7443f23cce5f4828"},
|
||||||
{file = "mypy-1.14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f995e511de847791c3b11ed90084a7a0aafdc074ab88c5a9711622fe4751138c"},
|
{file = "mypy-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2922d42e16d6de288022e5ca321cd0618b238cfc5570e0263e5ba0a77dbef56f"},
|
||||||
{file = "mypy-1.14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d64169ec3b8461311f8ce2fd2eb5d33e2d0f2c7b49116259c51d0d96edee48d1"},
|
{file = "mypy-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2ee2d57e01a7c35de00f4634ba1bbf015185b219e4dc5909e281016df43f5ee5"},
|
||||||
{file = "mypy-1.14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba24549de7b89b6381b91fbc068d798192b1b5201987070319889e93038967a8"},
|
{file = "mypy-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:973500e0774b85d9689715feeffcc980193086551110fd678ebe1f4342fb7c5e"},
|
||||||
{file = "mypy-1.14.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:183cf0a45457d28ff9d758730cd0210419ac27d4d3f285beda038c9083363b1f"},
|
{file = "mypy-1.15.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a95fb17c13e29d2d5195869262f8125dfdb5c134dc8d9a9d0aecf7525b10c2c"},
|
||||||
{file = "mypy-1.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f2a0ecc86378f45347f586e4163d1769dd81c5a223d577fe351f26b179e148b1"},
|
{file = "mypy-1.15.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1905f494bfd7d85a23a88c5d97840888a7bd516545fc5aaedff0267e0bb54e2f"},
|
||||||
{file = "mypy-1.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:ad3301ebebec9e8ee7135d8e3109ca76c23752bac1e717bc84cd3836b4bf3eae"},
|
{file = "mypy-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:c9817fa23833ff189db061e6d2eff49b2f3b6ed9856b4a0a73046e41932d744f"},
|
||||||
{file = "mypy-1.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:30ff5ef8519bbc2e18b3b54521ec319513a26f1bba19a7582e7b1f58a6e69f14"},
|
{file = "mypy-1.15.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:aea39e0583d05124836ea645f412e88a5c7d0fd77a6d694b60d9b6b2d9f184fd"},
|
||||||
{file = "mypy-1.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cb9f255c18052343c70234907e2e532bc7e55a62565d64536dbc7706a20b78b9"},
|
{file = "mypy-1.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f2147ab812b75e5b5499b01ade1f4a81489a147c01585cda36019102538615f"},
|
||||||
{file = "mypy-1.14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b4e3413e0bddea671012b063e27591b953d653209e7a4fa5e48759cda77ca11"},
|
{file = "mypy-1.15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce436f4c6d218a070048ed6a44c0bbb10cd2cc5e272b29e7845f6a2f57ee4464"},
|
||||||
{file = "mypy-1.14.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:553c293b1fbdebb6c3c4030589dab9fafb6dfa768995a453d8a5d3b23784af2e"},
|
{file = "mypy-1.15.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8023ff13985661b50a5928fc7a5ca15f3d1affb41e5f0a9952cb68ef090b31ee"},
|
||||||
{file = "mypy-1.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fad79bfe3b65fe6a1efaed97b445c3d37f7be9fdc348bdb2d7cac75579607c89"},
|
{file = "mypy-1.15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1124a18bc11a6a62887e3e137f37f53fbae476dc36c185d549d4f837a2a6a14e"},
|
||||||
{file = "mypy-1.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:8fa2220e54d2946e94ab6dbb3ba0a992795bd68b16dc852db33028df2b00191b"},
|
{file = "mypy-1.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:171a9ca9a40cd1843abeca0e405bc1940cd9b305eaeea2dda769ba096932bb22"},
|
||||||
{file = "mypy-1.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:92c3ed5afb06c3a8e188cb5da4984cab9ec9a77ba956ee419c68a388b4595255"},
|
{file = "mypy-1.15.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93faf3fdb04768d44bf28693293f3904bbb555d076b781ad2530214ee53e3445"},
|
||||||
{file = "mypy-1.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dbec574648b3e25f43d23577309b16534431db4ddc09fda50841f1e34e64ed34"},
|
{file = "mypy-1.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:811aeccadfb730024c5d3e326b2fbe9249bb7413553f15499a4050f7c30e801d"},
|
||||||
{file = "mypy-1.14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8c6d94b16d62eb3e947281aa7347d78236688e21081f11de976376cf010eb31a"},
|
{file = "mypy-1.15.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:98b7b9b9aedb65fe628c62a6dc57f6d5088ef2dfca37903a7d9ee374d03acca5"},
|
||||||
{file = "mypy-1.14.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d4b19b03fdf54f3c5b2fa474c56b4c13c9dbfb9a2db4370ede7ec11a2c5927d9"},
|
{file = "mypy-1.15.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c43a7682e24b4f576d93072216bf56eeff70d9140241f9edec0c104d0c515036"},
|
||||||
{file = "mypy-1.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0c911fde686394753fff899c409fd4e16e9b294c24bfd5e1ea4675deae1ac6fd"},
|
{file = "mypy-1.15.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:baefc32840a9f00babd83251560e0ae1573e2f9d1b067719479bfb0e987c6357"},
|
||||||
{file = "mypy-1.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:8b21525cb51671219f5307be85f7e646a153e5acc656e5cebf64bfa076c50107"},
|
{file = "mypy-1.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:b9378e2c00146c44793c98b8d5a61039a048e31f429fb0eb546d93f4b000bedf"},
|
||||||
{file = "mypy-1.14.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7084fb8f1128c76cd9cf68fe5971b37072598e7c31b2f9f95586b65c741a9d31"},
|
{file = "mypy-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e601a7fa172c2131bff456bb3ee08a88360760d0d2f8cbd7a75a65497e2df078"},
|
||||||
{file = "mypy-1.14.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8f845a00b4f420f693f870eaee5f3e2692fa84cc8514496114649cfa8fd5e2c6"},
|
{file = "mypy-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:712e962a6357634fef20412699a3655c610110e01cdaa6180acec7fc9f8513ba"},
|
||||||
{file = "mypy-1.14.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:44bf464499f0e3a2d14d58b54674dee25c031703b2ffc35064bd0df2e0fac319"},
|
{file = "mypy-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95579473af29ab73a10bada2f9722856792a36ec5af5399b653aa28360290a5"},
|
||||||
{file = "mypy-1.14.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c99f27732c0b7dc847adb21c9d47ce57eb48fa33a17bc6d7d5c5e9f9e7ae5bac"},
|
{file = "mypy-1.15.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8f8722560a14cde92fdb1e31597760dc35f9f5524cce17836c0d22841830fd5b"},
|
||||||
{file = "mypy-1.14.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:bce23c7377b43602baa0bd22ea3265c49b9ff0b76eb315d6c34721af4cdf1d9b"},
|
{file = "mypy-1.15.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1fbb8da62dc352133d7d7ca90ed2fb0e9d42bb1a32724c287d3c76c58cbaa9c2"},
|
||||||
{file = "mypy-1.14.1-cp38-cp38-win_amd64.whl", hash = "sha256:8edc07eeade7ebc771ff9cf6b211b9a7d93687ff892150cb5692e4f4272b0837"},
|
{file = "mypy-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:d10d994b41fb3497719bbf866f227b3489048ea4bbbb5015357db306249f7980"},
|
||||||
{file = "mypy-1.14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3888a1816d69f7ab92092f785a462944b3ca16d7c470d564165fe703b0970c35"},
|
{file = "mypy-1.15.0-py3-none-any.whl", hash = "sha256:5469affef548bd1895d86d3bf10ce2b44e33d86923c29e4d675b3e323437ea3e"},
|
||||||
{file = "mypy-1.14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:46c756a444117c43ee984bd055db99e498bc613a70bbbc120272bd13ca579fbc"},
|
{file = "mypy-1.15.0.tar.gz", hash = "sha256:404534629d51d3efea5c800ee7c42b72a6554d6c400e6a79eafe15d11341fd43"},
|
||||||
{file = "mypy-1.14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:27fc248022907e72abfd8e22ab1f10e903915ff69961174784a3900a8cba9ad9"},
|
|
||||||
{file = "mypy-1.14.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:499d6a72fb7e5de92218db961f1a66d5f11783f9ae549d214617edab5d4dbdbb"},
|
|
||||||
{file = "mypy-1.14.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:57961db9795eb566dc1d1b4e9139ebc4c6b0cb6e7254ecde69d1552bf7613f60"},
|
|
||||||
{file = "mypy-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:07ba89fdcc9451f2ebb02853deb6aaaa3d2239a236669a63ab3801bbf923ef5c"},
|
|
||||||
{file = "mypy-1.14.1-py3-none-any.whl", hash = "sha256:b66a60cc4073aeb8ae00057f9c1f64d49e90f918fbcef9a977eb121da8b8f1d1"},
|
|
||||||
{file = "mypy-1.14.1.tar.gz", hash = "sha256:7ec88144fe9b510e8475ec2f5f251992690fcf89ccb4500b214b4226abcd32d6"},
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@@ -2181,94 +2175,98 @@ files = [
|
|||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
numpy = [
|
numpy = [
|
||||||
{version = ">=1.26.0", markers = "python_version >= \"3.12\""},
|
|
||||||
{version = ">=1.23.5", markers = "python_version >= \"3.11\" and python_version < \"3.12\""},
|
{version = ">=1.23.5", markers = "python_version >= \"3.11\" and python_version < \"3.12\""},
|
||||||
{version = ">=1.21.4", markers = "python_version >= \"3.10\" and platform_system == \"Darwin\" and python_version < \"3.11\""},
|
{version = ">=1.21.4", markers = "python_version >= \"3.10\" and platform_system == \"Darwin\" and python_version < \"3.11\""},
|
||||||
{version = ">=1.21.2", markers = "platform_system != \"Darwin\" and python_version >= \"3.10\" and python_version < \"3.11\""},
|
{version = ">=1.21.2", markers = "platform_system != \"Darwin\" and python_version >= \"3.10\" and python_version < \"3.11\""},
|
||||||
|
{version = ">=1.26.0", markers = "python_version >= \"3.12\""},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "orjson"
|
name = "orjson"
|
||||||
version = "3.10.14"
|
version = "3.10.15"
|
||||||
description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy"
|
description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "orjson-3.10.14-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:849ea7845a55f09965826e816cdc7689d6cf74fe9223d79d758c714af955bcb6"},
|
{file = "orjson-3.10.15-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:552c883d03ad185f720d0c09583ebde257e41b9521b74ff40e08b7dec4559c04"},
|
||||||
{file = "orjson-3.10.14-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5947b139dfa33f72eecc63f17e45230a97e741942955a6c9e650069305eb73d"},
|
{file = "orjson-3.10.15-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:616e3e8d438d02e4854f70bfdc03a6bcdb697358dbaa6bcd19cbe24d24ece1f8"},
|
||||||
{file = "orjson-3.10.14-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cde6d76910d3179dae70f164466692f4ea36da124d6fb1a61399ca589e81d69a"},
|
{file = "orjson-3.10.15-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7c2c79fa308e6edb0ffab0a31fd75a7841bf2a79a20ef08a3c6e3b26814c8ca8"},
|
||||||
{file = "orjson-3.10.14-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c6dfbaeb7afa77ca608a50e2770a0461177b63a99520d4928e27591b142c74b1"},
|
{file = "orjson-3.10.15-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73cb85490aa6bf98abd20607ab5c8324c0acb48d6da7863a51be48505646c814"},
|
||||||
{file = "orjson-3.10.14-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fa45e489ef80f28ff0e5ba0a72812b8cfc7c1ef8b46a694723807d1b07c89ebb"},
|
{file = "orjson-3.10.15-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:763dadac05e4e9d2bc14938a45a2d0560549561287d41c465d3c58aec818b164"},
|
||||||
{file = "orjson-3.10.14-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f5007abfdbb1d866e2aa8990bd1c465f0f6da71d19e695fc278282be12cffa5"},
|
{file = "orjson-3.10.15-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a330b9b4734f09a623f74a7490db713695e13b67c959713b78369f26b3dee6bf"},
|
||||||
{file = "orjson-3.10.14-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1b49e2af011c84c3f2d541bb5cd1e3c7c2df672223e7e3ea608f09cf295e5f8a"},
|
{file = "orjson-3.10.15-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a61a4622b7ff861f019974f73d8165be1bd9a0855e1cad18ee167acacabeb061"},
|
||||||
{file = "orjson-3.10.14-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:164ac155109226b3a2606ee6dda899ccfbe6e7e18b5bdc3fbc00f79cc074157d"},
|
{file = "orjson-3.10.15-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:acd271247691574416b3228db667b84775c497b245fa275c6ab90dc1ffbbd2b3"},
|
||||||
{file = "orjson-3.10.14-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:6b1225024cf0ef5d15934b5ffe9baf860fe8bc68a796513f5ea4f5056de30bca"},
|
{file = "orjson-3.10.15-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:e4759b109c37f635aa5c5cc93a1b26927bfde24b254bcc0e1149a9fada253d2d"},
|
||||||
{file = "orjson-3.10.14-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:d6546e8073dc382e60fcae4a001a5a1bc46da5eab4a4878acc2d12072d6166d5"},
|
{file = "orjson-3.10.15-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:9e992fd5cfb8b9f00bfad2fd7a05a4299db2bbe92e6440d9dd2fab27655b3182"},
|
||||||
{file = "orjson-3.10.14-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9f1d2942605c894162252d6259b0121bf1cb493071a1ea8cb35d79cb3e6ac5bc"},
|
{file = "orjson-3.10.15-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f95fb363d79366af56c3f26b71df40b9a583b07bbaaf5b317407c4d58497852e"},
|
||||||
{file = "orjson-3.10.14-cp310-cp310-win32.whl", hash = "sha256:397083806abd51cf2b3bbbf6c347575374d160331a2d33c5823e22249ad3118b"},
|
{file = "orjson-3.10.15-cp310-cp310-win32.whl", hash = "sha256:f9875f5fea7492da8ec2444839dcc439b0ef298978f311103d0b7dfd775898ab"},
|
||||||
{file = "orjson-3.10.14-cp310-cp310-win_amd64.whl", hash = "sha256:fa18f949d3183a8d468367056be989666ac2bef3a72eece0bade9cdb733b3c28"},
|
{file = "orjson-3.10.15-cp310-cp310-win_amd64.whl", hash = "sha256:17085a6aa91e1cd70ca8533989a18b5433e15d29c574582f76f821737c8d5806"},
|
||||||
{file = "orjson-3.10.14-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:f506fd666dd1ecd15a832bebc66c4df45c1902fd47526292836c339f7ba665a9"},
|
{file = "orjson-3.10.15-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:c4cc83960ab79a4031f3119cc4b1a1c627a3dc09df125b27c4201dff2af7eaa6"},
|
||||||
{file = "orjson-3.10.14-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efe5fd254cfb0eeee13b8ef7ecb20f5d5a56ddda8a587f3852ab2cedfefdb5f6"},
|
{file = "orjson-3.10.15-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ddbeef2481d895ab8be5185f2432c334d6dec1f5d1933a9c83014d188e102cef"},
|
||||||
{file = "orjson-3.10.14-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4ddc8c866d7467f5ee2991397d2ea94bcf60d0048bdd8ca555740b56f9042725"},
|
{file = "orjson-3.10.15-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9e590a0477b23ecd5b0ac865b1b907b01b3c5535f5e8a8f6ab0e503efb896334"},
|
||||||
{file = "orjson-3.10.14-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3af8e42ae4363773658b8d578d56dedffb4f05ceeb4d1d4dd3fb504950b45526"},
|
{file = "orjson-3.10.15-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a6be38bd103d2fd9bdfa31c2720b23b5d47c6796bcb1d1b598e3924441b4298d"},
|
||||||
{file = "orjson-3.10.14-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:84dd83110503bc10e94322bf3ffab8bc49150176b49b4984dc1cce4c0a993bf9"},
|
{file = "orjson-3.10.15-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ff4f6edb1578960ed628a3b998fa54d78d9bb3e2eb2cfc5c2a09732431c678d0"},
|
||||||
{file = "orjson-3.10.14-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36f5bfc0399cd4811bf10ec7a759c7ab0cd18080956af8ee138097d5b5296a95"},
|
{file = "orjson-3.10.15-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0482b21d0462eddd67e7fce10b89e0b6ac56570424662b685a0d6fccf581e13"},
|
||||||
{file = "orjson-3.10.14-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:868943660fb2a1e6b6b965b74430c16a79320b665b28dd4511d15ad5038d37d5"},
|
{file = "orjson-3.10.15-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bb5cc3527036ae3d98b65e37b7986a918955f85332c1ee07f9d3f82f3a6899b5"},
|
||||||
{file = "orjson-3.10.14-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:33449c67195969b1a677533dee9d76e006001213a24501333624623e13c7cc8e"},
|
{file = "orjson-3.10.15-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d569c1c462912acdd119ccbf719cf7102ea2c67dd03b99edcb1a3048651ac96b"},
|
||||||
{file = "orjson-3.10.14-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:e4c9f60f9fb0b5be66e416dcd8c9d94c3eabff3801d875bdb1f8ffc12cf86905"},
|
{file = "orjson-3.10.15-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:1e6d33efab6b71d67f22bf2962895d3dc6f82a6273a965fab762e64fa90dc399"},
|
||||||
{file = "orjson-3.10.14-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0de4d6315cfdbd9ec803b945c23b3a68207fd47cbe43626036d97e8e9561a436"},
|
{file = "orjson-3.10.15-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:c33be3795e299f565681d69852ac8c1bc5c84863c0b0030b2b3468843be90388"},
|
||||||
{file = "orjson-3.10.14-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:83adda3db595cb1a7e2237029b3249c85afbe5c747d26b41b802e7482cb3933e"},
|
{file = "orjson-3.10.15-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:eea80037b9fae5339b214f59308ef0589fc06dc870578b7cce6d71eb2096764c"},
|
||||||
{file = "orjson-3.10.14-cp311-cp311-win32.whl", hash = "sha256:998019ef74a4997a9d741b1473533cdb8faa31373afc9849b35129b4b8ec048d"},
|
{file = "orjson-3.10.15-cp311-cp311-win32.whl", hash = "sha256:d5ac11b659fd798228a7adba3e37c010e0152b78b1982897020a8e019a94882e"},
|
||||||
{file = "orjson-3.10.14-cp311-cp311-win_amd64.whl", hash = "sha256:9d034abdd36f0f0f2240f91492684e5043d46f290525d1117712d5b8137784eb"},
|
{file = "orjson-3.10.15-cp311-cp311-win_amd64.whl", hash = "sha256:cf45e0214c593660339ef63e875f32ddd5aa3b4adc15e662cdb80dc49e194f8e"},
|
||||||
{file = "orjson-3.10.14-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:2ad4b7e367efba6dc3f119c9a0fcd41908b7ec0399a696f3cdea7ec477441b09"},
|
{file = "orjson-3.10.15-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9d11c0714fc85bfcf36ada1179400862da3288fc785c30e8297844c867d7505a"},
|
||||||
{file = "orjson-3.10.14-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f496286fc85e93ce0f71cc84fc1c42de2decf1bf494094e188e27a53694777a7"},
|
{file = "orjson-3.10.15-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dba5a1e85d554e3897fa9fe6fbcff2ed32d55008973ec9a2b992bd9a65d2352d"},
|
||||||
{file = "orjson-3.10.14-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c7f189bbfcded40e41a6969c1068ba305850ba016665be71a217918931416fbf"},
|
{file = "orjson-3.10.15-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7723ad949a0ea502df656948ddd8b392780a5beaa4c3b5f97e525191b102fff0"},
|
||||||
{file = "orjson-3.10.14-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8cc8204f0b75606869c707da331058ddf085de29558b516fc43c73ee5ee2aadb"},
|
{file = "orjson-3.10.15-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6fd9bc64421e9fe9bd88039e7ce8e58d4fead67ca88e3a4014b143cec7684fd4"},
|
||||||
{file = "orjson-3.10.14-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:deaa2899dff7f03ab667e2ec25842d233e2a6a9e333efa484dfe666403f3501c"},
|
{file = "orjson-3.10.15-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dadba0e7b6594216c214ef7894c4bd5f08d7c0135f4dd0145600be4fbcc16767"},
|
||||||
{file = "orjson-3.10.14-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1c3ea52642c9714dc6e56de8a451a066f6d2707d273e07fe8a9cc1ba073813d"},
|
{file = "orjson-3.10.15-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b48f59114fe318f33bbaee8ebeda696d8ccc94c9e90bc27dbe72153094e26f41"},
|
||||||
{file = "orjson-3.10.14-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9d3f9ed72e7458ded9a1fb1b4d4ed4c4fdbaf82030ce3f9274b4dc1bff7ace2b"},
|
{file = "orjson-3.10.15-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:035fb83585e0f15e076759b6fedaf0abb460d1765b6a36f48018a52858443514"},
|
||||||
{file = "orjson-3.10.14-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:07520685d408a2aba514c17ccc16199ff2934f9f9e28501e676c557f454a37fe"},
|
{file = "orjson-3.10.15-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d13b7fe322d75bf84464b075eafd8e7dd9eae05649aa2a5354cfa32f43c59f17"},
|
||||||
{file = "orjson-3.10.14-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:76344269b550ea01488d19a2a369ab572c1ac4449a72e9f6ac0d70eb1cbfb953"},
|
{file = "orjson-3.10.15-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:7066b74f9f259849629e0d04db6609db4cf5b973248f455ba5d3bd58a4daaa5b"},
|
||||||
{file = "orjson-3.10.14-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e2979d0f2959990620f7e62da6cd954e4620ee815539bc57a8ae46e2dacf90e3"},
|
{file = "orjson-3.10.15-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:88dc3f65a026bd3175eb157fea994fca6ac7c4c8579fc5a86fc2114ad05705b7"},
|
||||||
{file = "orjson-3.10.14-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:03f61ca3674555adcb1aa717b9fc87ae936aa7a63f6aba90a474a88701278780"},
|
{file = "orjson-3.10.15-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b342567e5465bd99faa559507fe45e33fc76b9fb868a63f1642c6bc0735ad02a"},
|
||||||
{file = "orjson-3.10.14-cp312-cp312-win32.whl", hash = "sha256:d5075c54edf1d6ad81d4c6523ce54a748ba1208b542e54b97d8a882ecd810fd1"},
|
{file = "orjson-3.10.15-cp312-cp312-win32.whl", hash = "sha256:0a4f27ea5617828e6b58922fdbec67b0aa4bb844e2d363b9244c47fa2180e665"},
|
||||||
{file = "orjson-3.10.14-cp312-cp312-win_amd64.whl", hash = "sha256:175cafd322e458603e8ce73510a068d16b6e6f389c13f69bf16de0e843d7d406"},
|
{file = "orjson-3.10.15-cp312-cp312-win_amd64.whl", hash = "sha256:ef5b87e7aa9545ddadd2309efe6824bd3dd64ac101c15dae0f2f597911d46eaa"},
|
||||||
{file = "orjson-3.10.14-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:0905ca08a10f7e0e0c97d11359609300eb1437490a7f32bbaa349de757e2e0c7"},
|
{file = "orjson-3.10.15-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:bae0e6ec2b7ba6895198cd981b7cca95d1487d0147c8ed751e5632ad16f031a6"},
|
||||||
{file = "orjson-3.10.14-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:92d13292249f9f2a3e418cbc307a9fbbef043c65f4bd8ba1eb620bc2aaba3d15"},
|
{file = "orjson-3.10.15-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f93ce145b2db1252dd86af37d4165b6faa83072b46e3995ecc95d4b2301b725a"},
|
||||||
{file = "orjson-3.10.14-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90937664e776ad316d64251e2fa2ad69265e4443067668e4727074fe39676414"},
|
{file = "orjson-3.10.15-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7c203f6f969210128af3acae0ef9ea6aab9782939f45f6fe02d05958fe761ef9"},
|
||||||
{file = "orjson-3.10.14-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9ed3d26c4cb4f6babaf791aa46a029265850e80ec2a566581f5c2ee1a14df4f1"},
|
{file = "orjson-3.10.15-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8918719572d662e18b8af66aef699d8c21072e54b6c82a3f8f6404c1f5ccd5e0"},
|
||||||
{file = "orjson-3.10.14-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:56ee546c2bbe9599aba78169f99d1dc33301853e897dbaf642d654248280dc6e"},
|
{file = "orjson-3.10.15-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f71eae9651465dff70aa80db92586ad5b92df46a9373ee55252109bb6b703307"},
|
||||||
{file = "orjson-3.10.14-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:901e826cb2f1bdc1fcef3ef59adf0c451e8f7c0b5deb26c1a933fb66fb505eae"},
|
{file = "orjson-3.10.15-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e117eb299a35f2634e25ed120c37c641398826c2f5a3d3cc39f5993b96171b9e"},
|
||||||
{file = "orjson-3.10.14-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:26336c0d4b2d44636e1e1e6ed1002f03c6aae4a8a9329561c8883f135e9ff010"},
|
{file = "orjson-3.10.15-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:13242f12d295e83c2955756a574ddd6741c81e5b99f2bef8ed8d53e47a01e4b7"},
|
||||||
{file = "orjson-3.10.14-cp313-cp313-win32.whl", hash = "sha256:e2bc525e335a8545c4e48f84dd0328bc46158c9aaeb8a1c2276546e94540ea3d"},
|
{file = "orjson-3.10.15-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7946922ada8f3e0b7b958cc3eb22cfcf6c0df83d1fe5521b4a100103e3fa84c8"},
|
||||||
{file = "orjson-3.10.14-cp313-cp313-win_amd64.whl", hash = "sha256:eca04dfd792cedad53dc9a917da1a522486255360cb4e77619343a20d9f35364"},
|
{file = "orjson-3.10.15-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:b7155eb1623347f0f22c38c9abdd738b287e39b9982e1da227503387b81b34ca"},
|
||||||
{file = "orjson-3.10.14-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9a0fba3b8a587a54c18585f077dcab6dd251c170d85cfa4d063d5746cd595a0f"},
|
{file = "orjson-3.10.15-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:208beedfa807c922da4e81061dafa9c8489c6328934ca2a562efa707e049e561"},
|
||||||
{file = "orjson-3.10.14-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:175abf3d20e737fec47261d278f95031736a49d7832a09ab684026528c4d96db"},
|
{file = "orjson-3.10.15-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eca81f83b1b8c07449e1d6ff7074e82e3fd6777e588f1a6632127f286a968825"},
|
||||||
{file = "orjson-3.10.14-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:29ca1a93e035d570e8b791b6c0feddd403c6a5388bfe870bf2aa6bba1b9d9b8e"},
|
{file = "orjson-3.10.15-cp313-cp313-win32.whl", hash = "sha256:c03cd6eea1bd3b949d0d007c8d57049aa2b39bd49f58b4b2af571a5d3833d890"},
|
||||||
{file = "orjson-3.10.14-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f77202c80e8ab5a1d1e9faf642343bee5aaf332061e1ada4e9147dbd9eb00c46"},
|
{file = "orjson-3.10.15-cp313-cp313-win_amd64.whl", hash = "sha256:fd56a26a04f6ba5fb2045b0acc487a63162a958ed837648c5781e1fe3316cfbf"},
|
||||||
{file = "orjson-3.10.14-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6e2ec73b7099b6a29b40a62e08a23b936423bd35529f8f55c42e27acccde7954"},
|
{file = "orjson-3.10.15-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:5e8afd6200e12771467a1a44e5ad780614b86abb4b11862ec54861a82d677746"},
|
||||||
{file = "orjson-3.10.14-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2d1679df9f9cd9504f8dff24555c1eaabba8aad7f5914f28dab99e3c2552c9d"},
|
{file = "orjson-3.10.15-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da9a18c500f19273e9e104cca8c1f0b40a6470bcccfc33afcc088045d0bf5ea6"},
|
||||||
{file = "orjson-3.10.14-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:691ab9a13834310a263664313e4f747ceb93662d14a8bdf20eb97d27ed488f16"},
|
{file = "orjson-3.10.15-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb00b7bfbdf5d34a13180e4805d76b4567025da19a197645ca746fc2fb536586"},
|
||||||
{file = "orjson-3.10.14-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:b11ed82054fce82fb74cea33247d825d05ad6a4015ecfc02af5fbce442fbf361"},
|
{file = "orjson-3.10.15-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:33aedc3d903378e257047fee506f11e0833146ca3e57a1a1fb0ddb789876c1e1"},
|
||||||
{file = "orjson-3.10.14-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:e70a1d62b8288677d48f3bea66c21586a5f999c64ecd3878edb7393e8d1b548d"},
|
{file = "orjson-3.10.15-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd0099ae6aed5eb1fc84c9eb72b95505a3df4267e6962eb93cdd5af03be71c98"},
|
||||||
{file = "orjson-3.10.14-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:16642f10c1ca5611251bd835de9914a4b03095e28a34c8ba6a5500b5074338bd"},
|
{file = "orjson-3.10.15-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c864a80a2d467d7786274fce0e4f93ef2a7ca4ff31f7fc5634225aaa4e9e98c"},
|
||||||
{file = "orjson-3.10.14-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:3871bad546aa66c155e3f36f99c459780c2a392d502a64e23fb96d9abf338511"},
|
{file = "orjson-3.10.15-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c25774c9e88a3e0013d7d1a6c8056926b607a61edd423b50eb5c88fd7f2823ae"},
|
||||||
{file = "orjson-3.10.14-cp38-cp38-win32.whl", hash = "sha256:0293a88815e9bb5c90af4045f81ed364d982f955d12052d989d844d6c4e50945"},
|
{file = "orjson-3.10.15-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:e78c211d0074e783d824ce7bb85bf459f93a233eb67a5b5003498232ddfb0e8a"},
|
||||||
{file = "orjson-3.10.14-cp38-cp38-win_amd64.whl", hash = "sha256:6169d3868b190d6b21adc8e61f64e3db30f50559dfbdef34a1cd6c738d409dfc"},
|
{file = "orjson-3.10.15-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:43e17289ffdbbac8f39243916c893d2ae41a2ea1a9cbb060a56a4d75286351ae"},
|
||||||
{file = "orjson-3.10.14-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:06d4ec218b1ec1467d8d64da4e123b4794c781b536203c309ca0f52819a16c03"},
|
{file = "orjson-3.10.15-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:781d54657063f361e89714293c095f506c533582ee40a426cb6489c48a637b81"},
|
||||||
{file = "orjson-3.10.14-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:962c2ec0dcaf22b76dee9831fdf0c4a33d4bf9a257a2bc5d4adc00d5c8ad9034"},
|
{file = "orjson-3.10.15-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:6875210307d36c94873f553786a808af2788e362bd0cf4c8e66d976791e7b528"},
|
||||||
{file = "orjson-3.10.14-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:21d3be4132f71ef1360385770474f29ea1538a242eef72ac4934fe142800e37f"},
|
{file = "orjson-3.10.15-cp38-cp38-win32.whl", hash = "sha256:305b38b2b8f8083cc3d618927d7f424349afce5975b316d33075ef0f73576b60"},
|
||||||
{file = "orjson-3.10.14-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c28ed60597c149a9e3f5ad6dd9cebaee6fb2f0e3f2d159a4a2b9b862d4748860"},
|
{file = "orjson-3.10.15-cp38-cp38-win_amd64.whl", hash = "sha256:5dd9ef1639878cc3efffed349543cbf9372bdbd79f478615a1c633fe4e4180d1"},
|
||||||
{file = "orjson-3.10.14-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7e947f70167fe18469f2023644e91ab3d24f9aed69a5e1c78e2c81b9cea553fb"},
|
{file = "orjson-3.10.15-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:ffe19f3e8d68111e8644d4f4e267a069ca427926855582ff01fc012496d19969"},
|
||||||
{file = "orjson-3.10.14-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64410696c97a35af2432dea7bdc4ce32416458159430ef1b4beb79fd30093ad6"},
|
{file = "orjson-3.10.15-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d433bf32a363823863a96561a555227c18a522a8217a6f9400f00ddc70139ae2"},
|
||||||
{file = "orjson-3.10.14-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8050a5d81c022561ee29cd2739de5b4445f3c72f39423fde80a63299c1892c52"},
|
{file = "orjson-3.10.15-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:da03392674f59a95d03fa5fb9fe3a160b0511ad84b7a3914699ea5a1b3a38da2"},
|
||||||
{file = "orjson-3.10.14-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:b49a28e30d3eca86db3fe6f9b7f4152fcacbb4a467953cd1b42b94b479b77956"},
|
{file = "orjson-3.10.15-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3a63bb41559b05360ded9132032239e47983a39b151af1201f07ec9370715c82"},
|
||||||
{file = "orjson-3.10.14-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:ca041ad20291a65d853a9523744eebc3f5a4b2f7634e99f8fe88320695ddf766"},
|
{file = "orjson-3.10.15-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3766ac4702f8f795ff3fa067968e806b4344af257011858cc3d6d8721588b53f"},
|
||||||
{file = "orjson-3.10.14-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:d313a2998b74bb26e9e371851a173a9b9474764916f1fc7971095699b3c6e964"},
|
{file = "orjson-3.10.15-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a1c73dcc8fadbd7c55802d9aa093b36878d34a3b3222c41052ce6b0fc65f8e8"},
|
||||||
{file = "orjson-3.10.14-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7796692136a67b3e301ef9052bde6fe8e7bd5200da766811a3a608ffa62aaff0"},
|
{file = "orjson-3.10.15-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b299383825eafe642cbab34be762ccff9fd3408d72726a6b2a4506d410a71ab3"},
|
||||||
{file = "orjson-3.10.14-cp39-cp39-win32.whl", hash = "sha256:eee4bc767f348fba485ed9dc576ca58b0a9eac237f0e160f7a59bce628ed06b3"},
|
{file = "orjson-3.10.15-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:abc7abecdbf67a173ef1316036ebbf54ce400ef2300b4e26a7b843bd446c2480"},
|
||||||
{file = "orjson-3.10.14-cp39-cp39-win_amd64.whl", hash = "sha256:96a1c0ee30fb113b3ae3c748fd75ca74a157ff4c58476c47db4d61518962a011"},
|
{file = "orjson-3.10.15-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:3614ea508d522a621384c1d6639016a5a2e4f027f3e4a1c93a51867615d28829"},
|
||||||
{file = "orjson-3.10.14.tar.gz", hash = "sha256:cf31f6f071a6b8e7aa1ead1fa27b935b48d00fbfa6a28ce856cfff2d5dd68eed"},
|
{file = "orjson-3.10.15-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:295c70f9dc154307777ba30fe29ff15c1bcc9dfc5c48632f37d20a607e9ba85a"},
|
||||||
|
{file = "orjson-3.10.15-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:63309e3ff924c62404923c80b9e2048c1f74ba4b615e7584584389ada50ed428"},
|
||||||
|
{file = "orjson-3.10.15-cp39-cp39-win32.whl", hash = "sha256:a2f708c62d026fb5340788ba94a55c23df4e1869fec74be455e0b2f5363b8507"},
|
||||||
|
{file = "orjson-3.10.15-cp39-cp39-win_amd64.whl", hash = "sha256:efcf6c735c3d22ef60c4aa27a5238f1a477df85e9b15f2142f9d669beb2d13fd"},
|
||||||
|
{file = "orjson-3.10.15.tar.gz", hash = "sha256:05ca7fe452a2e9d8d9d706a2984c95b9c2ebc5db417ce0b7a49b91d50642a23e"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2498,13 +2496,13 @@ files = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pydantic"
|
name = "pydantic"
|
||||||
version = "2.10.5"
|
version = "2.10.6"
|
||||||
description = "Data validation using Python type hints"
|
description = "Data validation using Python type hints"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "pydantic-2.10.5-py3-none-any.whl", hash = "sha256:4dd4e322dbe55472cb7ca7e73f4b63574eecccf2835ffa2af9021ce113c83c53"},
|
{file = "pydantic-2.10.6-py3-none-any.whl", hash = "sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584"},
|
||||||
{file = "pydantic-2.10.5.tar.gz", hash = "sha256:278b38dbbaec562011d659ee05f63346951b3a248a6f3642e1bc68894ea2b4ff"},
|
{file = "pydantic-2.10.6.tar.gz", hash = "sha256:ca5daa827cce33de7a42be142548b0096bf05a7e7b365aebfa5f8eeec7128236"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@@ -2712,13 +2710,13 @@ dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pytest-asyncio"
|
name = "pytest-asyncio"
|
||||||
version = "0.25.2"
|
version = "0.25.3"
|
||||||
description = "Pytest support for asyncio"
|
description = "Pytest support for asyncio"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.9"
|
python-versions = ">=3.9"
|
||||||
files = [
|
files = [
|
||||||
{file = "pytest_asyncio-0.25.2-py3-none-any.whl", hash = "sha256:0d0bb693f7b99da304a0634afc0a4b19e49d5e0de2d670f38dc4bfa5727c5075"},
|
{file = "pytest_asyncio-0.25.3-py3-none-any.whl", hash = "sha256:9e89518e0f9bd08928f97a3482fdc4e244df17529460bc038291ccaf8f85c7c3"},
|
||||||
{file = "pytest_asyncio-0.25.2.tar.gz", hash = "sha256:3f8ef9a98f45948ea91a0ed3dc4268b5326c0e7bce73892acc654df4262ad45f"},
|
{file = "pytest_asyncio-0.25.3.tar.gz", hash = "sha256:fc1da2cf9f125ada7e710b4ddad05518d4cee187ae9412e9ac9271003497f07a"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@@ -3049,29 +3047,29 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruff"
|
name = "ruff"
|
||||||
version = "0.9.2"
|
version = "0.9.6"
|
||||||
description = "An extremely fast Python linter and code formatter, written in Rust."
|
description = "An extremely fast Python linter and code formatter, written in Rust."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.7"
|
||||||
files = [
|
files = [
|
||||||
{file = "ruff-0.9.2-py3-none-linux_armv6l.whl", hash = "sha256:80605a039ba1454d002b32139e4970becf84b5fee3a3c3bf1c2af6f61a784347"},
|
{file = "ruff-0.9.6-py3-none-linux_armv6l.whl", hash = "sha256:2f218f356dd2d995839f1941322ff021c72a492c470f0b26a34f844c29cdf5ba"},
|
||||||
{file = "ruff-0.9.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b9aab82bb20afd5f596527045c01e6ae25a718ff1784cb92947bff1f83068b00"},
|
{file = "ruff-0.9.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b908ff4df65dad7b251c9968a2e4560836d8f5487c2f0cc238321ed951ea0504"},
|
||||||
{file = "ruff-0.9.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:fbd337bac1cfa96be615f6efcd4bc4d077edbc127ef30e2b8ba2a27e18c054d4"},
|
{file = "ruff-0.9.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b109c0ad2ececf42e75fa99dc4043ff72a357436bb171900714a9ea581ddef83"},
|
||||||
{file = "ruff-0.9.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82b35259b0cbf8daa22a498018e300b9bb0174c2bbb7bcba593935158a78054d"},
|
{file = "ruff-0.9.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1de4367cca3dac99bcbd15c161404e849bb0bfd543664db39232648dc00112dc"},
|
||||||
{file = "ruff-0.9.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8b6a9701d1e371bf41dca22015c3f89769da7576884d2add7317ec1ec8cb9c3c"},
|
{file = "ruff-0.9.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac3ee4d7c2c92ddfdaedf0bf31b2b176fa7aa8950efc454628d477394d35638b"},
|
||||||
{file = "ruff-0.9.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9cc53e68b3c5ae41e8faf83a3b89f4a5d7b2cb666dff4b366bb86ed2a85b481f"},
|
{file = "ruff-0.9.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5dc1edd1775270e6aa2386119aea692039781429f0be1e0949ea5884e011aa8e"},
|
||||||
{file = "ruff-0.9.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:8efd9da7a1ee314b910da155ca7e8953094a7c10d0c0a39bfde3fcfd2a015684"},
|
{file = "ruff-0.9.6-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:4a091729086dffa4bd070aa5dab7e39cc6b9d62eb2bef8f3d91172d30d599666"},
|
||||||
{file = "ruff-0.9.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3292c5a22ea9a5f9a185e2d131dc7f98f8534a32fb6d2ee7b9944569239c648d"},
|
{file = "ruff-0.9.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d1bbc6808bf7b15796cef0815e1dfb796fbd383e7dbd4334709642649625e7c5"},
|
||||||
{file = "ruff-0.9.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a605fdcf6e8b2d39f9436d343d1f0ff70c365a1e681546de0104bef81ce88df"},
|
{file = "ruff-0.9.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:589d1d9f25b5754ff230dce914a174a7c951a85a4e9270613a2b74231fdac2f5"},
|
||||||
{file = "ruff-0.9.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c547f7f256aa366834829a08375c297fa63386cbe5f1459efaf174086b564247"},
|
{file = "ruff-0.9.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc61dd5131742e21103fbbdcad683a8813be0e3c204472d520d9a5021ca8b217"},
|
||||||
{file = "ruff-0.9.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:d18bba3d3353ed916e882521bc3e0af403949dbada344c20c16ea78f47af965e"},
|
{file = "ruff-0.9.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:5e2d9126161d0357e5c8f30b0bd6168d2c3872372f14481136d13de9937f79b6"},
|
||||||
{file = "ruff-0.9.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:b338edc4610142355ccf6b87bd356729b62bf1bc152a2fad5b0c7dc04af77bfe"},
|
{file = "ruff-0.9.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:68660eab1a8e65babb5229a1f97b46e3120923757a68b5413d8561f8a85d4897"},
|
||||||
{file = "ruff-0.9.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:492a5e44ad9b22a0ea98cf72e40305cbdaf27fac0d927f8bc9e1df316dcc96eb"},
|
{file = "ruff-0.9.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c4cae6c4cc7b9b4017c71114115db0445b00a16de3bcde0946273e8392856f08"},
|
||||||
{file = "ruff-0.9.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:af1e9e9fe7b1f767264d26b1075ac4ad831c7db976911fa362d09b2d0356426a"},
|
{file = "ruff-0.9.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:19f505b643228b417c1111a2a536424ddde0db4ef9023b9e04a46ed8a1cb4656"},
|
||||||
{file = "ruff-0.9.2-py3-none-win32.whl", hash = "sha256:71cbe22e178c5da20e1514e1e01029c73dc09288a8028a5d3446e6bba87a5145"},
|
{file = "ruff-0.9.6-py3-none-win32.whl", hash = "sha256:194d8402bceef1b31164909540a597e0d913c0e4952015a5b40e28c146121b5d"},
|
||||||
{file = "ruff-0.9.2-py3-none-win_amd64.whl", hash = "sha256:c5e1d6abc798419cf46eed03f54f2e0c3adb1ad4b801119dedf23fcaf69b55b5"},
|
{file = "ruff-0.9.6-py3-none-win_amd64.whl", hash = "sha256:03482d5c09d90d4ee3f40d97578423698ad895c87314c4de39ed2af945633caa"},
|
||||||
{file = "ruff-0.9.2-py3-none-win_arm64.whl", hash = "sha256:a1b63fa24149918f8b37cef2ee6fff81f24f0d74b6f0bdc37bc3e1f2143e41c6"},
|
{file = "ruff-0.9.6-py3-none-win_arm64.whl", hash = "sha256:0e2bb706a2be7ddfea4a4af918562fdc1bcb16df255e5fa595bbd800ce322a5a"},
|
||||||
{file = "ruff-0.9.2.tar.gz", hash = "sha256:b5eceb334d55fae5f316f783437392642ae18e16dcf4f1858d55d3c2a0f8f5d0"},
|
{file = "ruff-0.9.6.tar.gz", hash = "sha256:81761592f72b620ec8fa1068a6fd00e98a5ebee342a3642efd84454f3031dca9"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "machine-learning"
|
name = "machine-learning"
|
||||||
version = "1.125.6"
|
version = "1.126.1"
|
||||||
description = ""
|
description = ""
|
||||||
authors = ["Hau Tran <alex.tran1502@gmail.com>"]
|
authors = ["Hau Tran <alex.tran1502@gmail.com>"]
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
echo "Initializing Immich ML $IMMICH_SOURCE_REF"
|
||||||
|
|
||||||
lib_path="/usr/lib/$(arch)-linux-gnu/libmimalloc.so.2"
|
lib_path="/usr/lib/$(arch)-linux-gnu/libmimalloc.so.2"
|
||||||
# mimalloc seems to increase memory usage dramatically with openvino, need to investigate
|
# mimalloc seems to increase memory usage dramatically with openvino, need to investigate
|
||||||
if ! [ "$DEVICE" = "openvino" ]; then
|
if ! [ "$DEVICE" = "openvino" ]; then
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ custom_lint:
|
|||||||
- test/**.dart
|
- test/**.dart
|
||||||
# refactor the remaining providers
|
# refactor the remaining providers
|
||||||
- lib/providers/{archive,asset,authentication,db,favorite,partner,trash,user}.provider.dart
|
- lib/providers/{archive,asset,authentication,db,favorite,partner,trash,user}.provider.dart
|
||||||
- lib/providers/{album/album,album/shared_album,asset_viewer/asset_stack,asset_viewer/render_list,backup/backup,search/all_motion_photos,search/recently_added_asset}.provider.dart
|
- lib/providers/{asset_viewer/render_list,backup/backup,search/all_motion_photos,search/recently_added_asset}.provider.dart
|
||||||
|
|
||||||
- import_rule_openapi:
|
- import_rule_openapi:
|
||||||
message: openapi must only be used through ApiRepositories
|
message: openapi must only be used through ApiRepositories
|
||||||
@@ -110,51 +110,81 @@ custom_lint:
|
|||||||
- test/**.dart
|
- test/**.dart
|
||||||
|
|
||||||
dart_code_metrics:
|
dart_code_metrics:
|
||||||
metrics:
|
extends:
|
||||||
cyclomatic-complexity: 20
|
- recommended
|
||||||
number-of-parameters: 4
|
|
||||||
maximum-nesting-level: 5
|
|
||||||
rules:
|
rules:
|
||||||
# Common
|
# Common
|
||||||
- avoid-accessing-collections-by-constant-index
|
- arguments-ordering:
|
||||||
|
last:
|
||||||
|
- child
|
||||||
|
- children
|
||||||
- avoid-accessing-other-classes-private-members
|
- avoid-accessing-other-classes-private-members
|
||||||
- avoid-cascade-after-if-null
|
- avoid-assigning-to-static-field
|
||||||
|
- avoid-assignments-as-conditions
|
||||||
|
- avoid-async-call-in-sync-function
|
||||||
- avoid-collapsible-if
|
- avoid-collapsible-if
|
||||||
- avoid-collection-methods-with-unrelated-types
|
- avoid-collection-equality-checks
|
||||||
- avoid-double-slash-imports
|
- avoid-complex-loop-conditions
|
||||||
- avoid-duplicate-cascades
|
- avoid-declaring-call-method
|
||||||
- avoid-duplicate-patterns
|
- avoid-extensions-on-records
|
||||||
- avoid-generics-shadowing
|
- avoid-function-type-in-records
|
||||||
|
- avoid-future-ignore
|
||||||
- avoid-global-state
|
- avoid-global-state
|
||||||
|
- avoid-inverted-boolean-checks
|
||||||
|
- avoid-late-final-reassignment
|
||||||
|
- avoid-local-functions
|
||||||
|
- avoid-negated-conditions
|
||||||
|
- avoid-nested-streams-and-futures
|
||||||
|
- avoid-referencing-subclasses
|
||||||
|
- avoid-unnecessary-continue
|
||||||
|
- avoid-unnecessary-nullable-return-type: false
|
||||||
|
- binary-expression-operand-order
|
||||||
|
- move-variable-outside-iteration
|
||||||
|
- pattern-fields-ordering
|
||||||
|
- prefer-abstract-final-static-class
|
||||||
|
- prefer-commenting-future-delayed
|
||||||
|
- prefer-early-return
|
||||||
|
- prefer-first
|
||||||
|
- prefer-immediate-return
|
||||||
|
- prefer-last
|
||||||
|
- prefer-simpler-boolean-expressions
|
||||||
|
- prefer-switch-expression
|
||||||
|
- prefer-type-over-var
|
||||||
|
- use-existing-destructuring
|
||||||
|
- use-existing-variable
|
||||||
# Flutter
|
# Flutter
|
||||||
- add-copy-with:
|
|
||||||
file-name-pattern: '.model.dart'
|
|
||||||
- always-remove-listener
|
|
||||||
- avoid-border-all
|
- avoid-border-all
|
||||||
- avoid-empty-setstate
|
- avoid-complex-arithmetic-expressions
|
||||||
- avoid-expanded-as-spacer
|
- avoid-expanded-as-spacer
|
||||||
- avoid-incomplete-copy-with
|
- avoid-if-with-many-branches
|
||||||
- avoid-inherited-widget-in-initstate
|
- avoid-inherited-widget-in-initstate
|
||||||
- avoid-late-context
|
- avoid-late-context
|
||||||
- avoid-recursive-widget-calls
|
|
||||||
- avoid-returning-widgets
|
- avoid-returning-widgets
|
||||||
- avoid-shrink-wrap-in-lists
|
- avoid-shrink-wrap-in-lists
|
||||||
- avoid-single-child-column-or-row
|
- avoid-single-child-column-or-row
|
||||||
- avoid-state-constructors
|
|
||||||
- avoid-stateless-widget-initialized-fields
|
- avoid-stateless-widget-initialized-fields
|
||||||
- avoid-unnecessary-overrides-in-state
|
|
||||||
- avoid-unnecessary-stateful-widgets
|
|
||||||
- avoid-wrapping-in-padding
|
- avoid-wrapping-in-padding
|
||||||
- dispose-fields
|
- prefer-align-over-container
|
||||||
- prefer-const-border-radius
|
- prefer-const-border-radius
|
||||||
|
- prefer-correct-callback-field-name: false
|
||||||
- prefer-correct-edge-insets-constructor
|
- prefer-correct-edge-insets-constructor
|
||||||
- prefer-dedicated-media-query-methods
|
|
||||||
- prefer-define-hero-tag
|
- prefer-define-hero-tag
|
||||||
- prefer-extracting-callbacks
|
- prefer-extracting-callbacks
|
||||||
- prefer-single-widget-per-file:
|
- prefer-for-loop-in-children
|
||||||
ignore-private-widgets: true
|
- prefer-match-file-name: false
|
||||||
- prefer-sliver-prefix
|
- prefer-sliver-prefix
|
||||||
|
- prefer-spacing
|
||||||
- prefer-text-rich
|
- prefer-text-rich
|
||||||
|
- prefer-transform-over-container
|
||||||
- prefer-using-list-view
|
- prefer-using-list-view
|
||||||
- proper-super-calls
|
- prefer-widget-private-members:
|
||||||
- use-setstate-synchronously
|
ignore-static: true
|
||||||
|
- use-closest-build-context
|
||||||
|
# riverpod
|
||||||
|
- avoid-calling-notifier-members-inside-build
|
||||||
|
- avoid-notifier-constructors
|
||||||
|
- avoid-ref-read-inside-build
|
||||||
|
- avoid-ref-watch-outside-build
|
||||||
|
- avoid-unnecessary-consumer-widgets
|
||||||
|
- dispose-provided-instances
|
||||||
|
- use-ref-read-synchronously
|
||||||
|
|||||||
@@ -35,8 +35,8 @@ platform :android do
|
|||||||
task: 'bundle',
|
task: 'bundle',
|
||||||
build_type: 'Release',
|
build_type: 'Release',
|
||||||
properties: {
|
properties: {
|
||||||
"android.injected.version.code" => 182,
|
"android.injected.version.code" => 184,
|
||||||
"android.injected.version.name" => "1.125.6",
|
"android.injected.version.name" => "1.126.1",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
upload_to_play_store(skip_upload_apk: true, skip_upload_images: true, skip_upload_screenshots: true, aab: '../build/app/outputs/bundle/release/app-release.aab')
|
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')
|
||||||
|
|||||||
@@ -1,4 +1,10 @@
|
|||||||
{
|
{
|
||||||
|
"search_filter_contextual": "Search by context",
|
||||||
|
"search_filter_filename": "Search by file name",
|
||||||
|
"search_filter_description": "Search by description",
|
||||||
|
"search_no_result": "No results found, try a different search term or combination",
|
||||||
|
"description_search": "Hiking day in Sapa",
|
||||||
|
"search_no_more_result": "No more results",
|
||||||
"action_common_back": "Back",
|
"action_common_back": "Back",
|
||||||
"action_common_cancel": "Cancel",
|
"action_common_cancel": "Cancel",
|
||||||
"action_common_clear": "Clear",
|
"action_common_clear": "Clear",
|
||||||
@@ -282,9 +288,9 @@
|
|||||||
"header_settings_field_validator_msg": "Value cannot be empty",
|
"header_settings_field_validator_msg": "Value cannot be empty",
|
||||||
"header_settings_header_name_input": "Header name",
|
"header_settings_header_name_input": "Header name",
|
||||||
"header_settings_header_value_input": "Header value",
|
"header_settings_header_value_input": "Header value",
|
||||||
"header_settings_page_title": "Proxy Headers (EXPERIMENTAL)",
|
"header_settings_page_title": "Proxy Headers",
|
||||||
"headers_settings_tile_subtitle": "Define proxy headers the app should send with each network request",
|
"headers_settings_tile_subtitle": "Define proxy headers the app should send with each network request",
|
||||||
"headers_settings_tile_title": "Custom proxy headers",
|
"headers_settings_tile_title": "Custom proxy headers (EXPERIMENTAL)",
|
||||||
"home_page_add_to_album_conflicts": "Added {added} assets to album {album}. {failed} assets are already in the album.",
|
"home_page_add_to_album_conflicts": "Added {added} assets to album {album}. {failed} assets are already in the album.",
|
||||||
"home_page_add_to_album_err_local": "Can not add local assets to albums yet, skipping",
|
"home_page_add_to_album_err_local": "Can not add local assets to albums yet, skipping",
|
||||||
"home_page_add_to_album_success": "Added {added} assets to album {album}.",
|
"home_page_add_to_album_success": "Added {added} assets to album {album}.",
|
||||||
@@ -476,6 +482,7 @@
|
|||||||
"search_filter_media_type_video": "Video",
|
"search_filter_media_type_video": "Video",
|
||||||
"search_filter_people": "People",
|
"search_filter_people": "People",
|
||||||
"search_filter_people_title": "Select people",
|
"search_filter_people_title": "Select people",
|
||||||
|
"search_filter_people_hint": "Filter people",
|
||||||
"search_page_categories": "Categories",
|
"search_page_categories": "Categories",
|
||||||
"search_page_favorites": "Favorites",
|
"search_page_favorites": "Favorites",
|
||||||
"search_page_motion_photos": "Motion Photos",
|
"search_page_motion_photos": "Motion Photos",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'package:analyzer/error/listener.dart';
|
|
||||||
import 'package:analyzer/error/error.dart' show ErrorSeverity;
|
import 'package:analyzer/error/error.dart' show ErrorSeverity;
|
||||||
|
import 'package:analyzer/error/listener.dart';
|
||||||
import 'package:custom_lint_builder/custom_lint_builder.dart';
|
import 'package:custom_lint_builder/custom_lint_builder.dart';
|
||||||
// ignore: depend_on_referenced_packages
|
// ignore: depend_on_referenced_packages
|
||||||
import 'package:glob/glob.dart';
|
import 'package:glob/glob.dart';
|
||||||
@@ -65,7 +65,8 @@ class ImportRule extends DartLintRule {
|
|||||||
) {
|
) {
|
||||||
if (_rootOffset == -1) {
|
if (_rootOffset == -1) {
|
||||||
const project = "/immich/mobile/";
|
const project = "/immich/mobile/";
|
||||||
_rootOffset = resolver.path.indexOf(project) + project.length;
|
_rootOffset =
|
||||||
|
resolver.path.toLowerCase().indexOf(project) + project.length;
|
||||||
}
|
}
|
||||||
final path = resolver.path.substring(_rootOffset);
|
final path = resolver.path.substring(_rootOffset);
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ environment:
|
|||||||
sdk: '>=3.0.0 <4.0.0'
|
sdk: '>=3.0.0 <4.0.0'
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
analyzer: ^7.0.0
|
analyzer: ^6.0.0
|
||||||
analyzer_plugin: ^0.11.3
|
analyzer_plugin: ^0.11.3
|
||||||
custom_lint_builder: ^0.6.4
|
custom_lint_builder: ^0.6.4
|
||||||
glob: ^2.1.2
|
glob: ^2.1.2
|
||||||
|
|||||||
@@ -541,7 +541,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 = 190;
|
CURRENT_PROJECT_VERSION = 194;
|
||||||
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;
|
||||||
@@ -685,7 +685,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 = 190;
|
CURRENT_PROJECT_VERSION = 194;
|
||||||
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;
|
||||||
@@ -715,7 +715,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 = 190;
|
CURRENT_PROJECT_VERSION = 194;
|
||||||
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;
|
||||||
@@ -748,7 +748,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 = 190;
|
CURRENT_PROJECT_VERSION = 194;
|
||||||
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;
|
||||||
@@ -791,7 +791,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 = 190;
|
CURRENT_PROJECT_VERSION = 194;
|
||||||
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;
|
||||||
@@ -831,7 +831,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 = 190;
|
CURRENT_PROJECT_VERSION = 194;
|
||||||
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.125.2</string>
|
<string>1.126.1</string>
|
||||||
<key>CFBundleSignature</key>
|
<key>CFBundleSignature</key>
|
||||||
<string>????</string>
|
<string>????</string>
|
||||||
<key>CFBundleURLTypes</key>
|
<key>CFBundleURLTypes</key>
|
||||||
@@ -93,7 +93,7 @@
|
|||||||
</dict>
|
</dict>
|
||||||
</array>
|
</array>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>190</string>
|
<string>194</string>
|
||||||
<key>FLTEnableImpeller</key>
|
<key>FLTEnableImpeller</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>ITSAppUsesNonExemptEncryption</key>
|
<key>ITSAppUsesNonExemptEncryption</key>
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ platform :ios do
|
|||||||
desc "iOS Release"
|
desc "iOS Release"
|
||||||
lane :release do
|
lane :release do
|
||||||
increment_version_number(
|
increment_version_number(
|
||||||
version_number: "1.125.6"
|
version_number: "1.126.1"
|
||||||
)
|
)
|
||||||
increment_build_number(
|
increment_build_number(
|
||||||
build_number: latest_testflight_build_number + 1,
|
build_number: latest_testflight_build_number + 1,
|
||||||
|
|||||||
@@ -2,3 +2,9 @@ enum SortOrder {
|
|||||||
asc,
|
asc,
|
||||||
desc,
|
desc,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum TextSearchType {
|
||||||
|
context,
|
||||||
|
filename,
|
||||||
|
description,
|
||||||
|
}
|
||||||
|
|||||||
@@ -545,19 +545,13 @@ enum AssetType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extension AssetTypeEnumHelper on AssetTypeEnum {
|
extension AssetTypeEnumHelper on AssetTypeEnum {
|
||||||
AssetType toAssetType() {
|
AssetType toAssetType() => switch (this) {
|
||||||
switch (this) {
|
AssetTypeEnum.IMAGE => AssetType.image,
|
||||||
case AssetTypeEnum.IMAGE:
|
AssetTypeEnum.VIDEO => AssetType.video,
|
||||||
return AssetType.image;
|
AssetTypeEnum.AUDIO => AssetType.audio,
|
||||||
case AssetTypeEnum.VIDEO:
|
AssetTypeEnum.OTHER => AssetType.other,
|
||||||
return AssetType.video;
|
_ => throw Exception(),
|
||||||
case AssetTypeEnum.AUDIO:
|
};
|
||||||
return AssetType.audio;
|
|
||||||
case AssetTypeEnum.OTHER:
|
|
||||||
return AssetType.other;
|
|
||||||
}
|
|
||||||
throw Exception();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Describes where the information of this asset came from:
|
/// Describes where the information of this asset came from:
|
||||||
|
|||||||
@@ -96,25 +96,16 @@ class StoreValue {
|
|||||||
int? intValue;
|
int? intValue;
|
||||||
String? strValue;
|
String? strValue;
|
||||||
|
|
||||||
T? _extract<T>(StoreKey<T> key) {
|
T? _extract<T>(StoreKey<T> key) => switch (key.type) {
|
||||||
switch (key.type) {
|
const (int) => intValue as T?,
|
||||||
case const (int):
|
const (bool) => intValue == null ? null : (intValue! == 1) as T,
|
||||||
return intValue as T?;
|
const (DateTime) => intValue == null
|
||||||
case const (bool):
|
|
||||||
return intValue == null ? null : (intValue! == 1) as T;
|
|
||||||
case const (DateTime):
|
|
||||||
return intValue == null
|
|
||||||
? null
|
? null
|
||||||
: DateTime.fromMicrosecondsSinceEpoch(intValue!) as T;
|
: DateTime.fromMicrosecondsSinceEpoch(intValue!) as T,
|
||||||
case const (String):
|
const (String) => strValue as T?,
|
||||||
return strValue as T?;
|
_ when key.fromDb != null => key.fromDb!.call(Store._db, intValue!),
|
||||||
default:
|
_ => throw TypeError(),
|
||||||
if (key.fromDb != null) {
|
};
|
||||||
return key.fromDb!.call(Store._db, intValue!);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw TypeError();
|
|
||||||
}
|
|
||||||
|
|
||||||
static Future<StoreValue> _of<T>(T? value, StoreKey<T> key) async {
|
static Future<StoreValue> _of<T>(T? value, StoreKey<T> key) async {
|
||||||
int? i;
|
int? i;
|
||||||
|
|||||||
@@ -149,56 +149,33 @@ enum AvatarColorEnum {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extension AvatarColorEnumHelper on UserAvatarColor {
|
extension AvatarColorEnumHelper on UserAvatarColor {
|
||||||
AvatarColorEnum toAvatarColor() {
|
AvatarColorEnum toAvatarColor() => switch (this) {
|
||||||
switch (this) {
|
UserAvatarColor.primary => AvatarColorEnum.primary,
|
||||||
case UserAvatarColor.primary:
|
UserAvatarColor.pink => AvatarColorEnum.pink,
|
||||||
return AvatarColorEnum.primary;
|
UserAvatarColor.red => AvatarColorEnum.red,
|
||||||
case UserAvatarColor.pink:
|
UserAvatarColor.yellow => AvatarColorEnum.yellow,
|
||||||
return AvatarColorEnum.pink;
|
UserAvatarColor.blue => AvatarColorEnum.blue,
|
||||||
case UserAvatarColor.red:
|
UserAvatarColor.green => AvatarColorEnum.green,
|
||||||
return AvatarColorEnum.red;
|
UserAvatarColor.purple => AvatarColorEnum.purple,
|
||||||
case UserAvatarColor.yellow:
|
UserAvatarColor.orange => AvatarColorEnum.orange,
|
||||||
return AvatarColorEnum.yellow;
|
UserAvatarColor.gray => AvatarColorEnum.gray,
|
||||||
case UserAvatarColor.blue:
|
UserAvatarColor.amber => AvatarColorEnum.amber,
|
||||||
return AvatarColorEnum.blue;
|
_ => AvatarColorEnum.primary,
|
||||||
case UserAvatarColor.green:
|
};
|
||||||
return AvatarColorEnum.green;
|
|
||||||
case UserAvatarColor.purple:
|
|
||||||
return AvatarColorEnum.purple;
|
|
||||||
case UserAvatarColor.orange:
|
|
||||||
return AvatarColorEnum.orange;
|
|
||||||
case UserAvatarColor.gray:
|
|
||||||
return AvatarColorEnum.gray;
|
|
||||||
case UserAvatarColor.amber:
|
|
||||||
return AvatarColorEnum.amber;
|
|
||||||
}
|
|
||||||
return AvatarColorEnum.primary;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension AvatarColorToColorHelper on AvatarColorEnum {
|
extension AvatarColorToColorHelper on AvatarColorEnum {
|
||||||
Color toColor([bool isDarkTheme = false]) {
|
Color toColor([bool isDarkTheme = false]) => switch (this) {
|
||||||
switch (this) {
|
AvatarColorEnum.primary =>
|
||||||
case AvatarColorEnum.primary:
|
isDarkTheme ? const Color(0xFFABCBFA) : const Color(0xFF4250AF),
|
||||||
return isDarkTheme ? const Color(0xFFABCBFA) : const Color(0xFF4250AF);
|
AvatarColorEnum.pink => const Color.fromARGB(255, 244, 114, 182),
|
||||||
case AvatarColorEnum.pink:
|
AvatarColorEnum.red => const Color.fromARGB(255, 239, 68, 68),
|
||||||
return const Color.fromARGB(255, 244, 114, 182);
|
AvatarColorEnum.yellow => const Color.fromARGB(255, 234, 179, 8),
|
||||||
case AvatarColorEnum.red:
|
AvatarColorEnum.blue => const Color.fromARGB(255, 59, 130, 246),
|
||||||
return const Color.fromARGB(255, 239, 68, 68);
|
AvatarColorEnum.green => const Color.fromARGB(255, 22, 163, 74),
|
||||||
case AvatarColorEnum.yellow:
|
AvatarColorEnum.purple => const Color.fromARGB(255, 147, 51, 234),
|
||||||
return const Color.fromARGB(255, 234, 179, 8);
|
AvatarColorEnum.orange => const Color.fromARGB(255, 234, 88, 12),
|
||||||
case AvatarColorEnum.blue:
|
AvatarColorEnum.gray => const Color.fromARGB(255, 75, 85, 99),
|
||||||
return const Color.fromARGB(255, 59, 130, 246);
|
AvatarColorEnum.amber => const Color.fromARGB(255, 217, 119, 6),
|
||||||
case AvatarColorEnum.green:
|
};
|
||||||
return const Color.fromARGB(255, 22, 163, 74);
|
|
||||||
case AvatarColorEnum.purple:
|
|
||||||
return const Color.fromARGB(255, 147, 51, 234);
|
|
||||||
case AvatarColorEnum.orange:
|
|
||||||
return const Color.fromARGB(255, 234, 88, 12);
|
|
||||||
case AvatarColorEnum.gray:
|
|
||||||
return const Color.fromARGB(255, 75, 85, 99);
|
|
||||||
case AvatarColorEnum.amber:
|
|
||||||
return const Color.fromARGB(255, 217, 119, 6);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import 'package:immich_mobile/entities/asset.entity.dart';
|
|||||||
import 'package:immich_mobile/entities/user.entity.dart';
|
import 'package:immich_mobile/entities/user.entity.dart';
|
||||||
import 'package:immich_mobile/interfaces/database.interface.dart';
|
import 'package:immich_mobile/interfaces/database.interface.dart';
|
||||||
import 'package:immich_mobile/models/albums/album_search.model.dart';
|
import 'package:immich_mobile/models/albums/album_search.model.dart';
|
||||||
|
import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart';
|
||||||
|
|
||||||
abstract interface class IAlbumRepository implements IDatabaseRepository {
|
abstract interface class IAlbumRepository implements IDatabaseRepository {
|
||||||
Future<Album> create(Album album);
|
Future<Album> create(Album album);
|
||||||
@@ -42,6 +43,16 @@ abstract interface class IAlbumRepository implements IDatabaseRepository {
|
|||||||
Future<Album> recalculateMetadata(Album album);
|
Future<Album> recalculateMetadata(Album album);
|
||||||
|
|
||||||
Future<List<Album>> search(String searchTerm, QuickFilterMode filterMode);
|
Future<List<Album>> search(String searchTerm, QuickFilterMode filterMode);
|
||||||
|
|
||||||
|
Stream<List<Album>> watchRemoteAlbums();
|
||||||
|
|
||||||
|
Stream<List<Album>> watchLocalAlbums();
|
||||||
|
|
||||||
|
Stream<Album?> watchAlbum(int id);
|
||||||
|
|
||||||
|
Stream<RenderList> getRenderListStream(Album album);
|
||||||
|
|
||||||
|
Future<void> clearTable();
|
||||||
}
|
}
|
||||||
|
|
||||||
enum AlbumSort { remoteId, localId }
|
enum AlbumSort { remoteId, localId }
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ abstract interface class IAssetRepository implements IDatabaseRepository {
|
|||||||
|
|
||||||
Future<void> deleteAllByRemoteId(List<String> ids, {AssetState? state});
|
Future<void> deleteAllByRemoteId(List<String> ids, {AssetState? state});
|
||||||
|
|
||||||
Future<void> deleteById(List<int> ids);
|
Future<void> deleteByIds(List<int> ids);
|
||||||
|
|
||||||
Future<List<Asset>> getMatches({
|
Future<List<Asset>> getMatches({
|
||||||
required List<Asset> assets,
|
required List<Asset> assets,
|
||||||
@@ -57,6 +57,12 @@ abstract interface class IAssetRepository implements IDatabaseRepository {
|
|||||||
Future<void> upsertDuplicatedAssets(Iterable<String> duplicatedAssets);
|
Future<void> upsertDuplicatedAssets(Iterable<String> duplicatedAssets);
|
||||||
|
|
||||||
Future<List<String>> getAllDuplicatedAssetIds();
|
Future<List<String>> getAllDuplicatedAssetIds();
|
||||||
|
|
||||||
|
Future<List<Asset>> getStackAssets(String stackId);
|
||||||
|
|
||||||
|
Future<void> clearTable();
|
||||||
|
|
||||||
|
Stream<Asset?> watchAsset(int id, {bool fireImmediately = false});
|
||||||
}
|
}
|
||||||
|
|
||||||
enum AssetSort { checksum, ownerIdChecksum }
|
enum AssetSort { checksum, ownerIdChecksum }
|
||||||
|
|||||||
@@ -11,4 +11,6 @@ abstract interface class IETagRepository implements IDatabaseRepository {
|
|||||||
Future<void> upsertAll(List<ETag> etags);
|
Future<void> upsertAll(List<ETag> etags);
|
||||||
|
|
||||||
Future<void> deleteByIds(List<String> ids);
|
Future<void> deleteByIds(List<String> ids);
|
||||||
|
|
||||||
|
Future<void> clearTable();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,4 +9,6 @@ abstract interface class IExifInfoRepository implements IDatabaseRepository {
|
|||||||
Future<List<ExifInfo>> updateAll(List<ExifInfo> exifInfos);
|
Future<List<ExifInfo>> updateAll(List<ExifInfo> exifInfos);
|
||||||
|
|
||||||
Future<void> delete(int id);
|
Future<void> delete(int id);
|
||||||
|
|
||||||
|
Future<void> clearTable();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// ignore_for_file: public_member_api_docs, sort_constructors_first
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
abstract interface class IPersonApiRepository {
|
abstract interface class IPersonApiRepository {
|
||||||
Future<List<Person>> getAll();
|
Future<List<Person>> getAll();
|
||||||
Future<Person> update(String id, {String? name});
|
Future<Person> update(String id, {String? name});
|
||||||
@@ -6,10 +9,10 @@ abstract interface class IPersonApiRepository {
|
|||||||
class Person {
|
class Person {
|
||||||
Person({
|
Person({
|
||||||
required this.id,
|
required this.id,
|
||||||
|
this.birthDate,
|
||||||
required this.isHidden,
|
required this.isHidden,
|
||||||
required this.name,
|
required this.name,
|
||||||
required this.thumbnailPath,
|
required this.thumbnailPath,
|
||||||
this.birthDate,
|
|
||||||
this.updatedAt,
|
this.updatedAt,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -19,4 +22,80 @@ class Person {
|
|||||||
final String name;
|
final String name;
|
||||||
final String thumbnailPath;
|
final String thumbnailPath;
|
||||||
final DateTime? updatedAt;
|
final DateTime? updatedAt;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'Person(id: $id, birthDate: $birthDate, isHidden: $isHidden, name: $name, thumbnailPath: $thumbnailPath, updatedAt: $updatedAt)';
|
||||||
|
}
|
||||||
|
|
||||||
|
Person copyWith({
|
||||||
|
String? id,
|
||||||
|
DateTime? birthDate,
|
||||||
|
bool? isHidden,
|
||||||
|
String? name,
|
||||||
|
String? thumbnailPath,
|
||||||
|
DateTime? updatedAt,
|
||||||
|
}) {
|
||||||
|
return Person(
|
||||||
|
id: id ?? this.id,
|
||||||
|
birthDate: birthDate ?? this.birthDate,
|
||||||
|
isHidden: isHidden ?? this.isHidden,
|
||||||
|
name: name ?? this.name,
|
||||||
|
thumbnailPath: thumbnailPath ?? this.thumbnailPath,
|
||||||
|
updatedAt: updatedAt ?? this.updatedAt,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toMap() {
|
||||||
|
return <String, dynamic>{
|
||||||
|
'id': id,
|
||||||
|
'birthDate': birthDate?.millisecondsSinceEpoch,
|
||||||
|
'isHidden': isHidden,
|
||||||
|
'name': name,
|
||||||
|
'thumbnailPath': thumbnailPath,
|
||||||
|
'updatedAt': updatedAt?.millisecondsSinceEpoch,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
factory Person.fromMap(Map<String, dynamic> map) {
|
||||||
|
return Person(
|
||||||
|
id: map['id'] as String,
|
||||||
|
birthDate: map['birthDate'] != null
|
||||||
|
? DateTime.fromMillisecondsSinceEpoch(map['birthDate'] as int)
|
||||||
|
: null,
|
||||||
|
isHidden: map['isHidden'] as bool,
|
||||||
|
name: map['name'] as String,
|
||||||
|
thumbnailPath: map['thumbnailPath'] as String,
|
||||||
|
updatedAt: map['updatedAt'] != null
|
||||||
|
? DateTime.fromMillisecondsSinceEpoch(map['updatedAt'] as int)
|
||||||
|
: null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String toJson() => json.encode(toMap());
|
||||||
|
|
||||||
|
factory Person.fromJson(String source) =>
|
||||||
|
Person.fromMap(json.decode(source) as Map<String, dynamic>);
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(covariant Person other) {
|
||||||
|
if (identical(this, other)) return true;
|
||||||
|
|
||||||
|
return other.id == id &&
|
||||||
|
other.birthDate == birthDate &&
|
||||||
|
other.isHidden == isHidden &&
|
||||||
|
other.name == name &&
|
||||||
|
other.thumbnailPath == thumbnailPath &&
|
||||||
|
other.updatedAt == updatedAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode {
|
||||||
|
return id.hashCode ^
|
||||||
|
birthDate.hashCode ^
|
||||||
|
isHidden.hashCode ^
|
||||||
|
name.hashCode ^
|
||||||
|
thumbnailPath.hashCode ^
|
||||||
|
updatedAt.hashCode;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ abstract interface class IUserRepository implements IDatabaseRepository {
|
|||||||
Future<void> deleteById(List<int> ids);
|
Future<void> deleteById(List<int> ids);
|
||||||
|
|
||||||
Future<User> me();
|
Future<User> me();
|
||||||
|
|
||||||
|
Future<void> clearTable();
|
||||||
}
|
}
|
||||||
|
|
||||||
enum UserSort { id }
|
enum UserSort { id }
|
||||||
|
|||||||
@@ -235,6 +235,7 @@ class SearchDisplayFilters {
|
|||||||
class SearchFilter {
|
class SearchFilter {
|
||||||
String? context;
|
String? context;
|
||||||
String? filename;
|
String? filename;
|
||||||
|
String? description;
|
||||||
Set<Person> people;
|
Set<Person> people;
|
||||||
SearchLocationFilter location;
|
SearchLocationFilter location;
|
||||||
SearchCameraFilter camera;
|
SearchCameraFilter camera;
|
||||||
@@ -247,6 +248,7 @@ class SearchFilter {
|
|||||||
SearchFilter({
|
SearchFilter({
|
||||||
this.context,
|
this.context,
|
||||||
this.filename,
|
this.filename,
|
||||||
|
this.description,
|
||||||
required this.people,
|
required this.people,
|
||||||
required this.location,
|
required this.location,
|
||||||
required this.camera,
|
required this.camera,
|
||||||
@@ -255,9 +257,28 @@ class SearchFilter {
|
|||||||
required this.mediaType,
|
required this.mediaType,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
bool get isEmpty {
|
||||||
|
return (context == null || (context != null && context!.isEmpty)) &&
|
||||||
|
(filename == null || (filename!.isEmpty)) &&
|
||||||
|
(description == null || (description!.isEmpty)) &&
|
||||||
|
people.isEmpty &&
|
||||||
|
location.country == null &&
|
||||||
|
location.state == null &&
|
||||||
|
location.city == null &&
|
||||||
|
camera.make == null &&
|
||||||
|
camera.model == null &&
|
||||||
|
date.takenBefore == null &&
|
||||||
|
date.takenAfter == null &&
|
||||||
|
display.isNotInAlbum == false &&
|
||||||
|
display.isArchive == false &&
|
||||||
|
display.isFavorite == false &&
|
||||||
|
mediaType == AssetType.other;
|
||||||
|
}
|
||||||
|
|
||||||
SearchFilter copyWith({
|
SearchFilter copyWith({
|
||||||
String? context,
|
String? context,
|
||||||
String? filename,
|
String? filename,
|
||||||
|
String? description,
|
||||||
Set<Person>? people,
|
Set<Person>? people,
|
||||||
SearchLocationFilter? location,
|
SearchLocationFilter? location,
|
||||||
SearchCameraFilter? camera,
|
SearchCameraFilter? camera,
|
||||||
@@ -268,6 +289,7 @@ class SearchFilter {
|
|||||||
return SearchFilter(
|
return SearchFilter(
|
||||||
context: context ?? this.context,
|
context: context ?? this.context,
|
||||||
filename: filename ?? this.filename,
|
filename: filename ?? this.filename,
|
||||||
|
description: description ?? this.description,
|
||||||
people: people ?? this.people,
|
people: people ?? this.people,
|
||||||
location: location ?? this.location,
|
location: location ?? this.location,
|
||||||
camera: camera ?? this.camera,
|
camera: camera ?? this.camera,
|
||||||
@@ -279,7 +301,7 @@ class SearchFilter {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'SearchFilter(context: $context, filename: $filename, people: $people, location: $location, camera: $camera, date: $date, display: $display, mediaType: $mediaType)';
|
return 'SearchFilter(context: $context, filename: $filename, description: $description, people: $people, location: $location, camera: $camera, date: $date, display: $display, mediaType: $mediaType)';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -288,6 +310,7 @@ class SearchFilter {
|
|||||||
|
|
||||||
return other.context == context &&
|
return other.context == context &&
|
||||||
other.filename == filename &&
|
other.filename == filename &&
|
||||||
|
other.description == description &&
|
||||||
other.people == people &&
|
other.people == people &&
|
||||||
other.location == location &&
|
other.location == location &&
|
||||||
other.camera == camera &&
|
other.camera == camera &&
|
||||||
@@ -300,6 +323,7 @@ class SearchFilter {
|
|||||||
int get hashCode {
|
int get hashCode {
|
||||||
return context.hashCode ^
|
return context.hashCode ^
|
||||||
filename.hashCode ^
|
filename.hashCode ^
|
||||||
|
description.hashCode ^
|
||||||
people.hashCode ^
|
people.hashCode ^
|
||||||
location.hashCode ^
|
location.hashCode ^
|
||||||
camera.hashCode ^
|
camera.hashCode ^
|
||||||
|
|||||||
@@ -36,32 +36,19 @@ class AppLogPage extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget buildLeadingIcon(LogLevel level) {
|
Widget buildLeadingIcon(LogLevel level) => switch (level) {
|
||||||
switch (level) {
|
LogLevel.INFO => colorStatusIndicator(context.primaryColor),
|
||||||
case LogLevel.INFO:
|
LogLevel.SEVERE => colorStatusIndicator(Colors.redAccent),
|
||||||
return colorStatusIndicator(context.primaryColor);
|
LogLevel.WARNING => colorStatusIndicator(Colors.orangeAccent),
|
||||||
case LogLevel.SEVERE:
|
_ => colorStatusIndicator(Colors.grey),
|
||||||
return colorStatusIndicator(Colors.redAccent);
|
};
|
||||||
|
|
||||||
case LogLevel.WARNING:
|
Color getTileColor(LogLevel level) => switch (level) {
|
||||||
return colorStatusIndicator(Colors.orangeAccent);
|
LogLevel.INFO => Colors.transparent,
|
||||||
default:
|
LogLevel.SEVERE => Colors.redAccent.withOpacity(0.25),
|
||||||
return colorStatusIndicator(Colors.grey);
|
LogLevel.WARNING => Colors.orangeAccent.withOpacity(0.25),
|
||||||
}
|
_ => context.primaryColor.withOpacity(0.1),
|
||||||
}
|
};
|
||||||
|
|
||||||
getTileColor(LogLevel level) {
|
|
||||||
switch (level) {
|
|
||||||
case LogLevel.INFO:
|
|
||||||
return Colors.transparent;
|
|
||||||
case LogLevel.SEVERE:
|
|
||||||
return Colors.redAccent.withOpacity(0.25);
|
|
||||||
case LogLevel.WARNING:
|
|
||||||
return Colors.orangeAccent.withOpacity(0.25);
|
|
||||||
default:
|
|
||||||
return context.primaryColor.withOpacity(0.1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
|
|||||||
@@ -74,26 +74,16 @@ class DownloadTaskTile extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final progressPercent = (progress * 100).round();
|
final progressPercent = (progress * 100).round();
|
||||||
|
|
||||||
getStatusText() {
|
String getStatusText() => switch (status) {
|
||||||
switch (status) {
|
TaskStatus.running => 'downloading'.tr(),
|
||||||
case TaskStatus.running:
|
TaskStatus.complete => 'download_complete'.tr(),
|
||||||
return 'downloading'.tr();
|
TaskStatus.failed => 'download_failed'.tr(),
|
||||||
case TaskStatus.complete:
|
TaskStatus.canceled => 'download_canceled'.tr(),
|
||||||
return 'download_complete'.tr();
|
TaskStatus.paused => 'download_paused'.tr(),
|
||||||
case TaskStatus.failed:
|
TaskStatus.enqueued => 'download_enqueue'.tr(),
|
||||||
return 'download_failed'.tr();
|
TaskStatus.notFound => 'download_notfound'.tr(),
|
||||||
case TaskStatus.canceled:
|
TaskStatus.waitingToRetry => 'download_waiting_to_retry'.tr(),
|
||||||
return 'download_canceled'.tr();
|
};
|
||||||
case TaskStatus.paused:
|
|
||||||
return 'download_paused'.tr();
|
|
||||||
case TaskStatus.enqueued:
|
|
||||||
return 'download_enqueue'.tr();
|
|
||||||
case TaskStatus.notFound:
|
|
||||||
return 'download_notfound'.tr();
|
|
||||||
case TaskStatus.waitingToRetry:
|
|
||||||
return 'download_waiting_to_retry'.tr();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
key: const ValueKey('download_progress'),
|
key: const ValueKey('download_progress'),
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ class LargeLeadingTile extends StatelessWidget {
|
|||||||
this.trailing,
|
this.trailing,
|
||||||
this.selected = false,
|
this.selected = false,
|
||||||
this.disabled = false,
|
this.disabled = false,
|
||||||
|
this.selectedTileColor,
|
||||||
|
this.tileColor,
|
||||||
});
|
});
|
||||||
|
|
||||||
final Widget leading;
|
final Widget leading;
|
||||||
@@ -27,6 +29,9 @@ class LargeLeadingTile extends StatelessWidget {
|
|||||||
final Widget? trailing;
|
final Widget? trailing;
|
||||||
final bool selected;
|
final bool selected;
|
||||||
final bool disabled;
|
final bool disabled;
|
||||||
|
final Color? selectedTileColor;
|
||||||
|
final Color? tileColor;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return InkWell(
|
return InkWell(
|
||||||
@@ -35,8 +40,9 @@ class LargeLeadingTile extends StatelessWidget {
|
|||||||
child: Container(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: selected
|
color: selected
|
||||||
? Theme.of(context).primaryColor.withAlpha(30)
|
? selectedTileColor ??
|
||||||
: Colors.transparent,
|
Theme.of(context).primaryColor.withAlpha(30)
|
||||||
|
: tileColor ?? Colors.transparent,
|
||||||
borderRadius: BorderRadius.circular(borderRadius),
|
borderRadius: BorderRadius.circular(borderRadius),
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import 'package:wakelock_plus/wakelock_plus.dart';
|
|||||||
class NativeVideoViewerPage extends HookConsumerWidget {
|
class NativeVideoViewerPage extends HookConsumerWidget {
|
||||||
final Asset asset;
|
final Asset asset;
|
||||||
final bool showControls;
|
final bool showControls;
|
||||||
|
final int playbackDelayFactor;
|
||||||
final Widget image;
|
final Widget image;
|
||||||
|
|
||||||
const NativeVideoViewerPage({
|
const NativeVideoViewerPage({
|
||||||
@@ -33,6 +34,7 @@ class NativeVideoViewerPage extends HookConsumerWidget {
|
|||||||
required this.asset,
|
required this.asset,
|
||||||
required this.image,
|
required this.image,
|
||||||
this.showControls = true,
|
this.showControls = true,
|
||||||
|
this.playbackDelayFactor = 1,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -317,12 +319,16 @@ class NativeVideoViewerPage extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Delay the video playback to avoid a stutter in the swipe animation
|
// Delay the video playback to avoid a stutter in the swipe animation
|
||||||
|
// Note, in some circumstances a longer delay is needed (eg: memories),
|
||||||
|
// the playbackDelayFactor can be used for this
|
||||||
|
// This delay seems like a hacky way to resolve underlying bugs in video
|
||||||
|
// playback, but other resolutions failed thus far
|
||||||
Timer(
|
Timer(
|
||||||
Platform.isIOS
|
Platform.isIOS
|
||||||
? const Duration(milliseconds: 300)
|
? Duration(milliseconds: 300 * playbackDelayFactor)
|
||||||
: imageToVideo
|
: imageToVideo
|
||||||
? const Duration(milliseconds: 200)
|
? Duration(milliseconds: 200 * playbackDelayFactor)
|
||||||
: const Duration(milliseconds: 400), () {
|
: Duration(milliseconds: 400 * playbackDelayFactor), () {
|
||||||
if (!context.mounted) {
|
if (!context.mounted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ class TabControllerPage extends HookConsumerWidget {
|
|||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final isRefreshingAssets = ref.watch(assetProvider);
|
final isRefreshingAssets = ref.watch(assetProvider);
|
||||||
final isRefreshingRemoteAlbums = ref.watch(isRefreshingRemoteAlbumProvider);
|
final isRefreshingRemoteAlbums = ref.watch(isRefreshingRemoteAlbumProvider);
|
||||||
|
final isScreenLandscape =
|
||||||
|
MediaQuery.orientationOf(context) == Orientation.landscape;
|
||||||
|
|
||||||
Widget buildIcon({required Widget icon, required bool isProcessing}) {
|
Widget buildIcon({required Widget icon, required bool isProcessing}) {
|
||||||
if (!isProcessing) return icon;
|
if (!isProcessing) return icon;
|
||||||
@@ -45,7 +47,7 @@ class TabControllerPage extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
onNavigationSelected(TabsRouter router, int index) {
|
void onNavigationSelected(TabsRouter router, int index) {
|
||||||
// On Photos page menu tapped
|
// On Photos page menu tapped
|
||||||
if (router.activeIndex == 0 && index == 0) {
|
if (router.activeIndex == 0 && index == 0) {
|
||||||
scrollToTopNotifierProvider.scrollToTop();
|
scrollToTopNotifierProvider.scrollToTop();
|
||||||
@@ -61,62 +63,82 @@ class TabControllerPage extends HookConsumerWidget {
|
|||||||
ref.read(tabProvider.notifier).state = TabEnum.values[index];
|
ref.read(tabProvider.notifier).state = TabEnum.values[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
bottomNavigationBar(TabsRouter tabsRouter) {
|
final navigationDestinations = [
|
||||||
|
NavigationDestination(
|
||||||
|
label: 'tab_controller_nav_photos'.tr(),
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.photo_library_outlined,
|
||||||
|
),
|
||||||
|
selectedIcon: buildIcon(
|
||||||
|
isProcessing: isRefreshingAssets,
|
||||||
|
icon: Icon(
|
||||||
|
Icons.photo_library,
|
||||||
|
color: context.primaryColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
NavigationDestination(
|
||||||
|
label: 'tab_controller_nav_search'.tr(),
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.search_rounded,
|
||||||
|
),
|
||||||
|
selectedIcon: Icon(
|
||||||
|
Icons.search,
|
||||||
|
color: context.primaryColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
NavigationDestination(
|
||||||
|
label: 'albums'.tr(),
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.photo_album_outlined,
|
||||||
|
),
|
||||||
|
selectedIcon: buildIcon(
|
||||||
|
isProcessing: isRefreshingRemoteAlbums,
|
||||||
|
icon: Icon(
|
||||||
|
Icons.photo_album_rounded,
|
||||||
|
color: context.primaryColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
NavigationDestination(
|
||||||
|
label: 'library'.tr(),
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.space_dashboard_outlined,
|
||||||
|
),
|
||||||
|
selectedIcon: buildIcon(
|
||||||
|
isProcessing: isRefreshingAssets,
|
||||||
|
icon: Icon(
|
||||||
|
Icons.space_dashboard_rounded,
|
||||||
|
color: context.primaryColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
Widget bottomNavigationBar(TabsRouter tabsRouter) {
|
||||||
return NavigationBar(
|
return NavigationBar(
|
||||||
selectedIndex: tabsRouter.activeIndex,
|
selectedIndex: tabsRouter.activeIndex,
|
||||||
onDestinationSelected: (index) =>
|
onDestinationSelected: (index) =>
|
||||||
onNavigationSelected(tabsRouter, index),
|
onNavigationSelected(tabsRouter, index),
|
||||||
destinations: [
|
destinations: navigationDestinations,
|
||||||
NavigationDestination(
|
);
|
||||||
label: 'tab_controller_nav_photos'.tr(),
|
}
|
||||||
icon: const Icon(
|
|
||||||
Icons.photo_library_outlined,
|
Widget navigationRail(TabsRouter tabsRouter) {
|
||||||
),
|
return NavigationRail(
|
||||||
selectedIcon: buildIcon(
|
destinations: navigationDestinations
|
||||||
isProcessing: isRefreshingAssets,
|
.map(
|
||||||
icon: Icon(
|
(e) => NavigationRailDestination(
|
||||||
Icons.photo_library,
|
icon: e.icon,
|
||||||
color: context.primaryColor,
|
label: Text(e.label),
|
||||||
),
|
),
|
||||||
),
|
)
|
||||||
),
|
.toList(),
|
||||||
NavigationDestination(
|
onDestinationSelected: (index) =>
|
||||||
label: 'tab_controller_nav_search'.tr(),
|
onNavigationSelected(tabsRouter, index),
|
||||||
icon: const Icon(
|
selectedIndex: tabsRouter.activeIndex,
|
||||||
Icons.search_rounded,
|
labelType: NavigationRailLabelType.all,
|
||||||
),
|
groupAlignment: 0.0,
|
||||||
selectedIcon: Icon(
|
|
||||||
Icons.search,
|
|
||||||
color: context.primaryColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
NavigationDestination(
|
|
||||||
label: 'albums'.tr(),
|
|
||||||
icon: const Icon(
|
|
||||||
Icons.photo_album_outlined,
|
|
||||||
),
|
|
||||||
selectedIcon: buildIcon(
|
|
||||||
isProcessing: isRefreshingRemoteAlbums,
|
|
||||||
icon: Icon(
|
|
||||||
Icons.photo_album_rounded,
|
|
||||||
color: context.primaryColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
NavigationDestination(
|
|
||||||
label: 'library'.tr(),
|
|
||||||
icon: const Icon(
|
|
||||||
Icons.space_dashboard_outlined,
|
|
||||||
),
|
|
||||||
selectedIcon: buildIcon(
|
|
||||||
isProcessing: isRefreshingAssets,
|
|
||||||
icon: Icon(
|
|
||||||
Icons.space_dashboard_rounded,
|
|
||||||
color: context.primaryColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,17 +157,27 @@ class TabControllerPage extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
builder: (context, child) {
|
builder: (context, child) {
|
||||||
final tabsRouter = AutoTabsRouter.of(context);
|
final tabsRouter = AutoTabsRouter.of(context);
|
||||||
|
final heroedChild = HeroControllerScope(
|
||||||
|
controller: HeroController(),
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
return PopScope(
|
return PopScope(
|
||||||
canPop: tabsRouter.activeIndex == 0,
|
canPop: tabsRouter.activeIndex == 0,
|
||||||
onPopInvokedWithResult: (didPop, _) =>
|
onPopInvokedWithResult: (didPop, _) =>
|
||||||
!didPop ? tabsRouter.setActiveIndex(0) : null,
|
!didPop ? tabsRouter.setActiveIndex(0) : null,
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
body: HeroControllerScope(
|
body: isScreenLandscape
|
||||||
controller: HeroController(),
|
? Row(
|
||||||
child: child,
|
children: [
|
||||||
),
|
navigationRail(tabsRouter),
|
||||||
bottomNavigationBar:
|
const VerticalDivider(),
|
||||||
multiselectEnabled ? null : bottomNavigationBar(tabsRouter),
|
Expanded(child: heroedChild),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: heroedChild,
|
||||||
|
bottomNavigationBar: multiselectEnabled || isScreenLandscape
|
||||||
|
? null
|
||||||
|
: bottomNavigationBar(tabsRouter),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -174,33 +174,19 @@ class _AspectRatioButton extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
IconData iconData;
|
|
||||||
switch (label) {
|
|
||||||
case 'Free':
|
|
||||||
iconData = Icons.crop_free_rounded;
|
|
||||||
break;
|
|
||||||
case '1:1':
|
|
||||||
iconData = Icons.crop_square_rounded;
|
|
||||||
break;
|
|
||||||
case '16:9':
|
|
||||||
iconData = Icons.crop_16_9_rounded;
|
|
||||||
break;
|
|
||||||
case '3:2':
|
|
||||||
iconData = Icons.crop_3_2_rounded;
|
|
||||||
break;
|
|
||||||
case '7:5':
|
|
||||||
iconData = Icons.crop_7_5_rounded;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
iconData = Icons.crop_free_rounded;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
iconData,
|
switch (label) {
|
||||||
|
'Free' => Icons.crop_free_rounded,
|
||||||
|
'1:1' => Icons.crop_square_rounded,
|
||||||
|
'16:9' => Icons.crop_16_9_rounded,
|
||||||
|
'3:2' => Icons.crop_3_2_rounded,
|
||||||
|
'7:5' => Icons.crop_7_5_rounded,
|
||||||
|
_ => Icons.crop_free_rounded,
|
||||||
|
},
|
||||||
color: aspectRatio.value == ratio
|
color: aspectRatio.value == ratio
|
||||||
? context.primaryColor
|
? context.primaryColor
|
||||||
: context.themeData.iconTheme.color,
|
: context.themeData.iconTheme.color,
|
||||||
|
|||||||
@@ -136,23 +136,16 @@ class PermissionOnboardingPage extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final Widget child;
|
final Widget child = switch (permission) {
|
||||||
switch (permission) {
|
PermissionStatus.limited => buildPermissionLimited(),
|
||||||
case PermissionStatus.limited:
|
PermissionStatus.denied => buildRequestPermission(),
|
||||||
child = buildPermissionLimited();
|
PermissionStatus.granted ||
|
||||||
break;
|
PermissionStatus.provisional =>
|
||||||
case PermissionStatus.denied:
|
buildPermissionGranted(),
|
||||||
child = buildRequestPermission();
|
PermissionStatus.restricted ||
|
||||||
break;
|
PermissionStatus.permanentlyDenied =>
|
||||||
case PermissionStatus.granted:
|
buildPermissionDenied()
|
||||||
case PermissionStatus.provisional:
|
};
|
||||||
child = buildPermissionGranted();
|
|
||||||
break;
|
|
||||||
case PermissionStatus.restricted:
|
|
||||||
case PermissionStatus.permanentlyDenied:
|
|
||||||
child = buildPermissionDenied();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import 'package:flutter_hooks/flutter_hooks.dart';
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||||
import 'package:immich_mobile/models/memories/memory.model.dart';
|
import 'package:immich_mobile/models/memories/memory.model.dart';
|
||||||
|
import 'package:immich_mobile/providers/asset_viewer/current_asset.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/asset_viewer/video_player_value_provider.dart';
|
||||||
import 'package:immich_mobile/providers/haptic_feedback.provider.dart';
|
import 'package:immich_mobile/providers/haptic_feedback.provider.dart';
|
||||||
import 'package:immich_mobile/widgets/common/immich_image.dart';
|
import 'package:immich_mobile/widgets/common/immich_image.dart';
|
||||||
import 'package:immich_mobile/widgets/memories/memory_bottom_info.dart';
|
import 'package:immich_mobile/widgets/memories/memory_bottom_info.dart';
|
||||||
@@ -13,6 +15,8 @@ import 'package:immich_mobile/widgets/memories/memory_epilogue.dart';
|
|||||||
import 'package:immich_mobile/widgets/memories/memory_progress_indicator.dart';
|
import 'package:immich_mobile/widgets/memories/memory_progress_indicator.dart';
|
||||||
|
|
||||||
@RoutePage()
|
@RoutePage()
|
||||||
|
|
||||||
|
/// Expects [currentAssetProvider] to be set before navigating to this page
|
||||||
class MemoryPage extends HookConsumerWidget {
|
class MemoryPage extends HookConsumerWidget {
|
||||||
final List<Memory> memories;
|
final List<Memory> memories;
|
||||||
final int memoryIndex;
|
final int memoryIndex;
|
||||||
@@ -32,6 +36,7 @@ class MemoryPage extends HookConsumerWidget {
|
|||||||
"${currentAssetPage.value + 1}|${currentMemory.value.assets.length}",
|
"${currentAssetPage.value + 1}|${currentMemory.value.assets.length}",
|
||||||
);
|
);
|
||||||
const bgColor = Colors.black;
|
const bgColor = Colors.black;
|
||||||
|
final currentAsset = useState<Asset?>(null);
|
||||||
|
|
||||||
/// The list of all of the asset page controllers
|
/// The list of all of the asset page controllers
|
||||||
final memoryAssetPageControllers =
|
final memoryAssetPageControllers =
|
||||||
@@ -135,6 +140,14 @@ class MemoryPage extends HookConsumerWidget {
|
|||||||
ref.read(hapticFeedbackProvider.notifier).selectionClick();
|
ref.read(hapticFeedbackProvider.notifier).selectionClick();
|
||||||
currentAssetPage.value = otherIndex;
|
currentAssetPage.value = otherIndex;
|
||||||
updateProgressText();
|
updateProgressText();
|
||||||
|
|
||||||
|
final asset = currentMemory.value.assets[otherIndex];
|
||||||
|
currentAsset.value = asset;
|
||||||
|
ref.read(currentAssetProvider.notifier).set(asset);
|
||||||
|
if (asset.isVideo || asset.isMotionPhoto) {
|
||||||
|
ref.read(videoPlaybackValueProvider.notifier).reset();
|
||||||
|
}
|
||||||
|
|
||||||
// Wait for page change animation to finish
|
// Wait for page change animation to finish
|
||||||
await Future.delayed(const Duration(milliseconds: 400));
|
await Future.delayed(const Duration(milliseconds: 400));
|
||||||
// And then precache the next asset
|
// And then precache the next asset
|
||||||
@@ -274,6 +287,16 @@ class MemoryPage extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
if (currentAsset.value != null &&
|
||||||
|
currentAsset.value!.isVideo)
|
||||||
|
Positioned(
|
||||||
|
bottom: 24,
|
||||||
|
right: 32,
|
||||||
|
child: Icon(
|
||||||
|
Icons.videocam_outlined,
|
||||||
|
color: Colors.grey[200],
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import 'package:easy_localization/easy_localization.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/constants/enums.dart';
|
||||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/extensions/theme_extensions.dart';
|
import 'package:immich_mobile/extensions/theme_extensions.dart';
|
||||||
@@ -31,7 +32,8 @@ class SearchPage extends HookConsumerWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final isContextualSearch = useState(true);
|
final textSearchType = useState<TextSearchType>(TextSearchType.context);
|
||||||
|
final searchHintText = useState<String>('contextual_search'.tr());
|
||||||
final textSearchController = useTextEditingController();
|
final textSearchController = useTextEditingController();
|
||||||
final filter = useState<SearchFilter>(
|
final filter = useState<SearchFilter>(
|
||||||
SearchFilter(
|
SearchFilter(
|
||||||
@@ -49,7 +51,7 @@ class SearchPage extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
final previousFilter = useState(filter.value);
|
final previousFilter = useState<SearchFilter?>(null);
|
||||||
|
|
||||||
final peopleCurrentFilterWidget = useState<Widget?>(null);
|
final peopleCurrentFilterWidget = useState<Widget?>(null);
|
||||||
final dateRangeCurrentFilterWidget = useState<Widget?>(null);
|
final dateRangeCurrentFilterWidget = useState<Widget?>(null);
|
||||||
@@ -60,19 +62,55 @@ class SearchPage extends HookConsumerWidget {
|
|||||||
|
|
||||||
final isSearching = useState(false);
|
final isSearching = useState(false);
|
||||||
|
|
||||||
|
SnackBar searchInfoSnackBar(String message) {
|
||||||
|
return SnackBar(
|
||||||
|
content: Text(
|
||||||
|
message,
|
||||||
|
style: context.textTheme.labelLarge,
|
||||||
|
),
|
||||||
|
showCloseIcon: true,
|
||||||
|
behavior: SnackBarBehavior.fixed,
|
||||||
|
closeIconColor: context.colorScheme.onSurface,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
search() async {
|
search() async {
|
||||||
if (prefilter == null && filter.value == previousFilter.value) return;
|
if (filter.value.isEmpty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prefilter == null && filter.value == previousFilter.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
isSearching.value = true;
|
isSearching.value = true;
|
||||||
ref.watch(paginatedSearchProvider.notifier).clear();
|
ref.watch(paginatedSearchProvider.notifier).clear();
|
||||||
await ref.watch(paginatedSearchProvider.notifier).search(filter.value);
|
final hasResult = await ref
|
||||||
|
.watch(paginatedSearchProvider.notifier)
|
||||||
|
.search(filter.value);
|
||||||
|
|
||||||
|
if (!hasResult) {
|
||||||
|
context.showSnackBar(
|
||||||
|
searchInfoSnackBar('search_no_result'.tr()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
previousFilter.value = filter.value;
|
previousFilter.value = filter.value;
|
||||||
isSearching.value = false;
|
isSearching.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
loadMoreSearchResult() async {
|
loadMoreSearchResult() async {
|
||||||
isSearching.value = true;
|
isSearching.value = true;
|
||||||
await ref.watch(paginatedSearchProvider.notifier).search(filter.value);
|
final hasResult = await ref
|
||||||
|
.watch(paginatedSearchProvider.notifier)
|
||||||
|
.search(filter.value);
|
||||||
|
|
||||||
|
if (!hasResult) {
|
||||||
|
context.showSnackBar(
|
||||||
|
searchInfoSnackBar('search_no_more_result'.tr()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
isSearching.value = false;
|
isSearching.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -442,37 +480,148 @@ class SearchPage extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleTextSubmitted(String value) {
|
handleTextSubmitted(String value) {
|
||||||
if (isContextualSearch.value) {
|
switch (textSearchType.value) {
|
||||||
filter.value = filter.value.copyWith(
|
case TextSearchType.context:
|
||||||
filename: '',
|
filter.value = filter.value.copyWith(
|
||||||
context: value,
|
filename: '',
|
||||||
);
|
context: value,
|
||||||
} else {
|
description: '',
|
||||||
filter.value = filter.value.copyWith(
|
);
|
||||||
filename: value,
|
|
||||||
context: '',
|
break;
|
||||||
);
|
case TextSearchType.filename:
|
||||||
|
filter.value = filter.value.copyWith(
|
||||||
|
filename: value,
|
||||||
|
context: '',
|
||||||
|
description: '',
|
||||||
|
);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case TextSearchType.description:
|
||||||
|
filter.value = filter.value.copyWith(
|
||||||
|
filename: '',
|
||||||
|
context: '',
|
||||||
|
description: value,
|
||||||
|
);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
search();
|
search();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IconData getSearchPrefixIcon() {
|
||||||
|
switch (textSearchType.value) {
|
||||||
|
case TextSearchType.context:
|
||||||
|
return Icons.image_search_rounded;
|
||||||
|
case TextSearchType.filename:
|
||||||
|
return Icons.abc_rounded;
|
||||||
|
case TextSearchType.description:
|
||||||
|
return Icons.text_snippet_outlined;
|
||||||
|
default:
|
||||||
|
return Icons.search_rounded;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
resizeToAvoidBottomInset: true,
|
resizeToAvoidBottomInset: true,
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
automaticallyImplyLeading: true,
|
automaticallyImplyLeading: true,
|
||||||
actions: [
|
actions: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(right: 14.0),
|
padding: const EdgeInsets.only(right: 16.0),
|
||||||
child: IconButton(
|
child: MenuAnchor(
|
||||||
key: const Key('contextual_search_button'),
|
style: MenuStyle(
|
||||||
icon: isContextualSearch.value
|
elevation: const WidgetStatePropertyAll(1),
|
||||||
? const Icon(Icons.abc_rounded)
|
shape: WidgetStateProperty.all(
|
||||||
: const Icon(Icons.image_search_rounded),
|
RoundedRectangleBorder(
|
||||||
onPressed: () {
|
borderRadius: BorderRadius.circular(24),
|
||||||
isContextualSearch.value = !isContextualSearch.value;
|
),
|
||||||
textSearchController.clear();
|
),
|
||||||
|
padding: const WidgetStatePropertyAll(
|
||||||
|
EdgeInsets.all(4),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
builder: (
|
||||||
|
BuildContext context,
|
||||||
|
MenuController controller,
|
||||||
|
Widget? child,
|
||||||
|
) {
|
||||||
|
return IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
if (controller.isOpen) {
|
||||||
|
controller.close();
|
||||||
|
} else {
|
||||||
|
controller.open();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.more_vert_rounded),
|
||||||
|
tooltip: 'Show text search menu',
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
menuChildren: [
|
||||||
|
MenuItemButton(
|
||||||
|
child: ListTile(
|
||||||
|
leading: const Icon(Icons.image_search_rounded),
|
||||||
|
title: Text(
|
||||||
|
'search_filter_contextual'.tr(),
|
||||||
|
style: context.textTheme.bodyLarge?.copyWith(
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: textSearchType.value == TextSearchType.context
|
||||||
|
? context.colorScheme.primary
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
selectedColor: context.colorScheme.primary,
|
||||||
|
selected: textSearchType.value == TextSearchType.context,
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
textSearchType.value = TextSearchType.context;
|
||||||
|
searchHintText.value = 'contextual_search'.tr();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
MenuItemButton(
|
||||||
|
child: ListTile(
|
||||||
|
leading: const Icon(Icons.abc_rounded),
|
||||||
|
title: Text(
|
||||||
|
'search_filter_filename'.tr(),
|
||||||
|
style: context.textTheme.bodyLarge?.copyWith(
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: textSearchType.value == TextSearchType.filename
|
||||||
|
? context.colorScheme.primary
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
selectedColor: context.colorScheme.primary,
|
||||||
|
selected: textSearchType.value == TextSearchType.filename,
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
textSearchType.value = TextSearchType.filename;
|
||||||
|
searchHintText.value = 'filename_search'.tr();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
MenuItemButton(
|
||||||
|
child: ListTile(
|
||||||
|
leading: const Icon(Icons.text_snippet_outlined),
|
||||||
|
title: Text(
|
||||||
|
'search_filter_description'.tr(),
|
||||||
|
style: context.textTheme.bodyLarge?.copyWith(
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color:
|
||||||
|
textSearchType.value == TextSearchType.description
|
||||||
|
? context.colorScheme.primary
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
selectedColor: context.colorScheme.primary,
|
||||||
|
selected:
|
||||||
|
textSearchType.value == TextSearchType.description,
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
textSearchType.value = TextSearchType.description;
|
||||||
|
searchHintText.value = 'description_search'.tr();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -503,12 +652,10 @@ class SearchPage extends HookConsumerWidget {
|
|||||||
prefixIcon: prefilter != null
|
prefixIcon: prefilter != null
|
||||||
? null
|
? null
|
||||||
: Icon(
|
: Icon(
|
||||||
Icons.search_rounded,
|
getSearchPrefixIcon(),
|
||||||
color: context.colorScheme.primary,
|
color: context.colorScheme.primary,
|
||||||
),
|
),
|
||||||
hintText: isContextualSearch.value
|
hintText: searchHintText.value,
|
||||||
? 'contextual_search'.tr()
|
|
||||||
: 'filename_search'.tr(),
|
|
||||||
hintStyle: context.textTheme.bodyLarge?.copyWith(
|
hintStyle: context.textTheme.bodyLarge?.copyWith(
|
||||||
color: context.themeData.colorScheme.onSurfaceSecondary,
|
color: context.themeData.colorScheme.onSurfaceSecondary,
|
||||||
),
|
),
|
||||||
@@ -596,10 +743,15 @@ class SearchPage extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SearchResultGrid(
|
if (isSearching.value)
|
||||||
onScrollEnd: loadMoreSearchResult,
|
const Expanded(
|
||||||
isSearching: isSearching.value,
|
child: Center(child: CircularProgressIndicator.adaptive()),
|
||||||
),
|
)
|
||||||
|
else
|
||||||
|
SearchResultGrid(
|
||||||
|
onScrollEnd: loadMoreSearchResult,
|
||||||
|
isSearching: isSearching.value,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -8,43 +8,40 @@ import 'package:immich_mobile/services/album.service.dart';
|
|||||||
import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart';
|
import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart';
|
||||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||||
import 'package:immich_mobile/entities/album.entity.dart';
|
import 'package:immich_mobile/entities/album.entity.dart';
|
||||||
import 'package:immich_mobile/providers/db.provider.dart';
|
|
||||||
import 'package:immich_mobile/utils/renderlist_generator.dart';
|
|
||||||
import 'package:isar/isar.dart';
|
|
||||||
|
|
||||||
final isRefreshingRemoteAlbumProvider = StateProvider<bool>((ref) => false);
|
final isRefreshingRemoteAlbumProvider = StateProvider<bool>((ref) => false);
|
||||||
|
|
||||||
class AlbumNotifier extends StateNotifier<List<Album>> {
|
class AlbumNotifier extends StateNotifier<List<Album>> {
|
||||||
AlbumNotifier(this._albumService, this.db, this.ref) : super([]) {
|
AlbumNotifier(this.albumService, this.ref) : super([]) {
|
||||||
final query = db.albums.filter().remoteIdIsNotNull();
|
albumService.getAllRemoteAlbums().then((value) {
|
||||||
query.findAll().then((value) {
|
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
state = value;
|
state = value;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
_streamSub = query.watch().listen((data) => state = data);
|
|
||||||
|
_streamSub =
|
||||||
|
albumService.watchRemoteAlbums().listen((data) => state = data);
|
||||||
}
|
}
|
||||||
|
|
||||||
final AlbumService _albumService;
|
final AlbumService albumService;
|
||||||
final Isar db;
|
|
||||||
final Ref ref;
|
final Ref ref;
|
||||||
late final StreamSubscription<List<Album>> _streamSub;
|
late final StreamSubscription<List<Album>> _streamSub;
|
||||||
|
|
||||||
Future<void> refreshRemoteAlbums() async {
|
Future<void> refreshRemoteAlbums() async {
|
||||||
ref.read(isRefreshingRemoteAlbumProvider.notifier).state = true;
|
ref.read(isRefreshingRemoteAlbumProvider.notifier).state = true;
|
||||||
await _albumService.refreshRemoteAlbums();
|
await albumService.refreshRemoteAlbums();
|
||||||
ref.read(isRefreshingRemoteAlbumProvider.notifier).state = false;
|
ref.read(isRefreshingRemoteAlbumProvider.notifier).state = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> refreshDeviceAlbums() => _albumService.refreshDeviceAlbums();
|
Future<void> refreshDeviceAlbums() => albumService.refreshDeviceAlbums();
|
||||||
|
|
||||||
Future<bool> deleteAlbum(Album album) => _albumService.deleteAlbum(album);
|
Future<bool> deleteAlbum(Album album) => albumService.deleteAlbum(album);
|
||||||
|
|
||||||
Future<Album?> createAlbum(
|
Future<Album?> createAlbum(
|
||||||
String albumTitle,
|
String albumTitle,
|
||||||
Set<Asset> assets,
|
Set<Asset> assets,
|
||||||
) =>
|
) =>
|
||||||
_albumService.createAlbum(albumTitle, assets, []);
|
albumService.createAlbum(albumTitle, assets, []);
|
||||||
|
|
||||||
Future<Album?> getAlbumByName(
|
Future<Album?> getAlbumByName(
|
||||||
String albumName, {
|
String albumName, {
|
||||||
@@ -52,7 +49,7 @@ class AlbumNotifier extends StateNotifier<List<Album>> {
|
|||||||
bool? shared,
|
bool? shared,
|
||||||
bool? owner,
|
bool? owner,
|
||||||
}) =>
|
}) =>
|
||||||
_albumService.getAlbumByName(
|
albumService.getAlbumByName(
|
||||||
albumName,
|
albumName,
|
||||||
remote: remote,
|
remote: remote,
|
||||||
shared: shared,
|
shared: shared,
|
||||||
@@ -74,7 +71,7 @@ class AlbumNotifier extends StateNotifier<List<Album>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> leaveAlbum(Album album) async {
|
Future<bool> leaveAlbum(Album album) async {
|
||||||
var res = await _albumService.leaveAlbum(album);
|
var res = await albumService.leaveAlbum(album);
|
||||||
|
|
||||||
if (res) {
|
if (res) {
|
||||||
await deleteAlbum(album);
|
await deleteAlbum(album);
|
||||||
@@ -85,15 +82,15 @@ class AlbumNotifier extends StateNotifier<List<Album>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void searchAlbums(String searchTerm, QuickFilterMode filterMode) async {
|
void searchAlbums(String searchTerm, QuickFilterMode filterMode) async {
|
||||||
state = await _albumService.search(searchTerm, filterMode);
|
state = await albumService.search(searchTerm, filterMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> addUsers(Album album, List<String> userIds) async {
|
Future<void> addUsers(Album album, List<String> userIds) async {
|
||||||
await _albumService.addUsers(album, userIds);
|
await albumService.addUsers(album, userIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> removeUser(Album album, User user) async {
|
Future<bool> removeUser(Album album, User user) async {
|
||||||
final isRemoved = await _albumService.removeUser(album, user);
|
final isRemoved = await albumService.removeUser(album, user);
|
||||||
|
|
||||||
if (isRemoved && album.sharedUsers.isEmpty) {
|
if (isRemoved && album.sharedUsers.isEmpty) {
|
||||||
state = state.where((element) => element.id != album.id).toList();
|
state = state.where((element) => element.id != album.id).toList();
|
||||||
@@ -103,25 +100,25 @@ class AlbumNotifier extends StateNotifier<List<Album>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> addAssets(Album album, Iterable<Asset> assets) async {
|
Future<void> addAssets(Album album, Iterable<Asset> assets) async {
|
||||||
await _albumService.addAssets(album, assets);
|
await albumService.addAssets(album, assets);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> removeAsset(Album album, Iterable<Asset> assets) async {
|
Future<bool> removeAsset(Album album, Iterable<Asset> assets) async {
|
||||||
return await _albumService.removeAsset(album, assets);
|
return await albumService.removeAsset(album, assets);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> setActivitystatus(
|
Future<bool> setActivitystatus(
|
||||||
Album album,
|
Album album,
|
||||||
bool enabled,
|
bool enabled,
|
||||||
) {
|
) {
|
||||||
return _albumService.setActivityStatus(album, enabled);
|
return albumService.setActivityStatus(album, enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Album?> toggleSortOrder(Album album) {
|
Future<Album?> toggleSortOrder(Album album) {
|
||||||
final order =
|
final order =
|
||||||
album.sortOrder == SortOrder.asc ? SortOrder.desc : SortOrder.asc;
|
album.sortOrder == SortOrder.asc ? SortOrder.desc : SortOrder.asc;
|
||||||
|
|
||||||
return _albumService.updateSortOrder(album, order);
|
return albumService.updateSortOrder(album, order);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -135,57 +132,49 @@ final albumProvider =
|
|||||||
StateNotifierProvider.autoDispose<AlbumNotifier, List<Album>>((ref) {
|
StateNotifierProvider.autoDispose<AlbumNotifier, List<Album>>((ref) {
|
||||||
return AlbumNotifier(
|
return AlbumNotifier(
|
||||||
ref.watch(albumServiceProvider),
|
ref.watch(albumServiceProvider),
|
||||||
ref.watch(dbProvider),
|
|
||||||
ref,
|
ref,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
final albumWatcher =
|
final albumWatcher =
|
||||||
StreamProvider.autoDispose.family<Album, int>((ref, albumId) async* {
|
StreamProvider.autoDispose.family<Album, int>((ref, id) async* {
|
||||||
final db = ref.watch(dbProvider);
|
final albumService = ref.watch(albumServiceProvider);
|
||||||
final a = await db.albums.get(albumId);
|
|
||||||
if (a != null) yield a;
|
final album = await albumService.getAlbumById(id);
|
||||||
await for (final a in db.albums.watchObject(albumId, fireImmediately: true)) {
|
if (album != null) {
|
||||||
if (a != null) yield a;
|
yield album;
|
||||||
|
}
|
||||||
|
|
||||||
|
await for (final album in albumService.watchAlbum(id)) {
|
||||||
|
if (album != null) {
|
||||||
|
yield album;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
final albumRenderlistProvider =
|
final albumRenderlistProvider =
|
||||||
StreamProvider.autoDispose.family<RenderList, int>((ref, albumId) {
|
StreamProvider.autoDispose.family<RenderList, int>((ref, id) {
|
||||||
final album = ref.watch(albumWatcher(albumId)).value;
|
final album = ref.watch(albumWatcher(id)).value;
|
||||||
|
|
||||||
if (album != null) {
|
if (album != null) {
|
||||||
final query = album.assets.filter().isTrashedEqualTo(false);
|
return ref.watch(albumServiceProvider).getRenderListGenerator(album);
|
||||||
if (album.sortOrder == SortOrder.asc) {
|
|
||||||
return renderListGeneratorWithGroupBy(
|
|
||||||
query.sortByFileCreatedAt(),
|
|
||||||
GroupAssetsBy.none,
|
|
||||||
);
|
|
||||||
} else if (album.sortOrder == SortOrder.desc) {
|
|
||||||
return renderListGeneratorWithGroupBy(
|
|
||||||
query.sortByFileCreatedAtDesc(),
|
|
||||||
GroupAssetsBy.none,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return const Stream.empty();
|
return const Stream.empty();
|
||||||
});
|
});
|
||||||
|
|
||||||
class LocalAlbumsNotifier extends StateNotifier<List<Album>> {
|
class LocalAlbumsNotifier extends StateNotifier<List<Album>> {
|
||||||
LocalAlbumsNotifier(this.db) : super([]) {
|
LocalAlbumsNotifier(this.albumService) : super([]) {
|
||||||
final query = db.albums.where().remoteIdIsNull();
|
albumService.getAllLocalAlbums().then((value) {
|
||||||
|
|
||||||
query.findAll().then((value) {
|
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
state = value;
|
state = value;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
_streamSub = query.watch().listen((data) => state = data);
|
_streamSub = albumService.watchLocalAlbums().listen((data) => state = data);
|
||||||
}
|
}
|
||||||
|
|
||||||
final Isar db;
|
final AlbumService albumService;
|
||||||
late final StreamSubscription<List<Album>> _streamSub;
|
late final StreamSubscription<List<Album>> _streamSub;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -197,5 +186,5 @@ class LocalAlbumsNotifier extends StateNotifier<List<Album>> {
|
|||||||
|
|
||||||
final localAlbumsProvider =
|
final localAlbumsProvider =
|
||||||
StateNotifierProvider.autoDispose<LocalAlbumsNotifier, List<Album>>((ref) {
|
StateNotifierProvider.autoDispose<LocalAlbumsNotifier, List<Album>>((ref) {
|
||||||
return LocalAlbumsNotifier(ref.watch(dbProvider));
|
return LocalAlbumsNotifier(ref.watch(albumServiceProvider));
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,28 +2,40 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/providers/locale_provider.dart';
|
import 'package:immich_mobile/providers/locale_provider.dart';
|
||||||
import 'package:immich_mobile/providers/memory.provider.dart';
|
import 'package:immich_mobile/providers/memory.provider.dart';
|
||||||
import 'package:immich_mobile/repositories/asset_media.repository.dart';
|
|
||||||
import 'package:immich_mobile/services/album.service.dart';
|
import 'package:immich_mobile/services/album.service.dart';
|
||||||
import 'package:immich_mobile/entities/exif_info.entity.dart';
|
|
||||||
import 'package:immich_mobile/entities/store.entity.dart';
|
import 'package:immich_mobile/entities/store.entity.dart';
|
||||||
import 'package:immich_mobile/providers/db.provider.dart';
|
import 'package:immich_mobile/providers/db.provider.dart';
|
||||||
import 'package:immich_mobile/providers/user.provider.dart';
|
import 'package:immich_mobile/providers/user.provider.dart';
|
||||||
import 'package:immich_mobile/services/asset.service.dart';
|
import 'package:immich_mobile/services/asset.service.dart';
|
||||||
|
import 'package:immich_mobile/services/etag.service.dart';
|
||||||
|
import 'package:immich_mobile/services/exif.service.dart';
|
||||||
import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart';
|
import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart';
|
||||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||||
import 'package:immich_mobile/services/sync.service.dart';
|
import 'package:immich_mobile/services/sync.service.dart';
|
||||||
import 'package:immich_mobile/services/user.service.dart';
|
import 'package:immich_mobile/services/user.service.dart';
|
||||||
import 'package:immich_mobile/utils/db.dart';
|
|
||||||
import 'package:immich_mobile/utils/renderlist_generator.dart';
|
import 'package:immich_mobile/utils/renderlist_generator.dart';
|
||||||
import 'package:isar/isar.dart';
|
import 'package:isar/isar.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
|
|
||||||
|
final assetProvider = StateNotifierProvider<AssetNotifier, bool>((ref) {
|
||||||
|
return AssetNotifier(
|
||||||
|
ref.watch(assetServiceProvider),
|
||||||
|
ref.watch(albumServiceProvider),
|
||||||
|
ref.watch(userServiceProvider),
|
||||||
|
ref.watch(syncServiceProvider),
|
||||||
|
ref.watch(etagServiceProvider),
|
||||||
|
ref.watch(exifServiceProvider),
|
||||||
|
ref,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
class AssetNotifier extends StateNotifier<bool> {
|
class AssetNotifier extends StateNotifier<bool> {
|
||||||
final AssetService _assetService;
|
final AssetService _assetService;
|
||||||
final AlbumService _albumService;
|
final AlbumService _albumService;
|
||||||
final UserService _userService;
|
final UserService _userService;
|
||||||
final SyncService _syncService;
|
final SyncService _syncService;
|
||||||
final Isar _db;
|
final ETagService _etagService;
|
||||||
|
final ExifService _exifService;
|
||||||
final StateNotifierProviderRef _ref;
|
final StateNotifierProviderRef _ref;
|
||||||
final log = Logger('AssetNotifier');
|
final log = Logger('AssetNotifier');
|
||||||
bool _getAllAssetInProgress = false;
|
bool _getAllAssetInProgress = false;
|
||||||
@@ -34,7 +46,8 @@ class AssetNotifier extends StateNotifier<bool> {
|
|||||||
this._albumService,
|
this._albumService,
|
||||||
this._userService,
|
this._userService,
|
||||||
this._syncService,
|
this._syncService,
|
||||||
this._db,
|
this._etagService,
|
||||||
|
this._exifService,
|
||||||
this._ref,
|
this._ref,
|
||||||
) : super(false);
|
) : super(false);
|
||||||
|
|
||||||
@@ -48,7 +61,7 @@ class AssetNotifier extends StateNotifier<bool> {
|
|||||||
_getAllAssetInProgress = true;
|
_getAllAssetInProgress = true;
|
||||||
state = true;
|
state = true;
|
||||||
if (clear) {
|
if (clear) {
|
||||||
await clearAssetsAndAlbums(_db);
|
await clearAllAssets();
|
||||||
log.info("Manual refresh requested, cleared assets and albums from db");
|
log.info("Manual refresh requested, cleared assets and albums from db");
|
||||||
}
|
}
|
||||||
final bool changedUsers = await _userService.refreshUsers();
|
final bool changedUsers = await _userService.refreshUsers();
|
||||||
@@ -68,8 +81,15 @@ class AssetNotifier extends StateNotifier<bool> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> clearAllAsset() {
|
Future<void> clearAllAssets() async {
|
||||||
return clearAssetsAndAlbums(_db);
|
await Store.delete(StoreKey.assetETag);
|
||||||
|
await Future.wait([
|
||||||
|
_assetService.clearTable(),
|
||||||
|
_exifService.clearTable(),
|
||||||
|
_albumService.clearTable(),
|
||||||
|
_userService.clearTable(),
|
||||||
|
_etagService.clearTable(),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> onNewAssetUploaded(Asset newAsset) async {
|
Future<void> onNewAssetUploaded(Asset newAsset) async {
|
||||||
@@ -78,102 +98,43 @@ class AssetNotifier extends StateNotifier<bool> {
|
|||||||
await _syncService.syncNewAssetToDb(newAsset);
|
await _syncService.syncNewAssetToDb(newAsset);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> deleteLocalOnlyAssets(
|
Future<bool> deleteLocalAssets(List<Asset> assets) async {
|
||||||
Iterable<Asset> deleteAssets, {
|
|
||||||
bool onlyBackedUp = false,
|
|
||||||
}) async {
|
|
||||||
_deleteInProgress = true;
|
_deleteInProgress = true;
|
||||||
state = true;
|
state = true;
|
||||||
try {
|
try {
|
||||||
// Filter the assets based on the backed-up status
|
await _assetService.deleteLocalAssets(assets);
|
||||||
final assets = onlyBackedUp
|
return true;
|
||||||
? deleteAssets.where((e) => e.storage == AssetState.merged)
|
} catch (error) {
|
||||||
: deleteAssets;
|
log.severe("Failed to delete local assets", error);
|
||||||
|
return false;
|
||||||
if (assets.isEmpty) {
|
|
||||||
return false; // No assets to delete
|
|
||||||
}
|
|
||||||
|
|
||||||
// Proceed with local deletion of the filtered assets
|
|
||||||
final localDeleted = await _deleteLocalAssets(assets);
|
|
||||||
|
|
||||||
if (localDeleted.isNotEmpty) {
|
|
||||||
final localOnlyIds = assets
|
|
||||||
.where((e) => e.storage == AssetState.local)
|
|
||||||
.map((e) => e.id)
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
// Update merged assets to remote-only
|
|
||||||
final mergedAssets =
|
|
||||||
assets.where((e) => e.storage == AssetState.merged).map((e) {
|
|
||||||
e.localId = null;
|
|
||||||
return e;
|
|
||||||
}).toList();
|
|
||||||
|
|
||||||
// Update the local database
|
|
||||||
await _db.writeTxn(() async {
|
|
||||||
if (mergedAssets.isNotEmpty) {
|
|
||||||
await _db.assets
|
|
||||||
.putAll(mergedAssets); // Use the filtered merged assets
|
|
||||||
}
|
|
||||||
await _db.exifInfos.deleteAll(localOnlyIds);
|
|
||||||
await _db.assets.deleteAll(localOnlyIds);
|
|
||||||
});
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} finally {
|
} finally {
|
||||||
_deleteInProgress = false;
|
_deleteInProgress = false;
|
||||||
state = false;
|
state = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> deleteRemoteOnlyAssets(
|
/// Delete remote asset only
|
||||||
|
///
|
||||||
|
/// Default behavior is trashing the asset
|
||||||
|
Future<bool> deleteRemoteAssets(
|
||||||
Iterable<Asset> deleteAssets, {
|
Iterable<Asset> deleteAssets, {
|
||||||
bool force = false,
|
bool shouldDeletePermanently = false,
|
||||||
}) async {
|
}) async {
|
||||||
_deleteInProgress = true;
|
_deleteInProgress = true;
|
||||||
state = true;
|
state = true;
|
||||||
try {
|
try {
|
||||||
final remoteDeleted = await _deleteRemoteAssets(deleteAssets, force);
|
await _assetService.deleteRemoteAssets(
|
||||||
if (remoteDeleted.isNotEmpty) {
|
deleteAssets,
|
||||||
final assetsToUpdate = force
|
shouldDeletePermanently: shouldDeletePermanently,
|
||||||
|
);
|
||||||
/// If force, only update merged only assets and remove remote assets
|
return true;
|
||||||
? remoteDeleted
|
} catch (error) {
|
||||||
.where((e) => e.storage == AssetState.merged)
|
log.severe("Failed to delete remote assets", error);
|
||||||
.map((e) {
|
return false;
|
||||||
e.remoteId = null;
|
|
||||||
return e;
|
|
||||||
})
|
|
||||||
// If not force, trash everything
|
|
||||||
: remoteDeleted.where((e) => e.isRemote).map((e) {
|
|
||||||
e.isTrashed = true;
|
|
||||||
return e;
|
|
||||||
});
|
|
||||||
|
|
||||||
await _db.writeTxn(() async {
|
|
||||||
if (assetsToUpdate.isNotEmpty) {
|
|
||||||
await _db.assets.putAll(assetsToUpdate.toList());
|
|
||||||
}
|
|
||||||
if (force) {
|
|
||||||
final remoteOnly = remoteDeleted
|
|
||||||
.where((e) => e.storage == AssetState.remote)
|
|
||||||
.map((e) => e.id)
|
|
||||||
.toList();
|
|
||||||
await _db.exifInfos.deleteAll(remoteOnly);
|
|
||||||
await _db.assets.deleteAll(remoteOnly);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} finally {
|
} finally {
|
||||||
_deleteInProgress = false;
|
_deleteInProgress = false;
|
||||||
state = false;
|
state = false;
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> deleteAssets(
|
Future<bool> deleteAssets(
|
||||||
@@ -183,111 +144,18 @@ class AssetNotifier extends StateNotifier<bool> {
|
|||||||
_deleteInProgress = true;
|
_deleteInProgress = true;
|
||||||
state = true;
|
state = true;
|
||||||
try {
|
try {
|
||||||
final hasLocal = deleteAssets.any((a) => a.storage != AssetState.remote);
|
await _assetService.deleteAssets(
|
||||||
final localDeleted = await _deleteLocalAssets(deleteAssets);
|
deleteAssets,
|
||||||
final remoteDeleted = (hasLocal && localDeleted.isNotEmpty) || !hasLocal
|
shouldDeletePermanently: force,
|
||||||
? await _deleteRemoteAssets(deleteAssets, force)
|
);
|
||||||
: [];
|
return true;
|
||||||
if (localDeleted.isNotEmpty || remoteDeleted.isNotEmpty) {
|
} catch (error) {
|
||||||
final dbIds = <int>[];
|
log.severe("Failed to delete assets", error);
|
||||||
final dbUpdates = <Asset>[];
|
return false;
|
||||||
|
|
||||||
// Local assets are removed
|
|
||||||
if (localDeleted.isNotEmpty) {
|
|
||||||
// Permanently remove local only assets from isar
|
|
||||||
dbIds.addAll(
|
|
||||||
deleteAssets
|
|
||||||
.where((a) => a.storage == AssetState.local)
|
|
||||||
.map((e) => e.id),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (remoteDeleted.any((e) => e.isLocal)) {
|
|
||||||
// Force delete: Add all local assets including merged assets
|
|
||||||
if (force) {
|
|
||||||
dbIds.addAll(remoteDeleted.map((e) => e.id));
|
|
||||||
// Soft delete: Remove local Id from asset and trash it
|
|
||||||
} else {
|
|
||||||
dbUpdates.addAll(
|
|
||||||
remoteDeleted.map((e) {
|
|
||||||
e.localId = null;
|
|
||||||
e.isTrashed = true;
|
|
||||||
return e;
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle remote deletion
|
|
||||||
if (remoteDeleted.isNotEmpty) {
|
|
||||||
if (force) {
|
|
||||||
// Remove remote only assets
|
|
||||||
dbIds.addAll(
|
|
||||||
deleteAssets
|
|
||||||
.where((a) => a.storage == AssetState.remote)
|
|
||||||
.map((e) => e.id),
|
|
||||||
);
|
|
||||||
// Local assets are not removed and there are merged assets
|
|
||||||
final hasLocal = remoteDeleted.any((e) => e.isLocal);
|
|
||||||
if (localDeleted.isEmpty && hasLocal) {
|
|
||||||
// Remove remote Id from local assets
|
|
||||||
dbUpdates.addAll(
|
|
||||||
remoteDeleted.map((e) {
|
|
||||||
e.remoteId = null;
|
|
||||||
// Remove from trashed if remote asset is removed
|
|
||||||
e.isTrashed = false;
|
|
||||||
return e;
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
dbUpdates.addAll(
|
|
||||||
remoteDeleted.map((e) {
|
|
||||||
e.isTrashed = true;
|
|
||||||
return e;
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await _db.writeTxn(() async {
|
|
||||||
await _db.assets.putAll(dbUpdates);
|
|
||||||
await _db.exifInfos.deleteAll(dbIds);
|
|
||||||
await _db.assets.deleteAll(dbIds);
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} finally {
|
} finally {
|
||||||
_deleteInProgress = false;
|
_deleteInProgress = false;
|
||||||
state = false;
|
state = false;
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<List<String>> _deleteLocalAssets(
|
|
||||||
Iterable<Asset> assetsToDelete,
|
|
||||||
) async {
|
|
||||||
final List<String> local =
|
|
||||||
assetsToDelete.where((a) => a.isLocal).map((a) => a.localId!).toList();
|
|
||||||
// Delete asset from device
|
|
||||||
if (local.isNotEmpty) {
|
|
||||||
try {
|
|
||||||
return await _ref.read(assetMediaRepositoryProvider).deleteAll(local);
|
|
||||||
} catch (e, stack) {
|
|
||||||
log.severe("Failed to delete asset from device", e, stack);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<List<Asset>> _deleteRemoteAssets(
|
|
||||||
Iterable<Asset> assetsToDelete,
|
|
||||||
bool? force,
|
|
||||||
) async {
|
|
||||||
final Iterable<Asset> remote = assetsToDelete.where((e) => e.isRemote);
|
|
||||||
|
|
||||||
final isSuccess = await _assetService.deleteAssets(remote, force: force);
|
|
||||||
return isSuccess ? remote.toList() : [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> toggleFavorite(List<Asset> assets, [bool? status]) {
|
Future<void> toggleFavorite(List<Asset> assets, [bool? status]) {
|
||||||
@@ -301,41 +169,40 @@ class AssetNotifier extends StateNotifier<bool> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final assetProvider = StateNotifierProvider<AssetNotifier, bool>((ref) {
|
|
||||||
return AssetNotifier(
|
|
||||||
ref.watch(assetServiceProvider),
|
|
||||||
ref.watch(albumServiceProvider),
|
|
||||||
ref.watch(userServiceProvider),
|
|
||||||
ref.watch(syncServiceProvider),
|
|
||||||
ref.watch(dbProvider),
|
|
||||||
ref,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
final assetDetailProvider =
|
final assetDetailProvider =
|
||||||
StreamProvider.autoDispose.family<Asset, Asset>((ref, asset) async* {
|
StreamProvider.autoDispose.family<Asset, Asset>((ref, asset) async* {
|
||||||
yield await ref.watch(assetServiceProvider).loadExif(asset);
|
final assetService = ref.watch(assetServiceProvider);
|
||||||
final db = ref.watch(dbProvider);
|
yield await assetService.loadExif(asset);
|
||||||
await for (final a in db.assets.watchObject(asset.id)) {
|
|
||||||
if (a != null) {
|
await for (final asset in assetService.watchAsset(asset.id)) {
|
||||||
yield await ref.watch(assetServiceProvider).loadExif(a);
|
if (asset != null) {
|
||||||
|
yield await ref.watch(assetServiceProvider).loadExif(asset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
final assetWatcher =
|
final assetWatcher =
|
||||||
StreamProvider.autoDispose.family<Asset?, Asset>((ref, asset) {
|
StreamProvider.autoDispose.family<Asset?, Asset>((ref, asset) {
|
||||||
final db = ref.watch(dbProvider);
|
final assetService = ref.watch(assetServiceProvider);
|
||||||
return db.assets.watchObject(asset.id, fireImmediately: true);
|
return assetService.watchAsset(asset.id, fireImmediately: true);
|
||||||
});
|
});
|
||||||
|
|
||||||
final assetsProvider = StreamProvider.family<RenderList, int?>(
|
final assetsProvider = StreamProvider.family<RenderList, int?>(
|
||||||
(ref, userId) {
|
(ref, userId) {
|
||||||
if (userId == null) return const Stream.empty();
|
if (userId == null) return const Stream.empty();
|
||||||
ref.watch(localeProvider);
|
ref.watch(localeProvider);
|
||||||
final query = _commonFilterAndSort(
|
|
||||||
_assets(ref).where().ownerIdEqualToAnyChecksum(userId),
|
final query = ref
|
||||||
);
|
.watch(dbProvider)
|
||||||
|
.assets
|
||||||
|
.where()
|
||||||
|
.ownerIdEqualToAnyChecksum(userId)
|
||||||
|
.filter()
|
||||||
|
.isArchivedEqualTo(false)
|
||||||
|
.isTrashedEqualTo(false)
|
||||||
|
.stackPrimaryAssetIdIsNull()
|
||||||
|
.sortByFileCreatedAtDesc();
|
||||||
|
|
||||||
return renderListGenerator(query, ref);
|
return renderListGenerator(query, ref);
|
||||||
},
|
},
|
||||||
dependencies: [localeProvider],
|
dependencies: [localeProvider],
|
||||||
@@ -345,11 +212,17 @@ final multiUserAssetsProvider = StreamProvider.family<RenderList, List<int>>(
|
|||||||
(ref, userIds) {
|
(ref, userIds) {
|
||||||
if (userIds.isEmpty) return const Stream.empty();
|
if (userIds.isEmpty) return const Stream.empty();
|
||||||
ref.watch(localeProvider);
|
ref.watch(localeProvider);
|
||||||
final query = _commonFilterAndSort(
|
final query = ref
|
||||||
_assets(ref)
|
.watch(dbProvider)
|
||||||
.where()
|
.assets
|
||||||
.anyOf(userIds, (q, u) => q.ownerIdEqualToAnyChecksum(u)),
|
.where()
|
||||||
);
|
.anyOf(userIds, (q, u) => q.ownerIdEqualToAnyChecksum(u))
|
||||||
|
.filter()
|
||||||
|
.isArchivedEqualTo(false)
|
||||||
|
.isTrashedEqualTo(false)
|
||||||
|
.stackPrimaryAssetIdIsNull()
|
||||||
|
.sortByFileCreatedAtDesc();
|
||||||
|
|
||||||
return renderListGenerator(query, ref);
|
return renderListGenerator(query, ref);
|
||||||
},
|
},
|
||||||
dependencies: [localeProvider],
|
dependencies: [localeProvider],
|
||||||
@@ -371,17 +244,3 @@ QueryBuilder<Asset, Asset, QAfterSortBy>? getRemoteAssetQuery(WidgetRef ref) {
|
|||||||
.stackPrimaryAssetIdIsNull()
|
.stackPrimaryAssetIdIsNull()
|
||||||
.sortByFileCreatedAtDesc();
|
.sortByFileCreatedAtDesc();
|
||||||
}
|
}
|
||||||
|
|
||||||
IsarCollection<Asset> _assets(StreamProviderRef<RenderList> ref) =>
|
|
||||||
ref.watch(dbProvider).assets;
|
|
||||||
|
|
||||||
QueryBuilder<Asset, Asset, QAfterSortBy> _commonFilterAndSort(
|
|
||||||
QueryBuilder<Asset, Asset, QAfterWhereClause> query,
|
|
||||||
) {
|
|
||||||
return query
|
|
||||||
.filter()
|
|
||||||
.isArchivedEqualTo(false)
|
|
||||||
.isTrashedEqualTo(false)
|
|
||||||
.stackPrimaryAssetIdIsNull()
|
|
||||||
.sortByFileCreatedAtDesc();
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,16 +1,15 @@
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||||
import 'package:immich_mobile/providers/db.provider.dart';
|
import 'package:immich_mobile/services/asset.service.dart';
|
||||||
import 'package:isar/isar.dart';
|
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
|
|
||||||
part 'asset_stack.provider.g.dart';
|
part 'asset_stack.provider.g.dart';
|
||||||
|
|
||||||
class AssetStackNotifier extends StateNotifier<List<Asset>> {
|
class AssetStackNotifier extends StateNotifier<List<Asset>> {
|
||||||
|
final AssetService assetService;
|
||||||
final String _stackId;
|
final String _stackId;
|
||||||
final Ref _ref;
|
|
||||||
|
|
||||||
AssetStackNotifier(this._stackId, this._ref) : super([]) {
|
AssetStackNotifier(this.assetService, this._stackId) : super([]) {
|
||||||
_fetchStack(_stackId);
|
_fetchStack(_stackId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -19,7 +18,7 @@ class AssetStackNotifier extends StateNotifier<List<Asset>> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final stack = await _ref.read(assetStackProvider(stackId).future);
|
final stack = await assetService.getStackAssets(stackId);
|
||||||
if (stack.isNotEmpty) {
|
if (stack.isNotEmpty) {
|
||||||
state = stack;
|
state = stack;
|
||||||
}
|
}
|
||||||
@@ -35,24 +34,10 @@ class AssetStackNotifier extends StateNotifier<List<Asset>> {
|
|||||||
|
|
||||||
final assetStackStateProvider = StateNotifierProvider.autoDispose
|
final assetStackStateProvider = StateNotifierProvider.autoDispose
|
||||||
.family<AssetStackNotifier, List<Asset>, String>(
|
.family<AssetStackNotifier, List<Asset>, String>(
|
||||||
(ref, stackId) => AssetStackNotifier(stackId, ref),
|
(ref, stackId) =>
|
||||||
|
AssetStackNotifier(ref.watch(assetServiceProvider), stackId),
|
||||||
);
|
);
|
||||||
|
|
||||||
final assetStackProvider =
|
|
||||||
FutureProvider.autoDispose.family<List<Asset>, String>((ref, stackId) {
|
|
||||||
return ref
|
|
||||||
.watch(dbProvider)
|
|
||||||
.assets
|
|
||||||
.filter()
|
|
||||||
.isArchivedEqualTo(false)
|
|
||||||
.isTrashedEqualTo(false)
|
|
||||||
.stackIdEqualTo(stackId)
|
|
||||||
// orders primary asset first as its ID is null
|
|
||||||
.sortByStackPrimaryAssetId()
|
|
||||||
.thenByFileCreatedAtDesc()
|
|
||||||
.findAll();
|
|
||||||
});
|
|
||||||
|
|
||||||
@riverpod
|
@riverpod
|
||||||
int assetStackIndex(AssetStackIndexRef ref, Asset asset) {
|
int assetStackIndex(AssetStackIndexRef ref, Asset asset) {
|
||||||
return -1;
|
return -1;
|
||||||
|
|||||||
@@ -19,17 +19,23 @@ class PaginatedSearchNotifier extends StateNotifier<SearchResult> {
|
|||||||
PaginatedSearchNotifier(this._searchService)
|
PaginatedSearchNotifier(this._searchService)
|
||||||
: super(SearchResult(assets: [], nextPage: 1));
|
: super(SearchResult(assets: [], nextPage: 1));
|
||||||
|
|
||||||
search(SearchFilter filter) async {
|
Future<bool> search(SearchFilter filter) async {
|
||||||
if (state.nextPage == null) return;
|
if (state.nextPage == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
final result = await _searchService.search(filter, state.nextPage!);
|
final result = await _searchService.search(filter, state.nextPage!);
|
||||||
|
|
||||||
if (result == null) return;
|
if (result == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
state = SearchResult(
|
state = SearchResult(
|
||||||
assets: [...state.assets, ...result.assets],
|
assets: [...state.assets, ...result.assets],
|
||||||
nextPage: result.nextPage,
|
nextPage: result.nextPage,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
clear() {
|
clear() {
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ class TrashNotifier extends StateNotifier<bool> {
|
|||||||
|
|
||||||
final isRemoved = await _ref
|
final isRemoved = await _ref
|
||||||
.read(assetProvider.notifier)
|
.read(assetProvider.notifier)
|
||||||
.deleteRemoteOnlyAssets(assetList, force: true);
|
.deleteRemoteAssets(assetList, shouldDeletePermanently: true);
|
||||||
|
|
||||||
if (isRemoved) {
|
if (isRemoved) {
|
||||||
final idsToRemove =
|
final idsToRemove =
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user