mirror of
https://github.com/immich-app/immich.git
synced 2026-06-02 12:55:17 -04:00
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 408e1180ca | |||
| 07f19d2caa | |||
| 368cb7a4ad | |||
| 109e0a7ad0 | |||
| 59750dad7d | |||
| 13ecfc8876 | |||
| 65d8b35f8b | |||
| 942d3c648c | |||
| 82db8be5ff | |||
| 03554b24ad | |||
| c5fb67c004 | |||
| 40983b46c8 | |||
| 5dcdbf04ea | |||
| da8ed3eceb | |||
| 2afde23a5d | |||
| d57a152040 | |||
| 728e92ea33 | |||
| 138e2d9158 |
@@ -51,7 +51,7 @@ jobs:
|
||||
should_run: ${{ steps.check.outputs.should_run }}
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
@@ -79,7 +79,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
@@ -201,7 +201,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
@@ -19,7 +19,7 @@ jobs:
|
||||
actions: write
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
@@ -24,7 +24,7 @@ jobs:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Check for breaking API changes
|
||||
uses: oasdiff/oasdiff-action/breaking@6147a58e5d1249a12f42fc864ab791d571a30015 # v0.0.47
|
||||
uses: oasdiff/oasdiff-action/breaking@50e6a3413e5aa9c3ae4d8393c34745be44288b46 # v0.0.48
|
||||
with:
|
||||
base: https://raw.githubusercontent.com/${{ github.repository }}/main/open-api/immich-openapi-specs.json
|
||||
revision: open-api/immich-openapi-specs.json
|
||||
|
||||
@@ -31,7 +31,7 @@ jobs:
|
||||
working-directory: ./packages/cli
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
@@ -63,7 +63,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
@@ -75,13 +75,13 @@ jobs:
|
||||
token: ${{ steps.token.outputs.token }}
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
|
||||
uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
|
||||
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
|
||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||
with:
|
||||
registry: ghcr.io
|
||||
@@ -96,7 +96,7 @@ jobs:
|
||||
|
||||
- name: Generate docker image tags
|
||||
id: metadata
|
||||
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0
|
||||
uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6.1.0
|
||||
with:
|
||||
flavor: |
|
||||
latest=false
|
||||
@@ -107,7 +107,7 @@ jobs:
|
||||
type=raw,value=latest,enable=${{ github.event_name == 'release' && !github.event.release.prerelease }}
|
||||
|
||||
- name: Build and push image
|
||||
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
|
||||
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
|
||||
with:
|
||||
file: packages/cli/Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
|
||||
@@ -35,7 +35,7 @@ jobs:
|
||||
needs: [get_body, should_run]
|
||||
if: ${{ needs.should_run.outputs.should_run == 'true' }}
|
||||
container:
|
||||
image: ghcr.io/immich-app/mdq:main@sha256:0a8b8867773a0f8368061f47578603f438349f8f1f28b0e16105f481e5c794e0
|
||||
image: ghcr.io/immich-app/mdq:main@sha256:e73f60195b39748c4876f23e3e6cd22a68a9754acec8aef1fd6979fd52cd2c9f
|
||||
outputs:
|
||||
checked: ${{ steps.get_checkbox.outputs.checked }}
|
||||
steps:
|
||||
|
||||
@@ -44,7 +44,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
@@ -57,7 +57,7 @@ jobs:
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@9e0d7b8d25671d64c341c19c0152d693099fb5ba # v4.35.5
|
||||
uses: github/codeql-action/init@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
@@ -70,7 +70,7 @@ jobs:
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@9e0d7b8d25671d64c341c19c0152d693099fb5ba # v4.35.5
|
||||
uses: github/codeql-action/autobuild@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
@@ -83,6 +83,6 @@ jobs:
|
||||
# ./location_of_script_within_repo/buildscript.sh
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@9e0d7b8d25671d64c341c19c0152d693099fb5ba # v4.35.5
|
||||
uses: github/codeql-action/analyze@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0
|
||||
with:
|
||||
category: '/language:${{matrix.language}}'
|
||||
|
||||
@@ -23,7 +23,7 @@ jobs:
|
||||
should_run: ${{ steps.check.outputs.should_run }}
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
@@ -60,7 +60,7 @@ jobs:
|
||||
suffix: ['', '-cuda', '-rocm', '-openvino', '-armnn', '-rknn']
|
||||
steps:
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
|
||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
@@ -90,7 +90,7 @@ jobs:
|
||||
suffix: ['']
|
||||
steps:
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
|
||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
should_run: ${{ steps.check.outputs.should_run }}
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
@@ -54,7 +54,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
artifact: ${{ steps.get-artifact.outputs.result }}
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
@@ -126,7 +126,7 @@ jobs:
|
||||
if: ${{ fromJson(needs.checks.outputs.artifact).found && fromJson(needs.checks.outputs.parameters).shouldDeploy }}
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
@@ -15,7 +15,7 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
@@ -14,7 +14,7 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
@@ -12,7 +12,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
@@ -49,7 +49,7 @@ jobs:
|
||||
permissions: {} # No job-level permissions are needed because it uses the app-token
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
@@ -14,7 +14,7 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
@@ -32,7 +32,7 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
packages: write
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
should_run: ${{ steps.check.outputs.should_run }}
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
@@ -49,7 +49,7 @@ jobs:
|
||||
working-directory: ./mobile
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
+17
-17
@@ -17,7 +17,7 @@ jobs:
|
||||
should_run: ${{ steps.check.outputs.should_run }}
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
@@ -71,7 +71,7 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
@@ -102,7 +102,7 @@ jobs:
|
||||
working-directory: ./packages/cli
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
@@ -133,7 +133,7 @@ jobs:
|
||||
working-directory: ./packages/cli
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
@@ -177,7 +177,7 @@ jobs:
|
||||
working-directory: ./web
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
@@ -215,7 +215,7 @@ jobs:
|
||||
working-directory: ./web
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
@@ -243,7 +243,7 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
@@ -293,7 +293,7 @@ jobs:
|
||||
working-directory: ./e2e
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
@@ -325,7 +325,7 @@ jobs:
|
||||
working-directory: ./server
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
@@ -361,7 +361,7 @@ jobs:
|
||||
runner: [ubuntu-latest, ubuntu-24.04-arm]
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
@@ -438,7 +438,7 @@ jobs:
|
||||
runner: [ubuntu-latest, ubuntu-24.04-arm]
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
@@ -546,7 +546,7 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
@@ -583,7 +583,7 @@ jobs:
|
||||
working-directory: ./machine-learning
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
@@ -613,7 +613,7 @@ jobs:
|
||||
working-directory: ./.github
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
@@ -643,7 +643,7 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
@@ -664,7 +664,7 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
@@ -722,7 +722,7 @@ jobs:
|
||||
- 5432:5432
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
@@ -24,7 +24,7 @@ jobs:
|
||||
should_run: ${{ steps.check.outputs.should_run }}
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
@@ -47,7 +47,7 @@ jobs:
|
||||
if: ${{ fromJSON(needs.pre-job.outputs.should_run).i18n == true }}
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
@@ -154,7 +154,7 @@ services:
|
||||
|
||||
redis:
|
||||
container_name: immich_redis
|
||||
image: docker.io/valkey/valkey:9@sha256:8436e10bc65c94886a91d4415b6a6dfa9cb5a306fb3b996e5bb67cd2b4854193
|
||||
image: docker.io/valkey/valkey:9@sha256:4963247afc4cd33c7d3b2d2816b9f7f8eeebab148d29056c2ca4d7cbc966f2d9
|
||||
healthcheck:
|
||||
test: redis-cli ping || exit 1
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ services:
|
||||
|
||||
redis:
|
||||
container_name: immich_redis
|
||||
image: docker.io/valkey/valkey:9@sha256:8436e10bc65c94886a91d4415b6a6dfa9cb5a306fb3b996e5bb67cd2b4854193
|
||||
image: docker.io/valkey/valkey:9@sha256:4963247afc4cd33c7d3b2d2816b9f7f8eeebab148d29056c2ca4d7cbc966f2d9
|
||||
healthcheck:
|
||||
test: redis-cli ping || exit 1
|
||||
restart: always
|
||||
@@ -85,7 +85,7 @@ services:
|
||||
container_name: immich_prometheus
|
||||
ports:
|
||||
- 9090:9090
|
||||
image: prom/prometheus@sha256:e4254400b85610324913f0dc4acf92603d9984e7519414c5a12811aa6146acc3
|
||||
image: prom/prometheus@sha256:69f5241418838263316593f7274a304b095c40bcf22e57272865da91bd60a8ac
|
||||
volumes:
|
||||
- ./prometheus.yml:/etc/prometheus/prometheus.yml
|
||||
- prometheus-data:/prometheus
|
||||
|
||||
@@ -61,7 +61,7 @@ services:
|
||||
|
||||
redis:
|
||||
container_name: immich_redis
|
||||
image: docker.io/valkey/valkey:9@sha256:8436e10bc65c94886a91d4415b6a6dfa9cb5a306fb3b996e5bb67cd2b4854193
|
||||
image: docker.io/valkey/valkey:9@sha256:4963247afc4cd33c7d3b2d2816b9f7f8eeebab148d29056c2ca4d7cbc966f2d9
|
||||
user: '1000:1000'
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
|
||||
@@ -49,7 +49,7 @@ services:
|
||||
|
||||
redis:
|
||||
container_name: immich_redis
|
||||
image: docker.io/valkey/valkey:9@sha256:8436e10bc65c94886a91d4415b6a6dfa9cb5a306fb3b996e5bb67cd2b4854193
|
||||
image: docker.io/valkey/valkey:9@sha256:4963247afc4cd33c7d3b2d2816b9f7f8eeebab148d29056c2ca4d7cbc966f2d9
|
||||
healthcheck:
|
||||
test: redis-cli ping || exit 1
|
||||
restart: always
|
||||
|
||||
@@ -44,7 +44,7 @@ services:
|
||||
|
||||
redis:
|
||||
container_name: immich-e2e-redis
|
||||
image: docker.io/valkey/valkey:9@sha256:8436e10bc65c94886a91d4415b6a6dfa9cb5a306fb3b996e5bb67cd2b4854193
|
||||
image: docker.io/valkey/valkey:9@sha256:4963247afc4cd33c7d3b2d2816b9f7f8eeebab148d29056c2ca4d7cbc966f2d9
|
||||
healthcheck:
|
||||
test: redis-cli ping || exit 1
|
||||
|
||||
|
||||
@@ -116,6 +116,7 @@ describe('/server', () => {
|
||||
oauthAutoLaunch: false,
|
||||
ocr: false,
|
||||
passwordLogin: true,
|
||||
realtimeTranscoding: false,
|
||||
search: true,
|
||||
sidecar: true,
|
||||
trash: true,
|
||||
@@ -140,6 +141,7 @@ describe('/server', () => {
|
||||
maintenanceMode: false,
|
||||
mapDarkStyleUrl: 'https://tiles.immich.cloud/v1/style/dark.json',
|
||||
mapLightStyleUrl: 'https://tiles.immich.cloud/v1/style/light.json',
|
||||
minFaces: 3,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -230,6 +230,21 @@ describe('/users', () => {
|
||||
const after = await getMyPreferences({ headers: asBearerAuth(admin.accessToken) });
|
||||
expect(after).toMatchObject({ download: { includeEmbeddedVideos: true } });
|
||||
});
|
||||
|
||||
it('should update minimum face count to display people', async () => {
|
||||
const before = await getMyPreferences({ headers: asBearerAuth(admin.accessToken) });
|
||||
expect(before).toMatchObject({ people: { minimumFaces: 3 } });
|
||||
|
||||
const { status, body } = await request(app)
|
||||
.put('/users/me/preferences')
|
||||
.send({ people: { minimumFaces: 2 } })
|
||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||
expect(status).toBe(200);
|
||||
expect(body).toMatchObject({ people: { minimumFaces: 2 } });
|
||||
|
||||
const after = await getMyPreferences({ headers: asBearerAuth(admin.accessToken) });
|
||||
expect(after).toMatchObject({ people: { minimumFaces: 2 } });
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /users/:id', () => {
|
||||
|
||||
@@ -1592,6 +1592,8 @@
|
||||
"merge_people_prompt": "Do you want to merge these people? This action is irreversible.",
|
||||
"merge_people_successfully": "Merge people successfully",
|
||||
"merged_people_count": "Merged {count, plural, one {# person} other {# people}}",
|
||||
"minFaces": "Minimum faces",
|
||||
"minFaces_description": "The minimum number of recognized faces for a person to be displayed",
|
||||
"minimize": "Minimize",
|
||||
"minute": "Minute",
|
||||
"minutes": "Minutes",
|
||||
@@ -2460,6 +2462,7 @@
|
||||
"video": "Video",
|
||||
"video_hover_setting": "Play video thumbnail on hover",
|
||||
"video_hover_setting_description": "Play video thumbnail when mouse is hovering over item. Even when disabled, playback can be started by hovering over the play icon.",
|
||||
"video_quality": "Video quality",
|
||||
"videos": "Videos",
|
||||
"videos_count": "{count, plural, one {# Video} other {# Videos}}",
|
||||
"videos_only": "Videos only",
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
ARG DEVICE=cpu
|
||||
|
||||
FROM python:3.11-bookworm@sha256:970c99f886b839fc8829289040c1845dadaf2cae46b37acc7710333158ec29b4 AS builder-cpu
|
||||
FROM python:3.11-bookworm@sha256:121d86b6d08752968a7dddbc708849e5f3a839bbff47f32212b46d2a1d842bab AS builder-cpu
|
||||
|
||||
FROM python:3.13-slim-trixie@sha256:d168b8d9eb761f4d3fe305ebd04aeb7e7f2de0297cec5fb2f8f6403244621664 AS builder-openvino
|
||||
FROM python:3.13-slim-trixie@sha256:b04b5d7233d2ad9c379e22ea8927cd1378cd15c60d4ef876c065b25ea8fb3bf3 AS builder-openvino
|
||||
|
||||
FROM builder-cpu AS builder-cuda
|
||||
|
||||
@@ -39,12 +39,12 @@ RUN --mount=type=cache,target=/root/.cache/uv \
|
||||
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
|
||||
uv sync --frozen --extra ${DEVICE} --no-dev --no-editable --no-install-project --compile-bytecode --no-progress --active --link-mode copy
|
||||
|
||||
FROM python:3.11-slim-bookworm@sha256:9c6f90801e6b68e772b7c0ca74260cbf7af9f320acec894e26fccdaccfbe3b47 AS prod-cpu
|
||||
FROM python:3.11-slim-bookworm@sha256:8dca233de9f3d9bb410665f00a4da6dd06f331083137e0e98ccf227236fcc438 AS prod-cpu
|
||||
|
||||
ENV LD_PRELOAD=/usr/lib/libmimalloc.so.2 \
|
||||
MACHINE_LEARNING_MODEL_ARENA=false
|
||||
|
||||
FROM python:3.13-slim-trixie@sha256:d168b8d9eb761f4d3fe305ebd04aeb7e7f2de0297cec5fb2f8f6403244621664 AS prod-openvino
|
||||
FROM python:3.13-slim-trixie@sha256:b04b5d7233d2ad9c379e22ea8927cd1378cd15c60d4ef876c065b25ea8fb3bf3 AS prod-openvino
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install --no-install-recommends -yqq ocl-icd-libopencl1 wget && \
|
||||
|
||||
@@ -49,6 +49,7 @@ try:
|
||||
str(settings.http_keepalive_timeout_s),
|
||||
"--graceful-timeout",
|
||||
"10",
|
||||
"--no-control-socket",
|
||||
],
|
||||
) as cmd:
|
||||
cmd.wait()
|
||||
|
||||
@@ -12,7 +12,7 @@ from zipfile import BadZipFile
|
||||
|
||||
import orjson
|
||||
from fastapi import Depends, FastAPI, File, Form, HTTPException
|
||||
from fastapi.responses import ORJSONResponse, PlainTextResponse
|
||||
from fastapi.responses import PlainTextResponse
|
||||
from onnxruntime.capi.onnxruntime_pybind11_state import InvalidProtobuf, NoSuchFile
|
||||
from PIL.Image import Image
|
||||
from pydantic import ValidationError
|
||||
@@ -32,6 +32,7 @@ from .schemas import (
|
||||
ModelIdentity,
|
||||
ModelTask,
|
||||
ModelType,
|
||||
ORJSONResponse,
|
||||
PipelineRequest,
|
||||
T,
|
||||
)
|
||||
|
||||
@@ -89,7 +89,9 @@ class OpenClipTextualEncoder(BaseCLIPTextualEncoder):
|
||||
|
||||
tokenizer: Tokenizer = Tokenizer.from_file(self.tokenizer_file_path.as_posix())
|
||||
|
||||
pad_id: int = tokenizer.token_to_id(pad_token)
|
||||
pad_id = tokenizer.token_to_id(pad_token)
|
||||
if pad_id is None:
|
||||
raise ValueError(f"Pad token '{pad_token}' not found in tokenizer vocab")
|
||||
tokenizer.enable_padding(length=context_length, pad_token=pad_token, pad_id=pad_id)
|
||||
tokenizer.enable_truncation(max_length=context_length)
|
||||
|
||||
|
||||
@@ -3,9 +3,16 @@ from typing import Any, Literal, Protocol, TypeGuard, TypeVar
|
||||
|
||||
import numpy as np
|
||||
import numpy.typing as npt
|
||||
import orjson
|
||||
from fastapi.responses import JSONResponse
|
||||
from typing_extensions import TypedDict
|
||||
|
||||
|
||||
class ORJSONResponse(JSONResponse):
|
||||
def render(self, content: Any) -> bytes:
|
||||
return orjson.dumps(content, option=orjson.OPT_SERIALIZE_NUMPY)
|
||||
|
||||
|
||||
class StrEnum(str, Enum):
|
||||
value: str
|
||||
|
||||
|
||||
Generated
+498
-450
File diff suppressed because it is too large
Load Diff
@@ -63,16 +63,19 @@ class SheetTile extends ConsumerWidget {
|
||||
subtitleWidget = null;
|
||||
}
|
||||
|
||||
return ListTile(
|
||||
dense: true,
|
||||
visualDensity: VisualDensity.compact,
|
||||
title: GestureDetector(onLongPress: () => copyTitle(context, ref), child: titleWidget),
|
||||
titleAlignment: ListTileTitleAlignment.center,
|
||||
leading: leading,
|
||||
trailing: trailing,
|
||||
contentPadding: leading == null ? null : const EdgeInsets.only(left: 25),
|
||||
subtitle: subtitleWidget,
|
||||
onTap: onTap,
|
||||
return Material(
|
||||
type: MaterialType.transparency,
|
||||
child: ListTile(
|
||||
dense: true,
|
||||
visualDensity: VisualDensity.compact,
|
||||
title: GestureDetector(onLongPress: () => copyTitle(context, ref), child: titleWidget),
|
||||
titleAlignment: ListTileTitleAlignment.center,
|
||||
leading: leading,
|
||||
trailing: trailing,
|
||||
contentPadding: leading == null ? null : const EdgeInsets.only(left: 25),
|
||||
subtitle: subtitleWidget,
|
||||
onTap: onTap,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,30 +47,10 @@ dynamic upgradeDto(dynamic value, String targetType) {
|
||||
addDefault(value, 'profileChangedAt', DateTime.now().toIso8601String());
|
||||
addDefault(value, 'hasProfileImage', false);
|
||||
}
|
||||
case 'AssetEditActionItemDtoParameters':
|
||||
if (value is Map) {
|
||||
addDefault(value, 'angle', 0);
|
||||
addDefault(value, 'x', 0);
|
||||
addDefault(value, 'y', 0);
|
||||
addDefault(value, 'width', 0);
|
||||
addDefault(value, 'height', 0);
|
||||
addDefault(value, 'axis', 'horizontal');
|
||||
}
|
||||
break;
|
||||
case 'SyncAssetV1':
|
||||
if (value is Map) {
|
||||
addDefault(value, 'isEdited', false);
|
||||
addDefault(value, 'createdAt', null);
|
||||
}
|
||||
case 'SyncAssetV2':
|
||||
if (value is Map) {
|
||||
addDefault(value, 'createdAt', null);
|
||||
}
|
||||
case 'ServerVersionResponseDto':
|
||||
if (value is Map) {
|
||||
addDefault(value, 'prerelease', null);
|
||||
}
|
||||
break;
|
||||
case 'ServerFeaturesDto':
|
||||
if (value is Map) {
|
||||
addDefault(value, 'ocr', false);
|
||||
|
||||
+22
-1
@@ -14,32 +14,52 @@ class PeopleResponse {
|
||||
/// Returns a new [PeopleResponse] instance.
|
||||
PeopleResponse({
|
||||
required this.enabled,
|
||||
this.minimumFaces,
|
||||
required this.sidebarWeb,
|
||||
});
|
||||
|
||||
/// Whether people are enabled
|
||||
bool enabled;
|
||||
|
||||
/// People face threshold
|
||||
///
|
||||
/// Minimum value: 1
|
||||
/// Maximum value: 9007199254740991
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
int? minimumFaces;
|
||||
|
||||
/// Whether people appear in web sidebar
|
||||
bool sidebarWeb;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is PeopleResponse &&
|
||||
other.enabled == enabled &&
|
||||
other.minimumFaces == minimumFaces &&
|
||||
other.sidebarWeb == sidebarWeb;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(enabled.hashCode) +
|
||||
(minimumFaces == null ? 0 : minimumFaces!.hashCode) +
|
||||
(sidebarWeb.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'PeopleResponse[enabled=$enabled, sidebarWeb=$sidebarWeb]';
|
||||
String toString() => 'PeopleResponse[enabled=$enabled, minimumFaces=$minimumFaces, sidebarWeb=$sidebarWeb]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'enabled'] = this.enabled;
|
||||
if (this.minimumFaces != null) {
|
||||
json[r'minimumFaces'] = this.minimumFaces;
|
||||
} else {
|
||||
// json[r'minimumFaces'] = null;
|
||||
}
|
||||
json[r'sidebarWeb'] = this.sidebarWeb;
|
||||
return json;
|
||||
}
|
||||
@@ -54,6 +74,7 @@ class PeopleResponse {
|
||||
|
||||
return PeopleResponse(
|
||||
enabled: mapValueOfType<bool>(json, r'enabled')!,
|
||||
minimumFaces: mapValueOfType<int>(json, r'minimumFaces'),
|
||||
sidebarWeb: mapValueOfType<bool>(json, r'sidebarWeb')!,
|
||||
);
|
||||
}
|
||||
|
||||
+22
-1
@@ -14,6 +14,7 @@ class PeopleUpdate {
|
||||
/// Returns a new [PeopleUpdate] instance.
|
||||
PeopleUpdate({
|
||||
this.enabled,
|
||||
this.minimumFaces,
|
||||
this.sidebarWeb,
|
||||
});
|
||||
|
||||
@@ -26,6 +27,18 @@ class PeopleUpdate {
|
||||
///
|
||||
bool? enabled;
|
||||
|
||||
/// People face threshold
|
||||
///
|
||||
/// Minimum value: 1
|
||||
/// Maximum value: 9007199254740991
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
int? minimumFaces;
|
||||
|
||||
/// Whether people appear in web sidebar
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
@@ -38,16 +51,18 @@ class PeopleUpdate {
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is PeopleUpdate &&
|
||||
other.enabled == enabled &&
|
||||
other.minimumFaces == minimumFaces &&
|
||||
other.sidebarWeb == sidebarWeb;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(enabled == null ? 0 : enabled!.hashCode) +
|
||||
(minimumFaces == null ? 0 : minimumFaces!.hashCode) +
|
||||
(sidebarWeb == null ? 0 : sidebarWeb!.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'PeopleUpdate[enabled=$enabled, sidebarWeb=$sidebarWeb]';
|
||||
String toString() => 'PeopleUpdate[enabled=$enabled, minimumFaces=$minimumFaces, sidebarWeb=$sidebarWeb]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
@@ -56,6 +71,11 @@ class PeopleUpdate {
|
||||
} else {
|
||||
// json[r'enabled'] = null;
|
||||
}
|
||||
if (this.minimumFaces != null) {
|
||||
json[r'minimumFaces'] = this.minimumFaces;
|
||||
} else {
|
||||
// json[r'minimumFaces'] = null;
|
||||
}
|
||||
if (this.sidebarWeb != null) {
|
||||
json[r'sidebarWeb'] = this.sidebarWeb;
|
||||
} else {
|
||||
@@ -74,6 +94,7 @@ class PeopleUpdate {
|
||||
|
||||
return PeopleUpdate(
|
||||
enabled: mapValueOfType<bool>(json, r'enabled'),
|
||||
minimumFaces: mapValueOfType<int>(json, r'minimumFaces'),
|
||||
sidebarWeb: mapValueOfType<bool>(json, r'sidebarWeb'),
|
||||
);
|
||||
}
|
||||
|
||||
+13
-1
@@ -20,6 +20,7 @@ class ServerConfigDto {
|
||||
required this.maintenanceMode,
|
||||
required this.mapDarkStyleUrl,
|
||||
required this.mapLightStyleUrl,
|
||||
required this.minFaces,
|
||||
required this.oauthButtonText,
|
||||
required this.publicUsers,
|
||||
required this.trashDays,
|
||||
@@ -47,6 +48,12 @@ class ServerConfigDto {
|
||||
/// Map light style URL
|
||||
String mapLightStyleUrl;
|
||||
|
||||
/// People min faces server default
|
||||
///
|
||||
/// Minimum value: -9007199254740991
|
||||
/// Maximum value: 9007199254740991
|
||||
int minFaces;
|
||||
|
||||
/// OAuth button text
|
||||
String oauthButtonText;
|
||||
|
||||
@@ -74,6 +81,7 @@ class ServerConfigDto {
|
||||
other.maintenanceMode == maintenanceMode &&
|
||||
other.mapDarkStyleUrl == mapDarkStyleUrl &&
|
||||
other.mapLightStyleUrl == mapLightStyleUrl &&
|
||||
other.minFaces == minFaces &&
|
||||
other.oauthButtonText == oauthButtonText &&
|
||||
other.publicUsers == publicUsers &&
|
||||
other.trashDays == trashDays &&
|
||||
@@ -89,13 +97,14 @@ class ServerConfigDto {
|
||||
(maintenanceMode.hashCode) +
|
||||
(mapDarkStyleUrl.hashCode) +
|
||||
(mapLightStyleUrl.hashCode) +
|
||||
(minFaces.hashCode) +
|
||||
(oauthButtonText.hashCode) +
|
||||
(publicUsers.hashCode) +
|
||||
(trashDays.hashCode) +
|
||||
(userDeleteDelay.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'ServerConfigDto[externalDomain=$externalDomain, isInitialized=$isInitialized, isOnboarded=$isOnboarded, loginPageMessage=$loginPageMessage, maintenanceMode=$maintenanceMode, mapDarkStyleUrl=$mapDarkStyleUrl, mapLightStyleUrl=$mapLightStyleUrl, oauthButtonText=$oauthButtonText, publicUsers=$publicUsers, trashDays=$trashDays, userDeleteDelay=$userDeleteDelay]';
|
||||
String toString() => 'ServerConfigDto[externalDomain=$externalDomain, isInitialized=$isInitialized, isOnboarded=$isOnboarded, loginPageMessage=$loginPageMessage, maintenanceMode=$maintenanceMode, mapDarkStyleUrl=$mapDarkStyleUrl, mapLightStyleUrl=$mapLightStyleUrl, minFaces=$minFaces, oauthButtonText=$oauthButtonText, publicUsers=$publicUsers, trashDays=$trashDays, userDeleteDelay=$userDeleteDelay]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
@@ -106,6 +115,7 @@ class ServerConfigDto {
|
||||
json[r'maintenanceMode'] = this.maintenanceMode;
|
||||
json[r'mapDarkStyleUrl'] = this.mapDarkStyleUrl;
|
||||
json[r'mapLightStyleUrl'] = this.mapLightStyleUrl;
|
||||
json[r'minFaces'] = this.minFaces;
|
||||
json[r'oauthButtonText'] = this.oauthButtonText;
|
||||
json[r'publicUsers'] = this.publicUsers;
|
||||
json[r'trashDays'] = this.trashDays;
|
||||
@@ -129,6 +139,7 @@ class ServerConfigDto {
|
||||
maintenanceMode: mapValueOfType<bool>(json, r'maintenanceMode')!,
|
||||
mapDarkStyleUrl: mapValueOfType<String>(json, r'mapDarkStyleUrl')!,
|
||||
mapLightStyleUrl: mapValueOfType<String>(json, r'mapLightStyleUrl')!,
|
||||
minFaces: mapValueOfType<int>(json, r'minFaces')!,
|
||||
oauthButtonText: mapValueOfType<String>(json, r'oauthButtonText')!,
|
||||
publicUsers: mapValueOfType<bool>(json, r'publicUsers')!,
|
||||
trashDays: mapValueOfType<int>(json, r'trashDays')!,
|
||||
@@ -187,6 +198,7 @@ class ServerConfigDto {
|
||||
'maintenanceMode',
|
||||
'mapDarkStyleUrl',
|
||||
'mapLightStyleUrl',
|
||||
'minFaces',
|
||||
'oauthButtonText',
|
||||
'publicUsers',
|
||||
'trashDays',
|
||||
|
||||
+10
-1
@@ -23,6 +23,7 @@ class ServerFeaturesDto {
|
||||
required this.oauthAutoLaunch,
|
||||
required this.ocr,
|
||||
required this.passwordLogin,
|
||||
required this.realtimeTranscoding,
|
||||
required this.reverseGeocoding,
|
||||
required this.search,
|
||||
required this.sidecar,
|
||||
@@ -60,6 +61,9 @@ class ServerFeaturesDto {
|
||||
/// Whether password login is enabled
|
||||
bool passwordLogin;
|
||||
|
||||
/// Whether real-time transcoding is enabled
|
||||
bool realtimeTranscoding;
|
||||
|
||||
/// Whether reverse geocoding is enabled
|
||||
bool reverseGeocoding;
|
||||
|
||||
@@ -87,6 +91,7 @@ class ServerFeaturesDto {
|
||||
other.oauthAutoLaunch == oauthAutoLaunch &&
|
||||
other.ocr == ocr &&
|
||||
other.passwordLogin == passwordLogin &&
|
||||
other.realtimeTranscoding == realtimeTranscoding &&
|
||||
other.reverseGeocoding == reverseGeocoding &&
|
||||
other.search == search &&
|
||||
other.sidecar == sidecar &&
|
||||
@@ -106,6 +111,7 @@ class ServerFeaturesDto {
|
||||
(oauthAutoLaunch.hashCode) +
|
||||
(ocr.hashCode) +
|
||||
(passwordLogin.hashCode) +
|
||||
(realtimeTranscoding.hashCode) +
|
||||
(reverseGeocoding.hashCode) +
|
||||
(search.hashCode) +
|
||||
(sidecar.hashCode) +
|
||||
@@ -113,7 +119,7 @@ class ServerFeaturesDto {
|
||||
(trash.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'ServerFeaturesDto[configFile=$configFile, duplicateDetection=$duplicateDetection, email=$email, facialRecognition=$facialRecognition, importFaces=$importFaces, map=$map, oauth=$oauth, oauthAutoLaunch=$oauthAutoLaunch, ocr=$ocr, passwordLogin=$passwordLogin, reverseGeocoding=$reverseGeocoding, search=$search, sidecar=$sidecar, smartSearch=$smartSearch, trash=$trash]';
|
||||
String toString() => 'ServerFeaturesDto[configFile=$configFile, duplicateDetection=$duplicateDetection, email=$email, facialRecognition=$facialRecognition, importFaces=$importFaces, map=$map, oauth=$oauth, oauthAutoLaunch=$oauthAutoLaunch, ocr=$ocr, passwordLogin=$passwordLogin, realtimeTranscoding=$realtimeTranscoding, reverseGeocoding=$reverseGeocoding, search=$search, sidecar=$sidecar, smartSearch=$smartSearch, trash=$trash]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
@@ -127,6 +133,7 @@ class ServerFeaturesDto {
|
||||
json[r'oauthAutoLaunch'] = this.oauthAutoLaunch;
|
||||
json[r'ocr'] = this.ocr;
|
||||
json[r'passwordLogin'] = this.passwordLogin;
|
||||
json[r'realtimeTranscoding'] = this.realtimeTranscoding;
|
||||
json[r'reverseGeocoding'] = this.reverseGeocoding;
|
||||
json[r'search'] = this.search;
|
||||
json[r'sidecar'] = this.sidecar;
|
||||
@@ -154,6 +161,7 @@ class ServerFeaturesDto {
|
||||
oauthAutoLaunch: mapValueOfType<bool>(json, r'oauthAutoLaunch')!,
|
||||
ocr: mapValueOfType<bool>(json, r'ocr')!,
|
||||
passwordLogin: mapValueOfType<bool>(json, r'passwordLogin')!,
|
||||
realtimeTranscoding: mapValueOfType<bool>(json, r'realtimeTranscoding')!,
|
||||
reverseGeocoding: mapValueOfType<bool>(json, r'reverseGeocoding')!,
|
||||
search: mapValueOfType<bool>(json, r'search')!,
|
||||
sidecar: mapValueOfType<bool>(json, r'sidecar')!,
|
||||
@@ -216,6 +224,7 @@ class ServerFeaturesDto {
|
||||
'oauthAutoLaunch',
|
||||
'ocr',
|
||||
'passwordLogin',
|
||||
'realtimeTranscoding',
|
||||
'reverseGeocoding',
|
||||
'search',
|
||||
'sidecar',
|
||||
|
||||
@@ -19907,6 +19907,12 @@
|
||||
"description": "Whether people are enabled",
|
||||
"type": "boolean"
|
||||
},
|
||||
"minimumFaces": {
|
||||
"description": "People face threshold",
|
||||
"maximum": 9007199254740991,
|
||||
"minimum": 1,
|
||||
"type": "integer"
|
||||
},
|
||||
"sidebarWeb": {
|
||||
"description": "Whether people appear in web sidebar",
|
||||
"type": "boolean"
|
||||
@@ -19968,6 +19974,12 @@
|
||||
"description": "Whether people are enabled",
|
||||
"type": "boolean"
|
||||
},
|
||||
"minimumFaces": {
|
||||
"description": "People face threshold",
|
||||
"maximum": 9007199254740991,
|
||||
"minimum": 1,
|
||||
"type": "integer"
|
||||
},
|
||||
"sidebarWeb": {
|
||||
"description": "Whether people appear in web sidebar",
|
||||
"type": "boolean"
|
||||
@@ -21604,6 +21616,12 @@
|
||||
"description": "Map light style URL",
|
||||
"type": "string"
|
||||
},
|
||||
"minFaces": {
|
||||
"description": "People min faces server default",
|
||||
"maximum": 9007199254740991,
|
||||
"minimum": -9007199254740991,
|
||||
"type": "integer"
|
||||
},
|
||||
"oauthButtonText": {
|
||||
"description": "OAuth button text",
|
||||
"type": "string"
|
||||
@@ -21633,6 +21651,7 @@
|
||||
"maintenanceMode",
|
||||
"mapDarkStyleUrl",
|
||||
"mapLightStyleUrl",
|
||||
"minFaces",
|
||||
"oauthButtonText",
|
||||
"publicUsers",
|
||||
"trashDays",
|
||||
@@ -21682,6 +21701,10 @@
|
||||
"description": "Whether password login is enabled",
|
||||
"type": "boolean"
|
||||
},
|
||||
"realtimeTranscoding": {
|
||||
"description": "Whether real-time transcoding is enabled",
|
||||
"type": "boolean"
|
||||
},
|
||||
"reverseGeocoding": {
|
||||
"description": "Whether reverse geocoding is enabled",
|
||||
"type": "boolean"
|
||||
@@ -21714,6 +21737,7 @@
|
||||
"oauthAutoLaunch",
|
||||
"ocr",
|
||||
"passwordLogin",
|
||||
"realtimeTranscoding",
|
||||
"reverseGeocoding",
|
||||
"search",
|
||||
"sidecar",
|
||||
|
||||
@@ -298,6 +298,8 @@ export type MemoriesResponse = {
|
||||
export type PeopleResponse = {
|
||||
/** Whether people are enabled */
|
||||
enabled: boolean;
|
||||
/** People face threshold */
|
||||
minimumFaces?: number;
|
||||
/** Whether people appear in web sidebar */
|
||||
sidebarWeb: boolean;
|
||||
};
|
||||
@@ -375,6 +377,8 @@ export type MemoriesUpdate = {
|
||||
export type PeopleUpdate = {
|
||||
/** Whether people are enabled */
|
||||
enabled?: boolean;
|
||||
/** People face threshold */
|
||||
minimumFaces?: number;
|
||||
/** Whether people appear in web sidebar */
|
||||
sidebarWeb?: boolean;
|
||||
};
|
||||
@@ -1963,6 +1967,8 @@ export type ServerConfigDto = {
|
||||
mapDarkStyleUrl: string;
|
||||
/** Map light style URL */
|
||||
mapLightStyleUrl: string;
|
||||
/** People min faces server default */
|
||||
minFaces: number;
|
||||
/** OAuth button text */
|
||||
oauthButtonText: string;
|
||||
/** Whether public user registration is enabled */
|
||||
@@ -1993,6 +1999,8 @@ export type ServerFeaturesDto = {
|
||||
ocr: boolean;
|
||||
/** Whether password login is enabled */
|
||||
passwordLogin: boolean;
|
||||
/** Whether real-time transcoding is enabled */
|
||||
realtimeTranscoding: boolean;
|
||||
/** Whether reverse geocoding is enabled */
|
||||
reverseGeocoding: boolean;
|
||||
/** Whether search is enabled */
|
||||
|
||||
Generated
+105
-157
@@ -615,7 +615,7 @@ importers:
|
||||
version: 10.0.1(eslint@10.4.0(jiti@2.7.0))
|
||||
'@nestjs/cli':
|
||||
specifier: ^11.0.2
|
||||
version: 11.0.21(@swc/core@1.15.33(@swc/helpers@0.5.22))(@types/node@24.12.4)(esbuild@0.28.0)(lightningcss@1.32.0)(prettier@3.8.3)
|
||||
version: 11.0.21(@swc/core@1.15.33(@swc/helpers@0.5.23))(@types/node@24.12.4)(esbuild@0.28.0)(lightningcss@1.32.0)(prettier@3.8.3)
|
||||
'@nestjs/schematics':
|
||||
specifier: ^11.0.0
|
||||
version: 11.1.0(chokidar@4.0.3)(prettier@3.8.3)(typescript@6.0.3)
|
||||
@@ -624,7 +624,7 @@ importers:
|
||||
version: 11.1.21(@nestjs/common@11.1.21(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.21)(@nestjs/platform-express@11.1.21)
|
||||
'@swc/core':
|
||||
specifier: ^1.4.14
|
||||
version: 1.15.33(@swc/helpers@0.5.22)
|
||||
version: 1.15.33(@swc/helpers@0.5.23)
|
||||
'@types/archiver':
|
||||
specifier: ^7.0.0
|
||||
version: 7.0.0
|
||||
@@ -695,8 +695,8 @@ importers:
|
||||
specifier: ^13.15.2
|
||||
version: 13.15.10
|
||||
'@vitest/coverage-v8':
|
||||
specifier: ^3.0.0
|
||||
version: 3.2.4(vitest@3.2.4(@types/debug@4.1.13)(@types/node@24.12.4)(happy-dom@20.9.0)(jiti@2.7.0)(jsdom@26.1.0(canvas@3.2.3))(lightningcss@1.32.0)(sass@1.99.0)(terser@5.47.1)(tsx@4.22.3)(yaml@2.9.0))
|
||||
specifier: ^4.0.0
|
||||
version: 4.1.7(vitest@3.2.4(@types/debug@4.1.13)(@types/node@24.12.4)(happy-dom@20.9.0)(jiti@2.7.0)(jsdom@26.1.0(canvas@3.2.3))(lightningcss@1.32.0)(sass@1.99.0)(terser@5.47.1)(tsx@4.22.3)(yaml@2.9.0))
|
||||
eslint:
|
||||
specifier: ^10.0.0
|
||||
version: 10.4.0(jiti@2.7.0)
|
||||
@@ -734,8 +734,8 @@ importers:
|
||||
specifier: ^3.4.0
|
||||
version: 3.4.19(tsx@4.22.3)(yaml@2.9.0)
|
||||
testcontainers:
|
||||
specifier: ^11.0.0
|
||||
version: 11.14.0
|
||||
specifier: ^12.0.0
|
||||
version: 12.0.1
|
||||
typescript:
|
||||
specifier: ^6.0.0
|
||||
version: 6.0.3
|
||||
@@ -744,7 +744,7 @@ importers:
|
||||
version: 8.59.4(eslint@10.4.0(jiti@2.7.0))(typescript@6.0.3)
|
||||
unplugin-swc:
|
||||
specifier: ^1.4.5
|
||||
version: 1.5.9(@swc/core@1.15.33(@swc/helpers@0.5.22))(rollup@4.60.4)
|
||||
version: 1.5.9(@swc/core@1.15.33(@swc/helpers@0.5.23))(rollup@4.60.4)
|
||||
vite-tsconfig-paths:
|
||||
specifier: ^6.0.0
|
||||
version: 6.1.1(typescript@6.0.3)(vite@8.0.13(@types/node@24.12.4)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(tsx@4.22.3)(yaml@2.9.0))
|
||||
@@ -765,7 +765,7 @@ importers:
|
||||
version: link:../packages/sdk
|
||||
'@immich/ui':
|
||||
specifier: ^0.79.2
|
||||
version: 0.79.2(@sveltejs/kit@2.60.1(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.55.8(@typescript-eslint/types@8.59.4))(vite@8.0.13(@types/node@24.12.4)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(tsx@4.22.3)(yaml@2.9.0)))(svelte@5.55.8(@typescript-eslint/types@8.59.4))(typescript@6.0.3)(vite@8.0.13(@types/node@24.12.4)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(tsx@4.22.3)(yaml@2.9.0)))(svelte@5.55.8(@typescript-eslint/types@8.59.4))
|
||||
version: 0.79.3(@sveltejs/kit@2.60.1(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.55.8(@typescript-eslint/types@8.59.4))(vite@8.0.13(@types/node@24.12.4)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(tsx@4.22.3)(yaml@2.9.0)))(svelte@5.55.8(@typescript-eslint/types@8.59.4))(typescript@6.0.3)(vite@8.0.13(@types/node@24.12.4)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(tsx@4.22.3)(yaml@2.9.0)))(svelte@5.55.8(@typescript-eslint/types@8.59.4))
|
||||
'@mapbox/mapbox-gl-rtl-text':
|
||||
specifier: 0.4.0
|
||||
version: 0.4.0
|
||||
@@ -820,6 +820,12 @@ importers:
|
||||
happy-dom:
|
||||
specifier: ^20.0.0
|
||||
version: 20.9.0
|
||||
hls-video-element:
|
||||
specifier: ^1.5.11
|
||||
version: 1.5.11
|
||||
hls.js:
|
||||
specifier: ^1.6.16
|
||||
version: 1.6.16
|
||||
intl-messageformat:
|
||||
specifier: ^11.0.0
|
||||
version: 11.2.6
|
||||
@@ -922,7 +928,7 @@ importers:
|
||||
version: 14.6.1(@testing-library/dom@10.4.1)
|
||||
'@trivago/prettier-plugin-sort-imports':
|
||||
specifier: ^6.0.2
|
||||
version: 6.0.2(prettier-plugin-svelte@3.5.2(prettier@3.8.3)(svelte@5.55.8(@typescript-eslint/types@8.59.4)))(prettier@3.8.3)(svelte@5.55.8(@typescript-eslint/types@8.59.4))
|
||||
version: 6.0.2(prettier-plugin-svelte@4.1.0(prettier@3.8.3)(svelte@5.55.8(@typescript-eslint/types@8.59.4)))(prettier@3.8.3)(svelte@5.55.8(@typescript-eslint/types@8.59.4))
|
||||
'@types/chromecast-caf-sender':
|
||||
specifier: ^1.0.11
|
||||
version: 1.0.11
|
||||
@@ -978,8 +984,8 @@ importers:
|
||||
specifier: ^4.1.1
|
||||
version: 4.2.0(prettier@3.8.3)
|
||||
prettier-plugin-svelte:
|
||||
specifier: ^3.3.3
|
||||
version: 3.5.2(prettier@3.8.3)(svelte@5.55.8(@typescript-eslint/types@8.59.4))
|
||||
specifier: ^4.0.0
|
||||
version: 4.1.0(prettier@3.8.3)(svelte@5.55.8(@typescript-eslint/types@8.59.4))
|
||||
rollup-plugin-visualizer:
|
||||
specifier: ^7.0.0
|
||||
version: 7.0.1(rolldown@1.0.1)(rollup@4.60.4)
|
||||
@@ -1104,10 +1110,6 @@ packages:
|
||||
resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
'@ampproject/remapping@2.3.0':
|
||||
resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
|
||||
'@angular-devkit/core@19.2.24':
|
||||
resolution: {integrity: sha512-Kd49warf6U/EyWe5BszF/eebN3zQ3bk7tgfEljAw8q/rX95UUtriJubWvp6pgzHfzBA4jwq8f+QiNZB8eBEXPA==}
|
||||
engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'}
|
||||
@@ -1693,10 +1695,6 @@ packages:
|
||||
peerDependencies:
|
||||
'@babel/core': ^7.0.0-0
|
||||
|
||||
'@babel/runtime@7.29.2':
|
||||
resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
'@babel/runtime@7.29.7':
|
||||
resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
@@ -3214,8 +3212,8 @@ packages:
|
||||
resolution: {integrity: sha512-O1SJ+BbeFVsUTF4af1MfagJZM+lPgLjI8lQ3SZNjpo8SGJReSbUl2ii03OKuGni/G0yp2GnRLpOTNSHYGtVrcg==}
|
||||
hasBin: true
|
||||
|
||||
'@immich/ui@0.79.2':
|
||||
resolution: {integrity: sha512-tnpYhYHrjrFJK18QglRMzPUtHv6q5V6tW38HiAraQJBv7MCg+yaJDrdF8omM2L5F311FGlv1PZLJYvmR4e49PA==}
|
||||
'@immich/ui@0.79.3':
|
||||
resolution: {integrity: sha512-QO2gLcmAIVLvcv3eb6RZ5kDahmVMDZlfE2RfbpyRKNI1wTjvTLP5ibIsofY0fXxLf44cN3vcjmz16CXS8bvVWQ==}
|
||||
peerDependencies:
|
||||
'@sveltejs/kit': ^2.13.0
|
||||
svelte: ^5.0.0
|
||||
@@ -3363,8 +3361,8 @@ packages:
|
||||
'@types/node':
|
||||
optional: true
|
||||
|
||||
'@internationalized/date@3.12.1':
|
||||
resolution: {integrity: sha512-6IedsVWXyq4P9Tj+TxuU8WGWM70hYLl12nbYU8jkikVpa6WXapFazPUcHUMDMoWftIDE2ILDkFFte6W2nFCkRQ==}
|
||||
'@internationalized/date@3.12.2':
|
||||
resolution: {integrity: sha512-FY1Y+H64NDs+HAF6omlnWxm3mEpfgaCSWtL5l551ZZfImA+kGjPFgrnJrGjH6lfmLL0g8Z/mBu1R3kufeCp6Jw==}
|
||||
|
||||
'@ioredis/commands@1.5.1':
|
||||
resolution: {integrity: sha512-JH8ZL/ywcJyR9MmJ5BNqZllXNZQqQbnVZOqpPQqE1vHiFgAw4NHbvE0FOduNU8IX9babitBT46571OnPTT0Zcw==}
|
||||
@@ -3377,10 +3375,6 @@ packages:
|
||||
resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
'@istanbuljs/schema@0.1.6':
|
||||
resolution: {integrity: sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
'@jest/schemas@29.6.3':
|
||||
resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==}
|
||||
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||
@@ -4993,8 +4987,8 @@ packages:
|
||||
'@swc/counter@0.1.3':
|
||||
resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==}
|
||||
|
||||
'@swc/helpers@0.5.22':
|
||||
resolution: {integrity: sha512-/e2Ly3Docn9kYByap6TV4oquJ3wQuz3c+kC74riqtkwU9CwTMeuj6t2rW+bRr4pyOx/CYQM4wr0RgaKQwGEz0A==}
|
||||
'@swc/helpers@0.5.23':
|
||||
resolution: {integrity: sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==}
|
||||
|
||||
'@swc/types@0.1.26':
|
||||
resolution: {integrity: sha512-lyMwd7WGgG79RS7EERZV3T8wMdmPq3xwyg+1nmAM64kIhx5yl+juO2PYIHb7vTiPgPCj8LYjsNV2T5wiQHUEaw==}
|
||||
@@ -5690,15 +5684,6 @@ packages:
|
||||
peerDependencies:
|
||||
valibot: ^1.4.0
|
||||
|
||||
'@vitest/coverage-v8@3.2.4':
|
||||
resolution: {integrity: sha512-EyF9SXU6kS5Ku/U82E259WSnvg6c8KTjppUncuNdm5QHpe17mwREHnjDzozC8x9MZ0xfBUFSaLkRv4TMA75ALQ==}
|
||||
peerDependencies:
|
||||
'@vitest/browser': 3.2.4
|
||||
vitest: 3.2.4
|
||||
peerDependenciesMeta:
|
||||
'@vitest/browser':
|
||||
optional: true
|
||||
|
||||
'@vitest/coverage-v8@4.1.7':
|
||||
resolution: {integrity: sha512-qsYPeXc5Q9dFLd1i8Ap+Bx8sQgcp+rFVQo4R0dDsWNBzl26ldVF1qOO+RL24K7FDrR6pA+50XedRLSoSG24bVQ==}
|
||||
peerDependencies:
|
||||
@@ -6049,9 +6034,6 @@ packages:
|
||||
ast-metadata-inferer@0.8.1:
|
||||
resolution: {integrity: sha512-ht3Dm6Zr7SXv6t1Ra6gFo0+kLDglHGrEbYihTkcycrbHw7WCcuhBzPlJYHEsIpycaUwzsJHje+vUcxXUX4ztTA==}
|
||||
|
||||
ast-v8-to-istanbul@0.3.12:
|
||||
resolution: {integrity: sha512-BRRC8VRZY2R4Z4lFIL35MwNXmwVqBityvOIwETtsCSwvjl0IdgFsy9NhdaA6j74nUdtJJlIypeRhpDam19Wq3g==}
|
||||
|
||||
ast-v8-to-istanbul@1.0.0:
|
||||
resolution: {integrity: sha512-1fSfIwuDICFA4LKkCzRPO7F0hzFf0B7+Xqrl27ynQaa+Rh0e1Es0v6kWHPott3lU10AyAr7oKHa65OppjLn3Rg==}
|
||||
|
||||
@@ -6947,6 +6929,9 @@ packages:
|
||||
csstype@3.2.3:
|
||||
resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
|
||||
|
||||
custom-media-element@1.4.6:
|
||||
resolution: {integrity: sha512-/HRYqJOa1ob5ik4q7FIJVYxTJCFs/FL3+cQPAJjUf2uiqrDEzbTgB315gQ2rG8oK3w094W9m5tcB8S5Qah+caA==}
|
||||
|
||||
cytoscape-cose-bilkent@4.1.0:
|
||||
resolution: {integrity: sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==}
|
||||
peerDependencies:
|
||||
@@ -7307,9 +7292,9 @@ packages:
|
||||
resolution: {integrity: sha512-XJgGhoR/CLpqshm4d3L7rzH6t8NgDFUIIpztYlLHIApeJjMZKYJMz2zxPsYxnejq5h3ELYSw/RBsi3t5h7gNTA==}
|
||||
engines: {node: '>= 8.0'}
|
||||
|
||||
dockerode@4.0.12:
|
||||
resolution: {integrity: sha512-/bCZd6KlGcjZO8Buqmi/vXuqEGVEZ0PNjx/biBNqJD3MhK9DmdiAuKxqfNhflgDESDIiBz3qF+0e55+CpnrUcw==}
|
||||
engines: {node: '>= 8.0'}
|
||||
dockerode@5.0.0:
|
||||
resolution: {integrity: sha512-C52mvJ+7lcyhWNfrzVfFsbTrBfy/ezE9FGEYLpu17FUeBcCkxERk9nN7uDl/478ynDiQ4U+5DbQC2vENHkVEtQ==}
|
||||
engines: {node: '>= 14.17'}
|
||||
|
||||
docusaurus-lunr-search@3.6.0:
|
||||
resolution: {integrity: sha512-CCEAnj5e67sUZmIb2hOl4xb4nDN07fb0fvRDDmdWlYpUvyS1CSKbw4lsGInLyUFEEEBzxQmT6zaVQdF/8Zretg==}
|
||||
@@ -8271,6 +8256,12 @@ packages:
|
||||
history@4.10.1:
|
||||
resolution: {integrity: sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==}
|
||||
|
||||
hls-video-element@1.5.11:
|
||||
resolution: {integrity: sha512-tJJ65/52CDxj8XFyIve6zT9nVVdUIc6mqvKR25X0ycPKHk07rpjp4xxVteeCefDUBSf/tFLhlICFmn3KWj37xA==}
|
||||
|
||||
hls.js@1.6.16:
|
||||
resolution: {integrity: sha512-VSIRpLfRwlAAdGL4wiTucx2ScRipo0ed1FBatWkyt832jC4CReKstga6yIhYVwGu9LOBjuX9wzmRMeQdBJtzEA==}
|
||||
|
||||
hogan.js@3.0.2:
|
||||
resolution: {integrity: sha512-RqGs4wavGYJWE07t35JQccByczmNUXQT0E12ZYV1VKYu5UiAU9lsos/yBAcf840+zrUQQxgVduCR5/B8nNtibg==}
|
||||
hasBin: true
|
||||
@@ -8713,10 +8704,6 @@ packages:
|
||||
resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
istanbul-lib-source-maps@5.0.6:
|
||||
resolution: {integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
istanbul-reports@3.2.0:
|
||||
resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -9167,9 +9154,6 @@ packages:
|
||||
magic-string@0.30.21:
|
||||
resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
|
||||
|
||||
magicast@0.3.5:
|
||||
resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==}
|
||||
|
||||
magicast@0.5.3:
|
||||
resolution: {integrity: sha512-pVKE4UdSQ7DvHzivsCIFx2BJn1mHG6KsyrFcaxFx6tONdneEuThrDx0Cj3AMg58KyN4pzYT+LHOotxDQDjNvkw==}
|
||||
|
||||
@@ -9275,6 +9259,9 @@ packages:
|
||||
media-chrome@4.19.0:
|
||||
resolution: {integrity: sha512-HWhDTwts+BSbdPkkB1VsJXp5kvL0IxY7xFT5tBwliM2+89kTPVTnHnev+9it2f9PweANjT/C8/C/S0PW9oyZbA==}
|
||||
|
||||
media-tracks@0.3.5:
|
||||
resolution: {integrity: sha512-l54rkKXlLBt3ob3zOLWHcnjvwUmX5bNEZ70igyapOZZC9imzqBmq1oz8p2roiV04KhjblFIi2hetLPF1oYVLRA==}
|
||||
|
||||
media-typer@0.3.0:
|
||||
resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==}
|
||||
engines: {node: '>= 0.6'}
|
||||
@@ -10691,11 +10678,12 @@ packages:
|
||||
peerDependencies:
|
||||
prettier: ^3.0.0
|
||||
|
||||
prettier-plugin-svelte@3.5.2:
|
||||
resolution: {integrity: sha512-ItFouLvzSFE3ulNl4DKoWM3BGcbDCNVpIyy/Y3F2gC3aNiGLxtFUdffVqO5Z5hhYG+DFT5KULWaxmeFFpdbvaQ==}
|
||||
prettier-plugin-svelte@4.1.0:
|
||||
resolution: {integrity: sha512-YZkhA2Q9oOerFFG9tq+2f98WYT7Z2JgrybJrAyrB78jpsH9i/DdgplXemehuFPgsldetFNCcR/yCcYlDjPy94Q==}
|
||||
engines: {node: '>=20'}
|
||||
peerDependencies:
|
||||
prettier: ^3.0.0
|
||||
svelte: ^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0
|
||||
svelte: ^5.0.0
|
||||
|
||||
prettier@3.8.3:
|
||||
resolution: {integrity: sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==}
|
||||
@@ -11868,12 +11856,8 @@ packages:
|
||||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
|
||||
test-exclude@7.0.2:
|
||||
resolution: {integrity: sha512-u9E6A+ZDYdp7a4WnarkXPZOx8Ilz46+kby6p1yZ8zsGTz9gYa6FIS7lj2oezzNKmtdyyJNNmmXDppga5GB7kSw==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
testcontainers@11.14.0:
|
||||
resolution: {integrity: sha512-r9pniwv/iwzyHaI7gwAvAm4Y+IvjJg3vBWdjrUCaDMc2AXIr4jKbq7jJO18Mw2ybs73pZy1Aj7p/4RVBGMRWjg==}
|
||||
testcontainers@12.0.1:
|
||||
resolution: {integrity: sha512-EMjjfMNJf3HlL7V3elkxqKUO1r3CtqNBTdmKGwwma/lOtUGfoWvFJ0WQ/KQf1DHEMnRjLWzW4cXbv/Tndsbcbw==}
|
||||
|
||||
text-decoder@1.2.7:
|
||||
resolution: {integrity: sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==}
|
||||
@@ -11963,8 +11947,8 @@ packages:
|
||||
resolution: {integrity: sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==}
|
||||
hasBin: true
|
||||
|
||||
tmp@0.2.5:
|
||||
resolution: {integrity: sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==}
|
||||
tmp@0.2.7:
|
||||
resolution: {integrity: sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==}
|
||||
engines: {node: '>=14.14'}
|
||||
|
||||
to-regex-range@5.0.1:
|
||||
@@ -12305,11 +12289,6 @@ packages:
|
||||
resolution: {integrity: sha512-6S5mCapmzcxetOD/2UEjL0GF5e4+gB07Dh8qs63xylw5ay4XuyW6iQs70FOJo/puf10LCkvhp4jYMQSDUBYEFg==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
|
||||
uuid@10.0.0:
|
||||
resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==}
|
||||
deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).
|
||||
hasBin: true
|
||||
|
||||
uuid@14.0.0:
|
||||
resolution: {integrity: sha512-Qo+uWgilfSmAhXCMav1uYFynlQO7fMFiMVZsQqZRMIXp0O7rR7qjkj+cPvBHLgBqi960QCoo/PH2/6ZtVqKvrg==}
|
||||
hasBin: true
|
||||
@@ -13012,11 +12991,6 @@ snapshots:
|
||||
|
||||
'@alloc/quick-lru@5.2.0': {}
|
||||
|
||||
'@ampproject/remapping@2.3.0':
|
||||
dependencies:
|
||||
'@jridgewell/gen-mapping': 0.3.13
|
||||
'@jridgewell/trace-mapping': 0.3.31
|
||||
|
||||
'@angular-devkit/core@19.2.24(chokidar@4.0.3)':
|
||||
dependencies:
|
||||
ajv: 8.18.0
|
||||
@@ -13801,8 +13775,6 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@babel/runtime@7.29.2': {}
|
||||
|
||||
'@babel/runtime@7.29.7': {}
|
||||
|
||||
'@babel/template@7.28.6':
|
||||
@@ -14246,7 +14218,7 @@ snapshots:
|
||||
'@babel/preset-env': 7.29.5(@babel/core@7.29.0)
|
||||
'@babel/preset-react': 7.28.5(@babel/core@7.29.0)
|
||||
'@babel/preset-typescript': 7.28.5(@babel/core@7.29.0)
|
||||
'@babel/runtime': 7.29.2
|
||||
'@babel/runtime': 7.29.7
|
||||
'@babel/traverse': 7.29.0
|
||||
'@docusaurus/logger': 3.10.1
|
||||
'@docusaurus/utils': 3.10.1(clean-css@5.3.3)(cssnano@6.1.2(postcss@8.5.15))(html-minifier-terser@7.2.0)(postcss@8.5.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
|
||||
@@ -14280,7 +14252,7 @@ snapshots:
|
||||
'@babel/preset-env': 7.29.5(@babel/core@7.29.0)
|
||||
'@babel/preset-react': 7.28.5(@babel/core@7.29.0)
|
||||
'@babel/preset-typescript': 7.28.5(@babel/core@7.29.0)
|
||||
'@babel/runtime': 7.29.2
|
||||
'@babel/runtime': 7.29.7
|
||||
'@babel/traverse': 7.29.0
|
||||
'@docusaurus/logger': 3.10.1
|
||||
'@docusaurus/utils': 3.10.1(postcss@8.5.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
|
||||
@@ -14386,7 +14358,7 @@ snapshots:
|
||||
react-router: 5.3.4(react@19.2.6)
|
||||
react-router-config: 5.1.1(react-router@5.3.4(react@19.2.6))(react@19.2.6)
|
||||
react-router-dom: 5.3.4(react@19.2.6)
|
||||
semver: 7.8.0
|
||||
semver: 7.8.1
|
||||
serve-handler: 6.1.7
|
||||
tinypool: 1.1.1
|
||||
tslib: 2.8.1
|
||||
@@ -15897,12 +15869,12 @@ snapshots:
|
||||
pg-connection-string: 2.13.0
|
||||
postgres: 3.4.9
|
||||
|
||||
'@immich/ui@0.79.2(@sveltejs/kit@2.60.1(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.55.8(@typescript-eslint/types@8.59.4))(vite@8.0.13(@types/node@24.12.4)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(tsx@4.22.3)(yaml@2.9.0)))(svelte@5.55.8(@typescript-eslint/types@8.59.4))(typescript@6.0.3)(vite@8.0.13(@types/node@24.12.4)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(tsx@4.22.3)(yaml@2.9.0)))(svelte@5.55.8(@typescript-eslint/types@8.59.4))':
|
||||
'@immich/ui@0.79.3(@sveltejs/kit@2.60.1(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.55.8(@typescript-eslint/types@8.59.4))(vite@8.0.13(@types/node@24.12.4)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(tsx@4.22.3)(yaml@2.9.0)))(svelte@5.55.8(@typescript-eslint/types@8.59.4))(typescript@6.0.3)(vite@8.0.13(@types/node@24.12.4)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(tsx@4.22.3)(yaml@2.9.0)))(svelte@5.55.8(@typescript-eslint/types@8.59.4))':
|
||||
dependencies:
|
||||
'@internationalized/date': 3.12.1
|
||||
'@internationalized/date': 3.12.2
|
||||
'@mdi/js': 7.4.47
|
||||
'@sveltejs/kit': 2.60.1(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.55.8(@typescript-eslint/types@8.59.4))(vite@8.0.13(@types/node@24.12.4)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(tsx@4.22.3)(yaml@2.9.0)))(svelte@5.55.8(@typescript-eslint/types@8.59.4))(typescript@6.0.3)(vite@8.0.13(@types/node@24.12.4)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(tsx@4.22.3)(yaml@2.9.0))
|
||||
bits-ui: 2.18.1(@internationalized/date@3.12.1)(@sveltejs/kit@2.60.1(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.55.8(@typescript-eslint/types@8.59.4))(vite@8.0.13(@types/node@24.12.4)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(tsx@4.22.3)(yaml@2.9.0)))(svelte@5.55.8(@typescript-eslint/types@8.59.4))(typescript@6.0.3)(vite@8.0.13(@types/node@24.12.4)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(tsx@4.22.3)(yaml@2.9.0)))(svelte@5.55.8(@typescript-eslint/types@8.59.4))
|
||||
bits-ui: 2.18.1(@internationalized/date@3.12.2)(@sveltejs/kit@2.60.1(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.55.8(@typescript-eslint/types@8.59.4))(vite@8.0.13(@types/node@24.12.4)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(tsx@4.22.3)(yaml@2.9.0)))(svelte@5.55.8(@typescript-eslint/types@8.59.4))(typescript@6.0.3)(vite@8.0.13(@types/node@24.12.4)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(tsx@4.22.3)(yaml@2.9.0)))(svelte@5.55.8(@typescript-eslint/types@8.59.4))
|
||||
luxon: 3.7.2
|
||||
simple-icons: 16.20.0
|
||||
svelte: 5.55.8(@typescript-eslint/types@8.59.4)
|
||||
@@ -16051,9 +16023,9 @@ snapshots:
|
||||
optionalDependencies:
|
||||
'@types/node': 24.12.4
|
||||
|
||||
'@internationalized/date@3.12.1':
|
||||
'@internationalized/date@3.12.2':
|
||||
dependencies:
|
||||
'@swc/helpers': 0.5.22
|
||||
'@swc/helpers': 0.5.23
|
||||
|
||||
'@ioredis/commands@1.5.1': {}
|
||||
|
||||
@@ -16070,8 +16042,6 @@ snapshots:
|
||||
dependencies:
|
||||
minipass: 7.1.3
|
||||
|
||||
'@istanbuljs/schema@0.1.6': {}
|
||||
|
||||
'@jest/schemas@29.6.3':
|
||||
dependencies:
|
||||
'@sinclair/typebox': 0.27.10
|
||||
@@ -16456,7 +16426,7 @@ snapshots:
|
||||
bullmq: 5.76.10
|
||||
tslib: 2.8.1
|
||||
|
||||
'@nestjs/cli@11.0.21(@swc/core@1.15.33(@swc/helpers@0.5.22))(@types/node@24.12.4)(esbuild@0.28.0)(lightningcss@1.32.0)(prettier@3.8.3)':
|
||||
'@nestjs/cli@11.0.21(@swc/core@1.15.33(@swc/helpers@0.5.23))(@types/node@24.12.4)(esbuild@0.28.0)(lightningcss@1.32.0)(prettier@3.8.3)':
|
||||
dependencies:
|
||||
'@angular-devkit/core': 19.2.24(chokidar@4.0.3)
|
||||
'@angular-devkit/schematics': 19.2.24(chokidar@4.0.3)
|
||||
@@ -16467,17 +16437,17 @@ snapshots:
|
||||
chokidar: 4.0.3
|
||||
cli-table3: 0.6.5
|
||||
commander: 4.1.1
|
||||
fork-ts-checker-webpack-plugin: 9.1.0(typescript@5.9.3)(webpack@5.106.0(@swc/core@1.15.33(@swc/helpers@0.5.22))(esbuild@0.28.0)(lightningcss@1.32.0))
|
||||
fork-ts-checker-webpack-plugin: 9.1.0(typescript@5.9.3)(webpack@5.106.0(@swc/core@1.15.33(@swc/helpers@0.5.23))(esbuild@0.28.0)(lightningcss@1.32.0))
|
||||
glob: 13.0.6
|
||||
node-emoji: 1.11.0
|
||||
ora: 5.4.1
|
||||
tsconfig-paths: 4.2.0
|
||||
tsconfig-paths-webpack-plugin: 4.2.0
|
||||
typescript: 5.9.3
|
||||
webpack: 5.106.0(@swc/core@1.15.33(@swc/helpers@0.5.22))(esbuild@0.28.0)(lightningcss@1.32.0)
|
||||
webpack: 5.106.0(@swc/core@1.15.33(@swc/helpers@0.5.23))(esbuild@0.28.0)(lightningcss@1.32.0)
|
||||
webpack-node-externals: 3.0.0
|
||||
optionalDependencies:
|
||||
'@swc/core': 1.15.33(@swc/helpers@0.5.22)
|
||||
'@swc/core': 1.15.33(@swc/helpers@0.5.23)
|
||||
transitivePeerDependencies:
|
||||
- '@minify-html/node'
|
||||
- '@swc/css'
|
||||
@@ -17666,7 +17636,7 @@ snapshots:
|
||||
'@swc/core-win32-x64-msvc@1.15.33':
|
||||
optional: true
|
||||
|
||||
'@swc/core@1.15.33(@swc/helpers@0.5.22)':
|
||||
'@swc/core@1.15.33(@swc/helpers@0.5.23)':
|
||||
dependencies:
|
||||
'@swc/counter': 0.1.3
|
||||
'@swc/types': 0.1.26
|
||||
@@ -17683,11 +17653,11 @@ snapshots:
|
||||
'@swc/core-win32-arm64-msvc': 1.15.33
|
||||
'@swc/core-win32-ia32-msvc': 1.15.33
|
||||
'@swc/core-win32-x64-msvc': 1.15.33
|
||||
'@swc/helpers': 0.5.22
|
||||
'@swc/helpers': 0.5.23
|
||||
|
||||
'@swc/counter@0.1.3': {}
|
||||
|
||||
'@swc/helpers@0.5.22':
|
||||
'@swc/helpers@0.5.23':
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
|
||||
@@ -17813,7 +17783,7 @@ snapshots:
|
||||
|
||||
'@tokenizer/token@0.3.0': {}
|
||||
|
||||
'@trivago/prettier-plugin-sort-imports@6.0.2(prettier-plugin-svelte@3.5.2(prettier@3.8.3)(svelte@5.55.8(@typescript-eslint/types@8.59.4)))(prettier@3.8.3)(svelte@5.55.8(@typescript-eslint/types@8.59.4))':
|
||||
'@trivago/prettier-plugin-sort-imports@6.0.2(prettier-plugin-svelte@4.1.0(prettier@3.8.3)(svelte@5.55.8(@typescript-eslint/types@8.59.4)))(prettier@3.8.3)(svelte@5.55.8(@typescript-eslint/types@8.59.4))':
|
||||
dependencies:
|
||||
'@babel/generator': 7.29.1
|
||||
'@babel/parser': 7.29.3
|
||||
@@ -17825,7 +17795,7 @@ snapshots:
|
||||
parse-imports-exports: 0.2.4
|
||||
prettier: 3.8.3
|
||||
optionalDependencies:
|
||||
prettier-plugin-svelte: 3.5.2(prettier@3.8.3)(svelte@5.55.8(@typescript-eslint/types@8.59.4))
|
||||
prettier-plugin-svelte: 4.1.0(prettier@3.8.3)(svelte@5.55.8(@typescript-eslint/types@8.59.4))
|
||||
svelte: 5.55.8(@typescript-eslint/types@8.59.4)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
@@ -18508,24 +18478,19 @@ snapshots:
|
||||
dependencies:
|
||||
valibot: 1.4.0(typescript@6.0.3)
|
||||
|
||||
'@vitest/coverage-v8@3.2.4(vitest@3.2.4(@types/debug@4.1.13)(@types/node@24.12.4)(happy-dom@20.9.0)(jiti@2.7.0)(jsdom@26.1.0(canvas@3.2.3))(lightningcss@1.32.0)(sass@1.99.0)(terser@5.47.1)(tsx@4.22.3)(yaml@2.9.0))':
|
||||
'@vitest/coverage-v8@4.1.7(vitest@3.2.4(@types/debug@4.1.13)(@types/node@24.12.4)(happy-dom@20.9.0)(jiti@2.7.0)(jsdom@26.1.0(canvas@3.2.3))(lightningcss@1.32.0)(sass@1.99.0)(terser@5.47.1)(tsx@4.22.3)(yaml@2.9.0))':
|
||||
dependencies:
|
||||
'@ampproject/remapping': 2.3.0
|
||||
'@bcoe/v8-coverage': 1.0.2
|
||||
ast-v8-to-istanbul: 0.3.12
|
||||
debug: 4.4.3
|
||||
'@vitest/utils': 4.1.7
|
||||
ast-v8-to-istanbul: 1.0.0
|
||||
istanbul-lib-coverage: 3.2.2
|
||||
istanbul-lib-report: 3.0.1
|
||||
istanbul-lib-source-maps: 5.0.6
|
||||
istanbul-reports: 3.2.0
|
||||
magic-string: 0.30.21
|
||||
magicast: 0.3.5
|
||||
std-env: 3.10.0
|
||||
test-exclude: 7.0.2
|
||||
tinyrainbow: 2.0.0
|
||||
magicast: 0.5.3
|
||||
obug: 2.1.1
|
||||
std-env: 4.1.0
|
||||
tinyrainbow: 3.1.0
|
||||
vitest: 3.2.4(@types/debug@4.1.13)(@types/node@24.12.4)(happy-dom@20.9.0)(jiti@2.7.0)(jsdom@26.1.0(canvas@3.2.3))(lightningcss@1.32.0)(sass@1.99.0)(terser@5.47.1)(tsx@4.22.3)(yaml@2.9.0)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@vitest/coverage-v8@4.1.7(vitest@4.1.7)':
|
||||
dependencies:
|
||||
@@ -18939,12 +18904,6 @@ snapshots:
|
||||
dependencies:
|
||||
'@mdn/browser-compat-data': 5.7.6
|
||||
|
||||
ast-v8-to-istanbul@0.3.12:
|
||||
dependencies:
|
||||
'@jridgewell/trace-mapping': 0.3.31
|
||||
estree-walker: 3.0.3
|
||||
js-tokens: 10.0.0
|
||||
|
||||
ast-v8-to-istanbul@1.0.0:
|
||||
dependencies:
|
||||
'@jridgewell/trace-mapping': 0.3.31
|
||||
@@ -19092,11 +19051,11 @@ snapshots:
|
||||
|
||||
binary-extensions@2.3.0: {}
|
||||
|
||||
bits-ui@2.18.1(@internationalized/date@3.12.1)(@sveltejs/kit@2.60.1(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.55.8(@typescript-eslint/types@8.59.4))(vite@8.0.13(@types/node@24.12.4)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(tsx@4.22.3)(yaml@2.9.0)))(svelte@5.55.8(@typescript-eslint/types@8.59.4))(typescript@6.0.3)(vite@8.0.13(@types/node@24.12.4)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(tsx@4.22.3)(yaml@2.9.0)))(svelte@5.55.8(@typescript-eslint/types@8.59.4)):
|
||||
bits-ui@2.18.1(@internationalized/date@3.12.2)(@sveltejs/kit@2.60.1(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.55.8(@typescript-eslint/types@8.59.4))(vite@8.0.13(@types/node@24.12.4)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(tsx@4.22.3)(yaml@2.9.0)))(svelte@5.55.8(@typescript-eslint/types@8.59.4))(typescript@6.0.3)(vite@8.0.13(@types/node@24.12.4)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(tsx@4.22.3)(yaml@2.9.0)))(svelte@5.55.8(@typescript-eslint/types@8.59.4)):
|
||||
dependencies:
|
||||
'@floating-ui/core': 1.7.5
|
||||
'@floating-ui/dom': 1.7.6
|
||||
'@internationalized/date': 3.12.1
|
||||
'@internationalized/date': 3.12.2
|
||||
esm-env: 1.2.2
|
||||
runed: 0.35.1(@sveltejs/kit@2.60.1(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.55.8(@typescript-eslint/types@8.59.4))(vite@8.0.13(@types/node@24.12.4)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(tsx@4.22.3)(yaml@2.9.0)))(svelte@5.55.8(@typescript-eslint/types@8.59.4))(typescript@6.0.3)(vite@8.0.13(@types/node@24.12.4)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(tsx@4.22.3)(yaml@2.9.0)))(svelte@5.55.8(@typescript-eslint/types@8.59.4))
|
||||
svelte: 5.55.8(@typescript-eslint/types@8.59.4)
|
||||
@@ -19869,6 +19828,8 @@ snapshots:
|
||||
|
||||
csstype@3.2.3: {}
|
||||
|
||||
custom-media-element@1.4.6: {}
|
||||
|
||||
cytoscape-cose-bilkent@4.1.0(cytoscape@3.33.4):
|
||||
dependencies:
|
||||
cose-base: 1.0.3
|
||||
@@ -20214,7 +20175,7 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
dockerode@4.0.12:
|
||||
dockerode@5.0.0:
|
||||
dependencies:
|
||||
'@balena/dockerignore': 1.0.2
|
||||
'@grpc/grpc-js': 1.14.3
|
||||
@@ -20222,7 +20183,6 @@ snapshots:
|
||||
docker-modem: 5.0.7
|
||||
protobufjs: 7.6.0
|
||||
tar-fs: 2.1.4
|
||||
uuid: 10.0.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@@ -20661,7 +20621,7 @@ snapshots:
|
||||
pluralize: 8.0.0
|
||||
regexp-tree: 0.1.27
|
||||
regjsparser: 0.13.1
|
||||
semver: 7.8.0
|
||||
semver: 7.8.1
|
||||
strip-indent: 4.1.1
|
||||
|
||||
eslint-scope@5.1.1:
|
||||
@@ -21103,7 +21063,7 @@ snapshots:
|
||||
cross-spawn: 7.0.6
|
||||
signal-exit: 4.1.0
|
||||
|
||||
fork-ts-checker-webpack-plugin@9.1.0(typescript@5.9.3)(webpack@5.106.0(@swc/core@1.15.33(@swc/helpers@0.5.22))(esbuild@0.28.0)(lightningcss@1.32.0)):
|
||||
fork-ts-checker-webpack-plugin@9.1.0(typescript@5.9.3)(webpack@5.106.0(@swc/core@1.15.33(@swc/helpers@0.5.23))(esbuild@0.28.0)(lightningcss@1.32.0)):
|
||||
dependencies:
|
||||
'@babel/code-frame': 7.29.0
|
||||
chalk: 4.1.2
|
||||
@@ -21118,7 +21078,7 @@ snapshots:
|
||||
semver: 7.8.1
|
||||
tapable: 2.3.3
|
||||
typescript: 5.9.3
|
||||
webpack: 5.106.0(@swc/core@1.15.33(@swc/helpers@0.5.22))(esbuild@0.28.0)(lightningcss@1.32.0)
|
||||
webpack: 5.106.0(@swc/core@1.15.33(@swc/helpers@0.5.23))(esbuild@0.28.0)(lightningcss@1.32.0)
|
||||
|
||||
form-data-encoder@2.1.4: {}
|
||||
|
||||
@@ -21558,6 +21518,14 @@ snapshots:
|
||||
tiny-warning: 1.0.3
|
||||
value-equal: 1.0.1
|
||||
|
||||
hls-video-element@1.5.11:
|
||||
dependencies:
|
||||
custom-media-element: 1.4.6
|
||||
hls.js: 1.6.16
|
||||
media-tracks: 0.3.5
|
||||
|
||||
hls.js@1.6.16: {}
|
||||
|
||||
hogan.js@3.0.2:
|
||||
dependencies:
|
||||
mkdirp: 0.3.0
|
||||
@@ -21982,14 +21950,6 @@ snapshots:
|
||||
make-dir: 4.0.0
|
||||
supports-color: 7.2.0
|
||||
|
||||
istanbul-lib-source-maps@5.0.6:
|
||||
dependencies:
|
||||
'@jridgewell/trace-mapping': 0.3.31
|
||||
debug: 4.4.3
|
||||
istanbul-lib-coverage: 3.2.2
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
istanbul-reports@3.2.0:
|
||||
dependencies:
|
||||
html-escaper: 2.0.2
|
||||
@@ -22407,12 +22367,6 @@ snapshots:
|
||||
dependencies:
|
||||
'@jridgewell/sourcemap-codec': 1.5.5
|
||||
|
||||
magicast@0.3.5:
|
||||
dependencies:
|
||||
'@babel/parser': 7.29.3
|
||||
'@babel/types': 7.29.0
|
||||
source-map-js: 1.2.1
|
||||
|
||||
magicast@0.5.3:
|
||||
dependencies:
|
||||
'@babel/parser': 7.29.3
|
||||
@@ -22661,6 +22615,8 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- react
|
||||
|
||||
media-tracks@0.3.5: {}
|
||||
|
||||
media-typer@0.3.0: {}
|
||||
|
||||
media-typer@1.1.0: {}
|
||||
@@ -24281,7 +24237,7 @@ snapshots:
|
||||
dependencies:
|
||||
prettier: 3.8.3
|
||||
|
||||
prettier-plugin-svelte@3.5.2(prettier@3.8.3)(svelte@5.55.8(@typescript-eslint/types@8.59.4)):
|
||||
prettier-plugin-svelte@4.1.0(prettier@3.8.3)(svelte@5.55.8(@typescript-eslint/types@8.59.4)):
|
||||
dependencies:
|
||||
prettier: 3.8.3
|
||||
svelte: 5.55.8(@typescript-eslint/types@8.59.4)
|
||||
@@ -24491,19 +24447,19 @@ snapshots:
|
||||
|
||||
react-loadable-ssr-addon-v5-slorber@1.0.3(@docusaurus/react-loadable@6.0.0(react@19.2.6))(webpack@5.107.0(postcss@8.5.15)):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.29.2
|
||||
'@babel/runtime': 7.29.7
|
||||
react-loadable: '@docusaurus/react-loadable@6.0.0(react@19.2.6)'
|
||||
webpack: 5.107.0(postcss@8.5.15)
|
||||
|
||||
react-router-config@5.1.1(react-router@5.3.4(react@19.2.6))(react@19.2.6):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.29.2
|
||||
'@babel/runtime': 7.29.7
|
||||
react: 19.2.6
|
||||
react-router: 5.3.4(react@19.2.6)
|
||||
|
||||
react-router-dom@5.3.4(react@19.2.6):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.29.2
|
||||
'@babel/runtime': 7.29.7
|
||||
history: 4.10.1
|
||||
loose-envify: 1.4.0
|
||||
prop-types: 15.8.1
|
||||
@@ -24514,7 +24470,7 @@ snapshots:
|
||||
|
||||
react-router@5.3.4(react@19.2.6):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.29.2
|
||||
'@babel/runtime': 7.29.7
|
||||
history: 4.10.1
|
||||
hoist-non-react-statics: 3.3.2
|
||||
loose-envify: 1.4.0
|
||||
@@ -25104,7 +25060,7 @@ snapshots:
|
||||
detect-libc: 2.1.2
|
||||
node-addon-api: 8.7.0
|
||||
node-gyp: 12.3.0
|
||||
semver: 7.8.0
|
||||
semver: 7.8.1
|
||||
optionalDependencies:
|
||||
'@img/sharp-darwin-arm64': 0.34.5
|
||||
'@img/sharp-darwin-x64': 0.34.5
|
||||
@@ -25782,15 +25738,15 @@ snapshots:
|
||||
- bare-abort-controller
|
||||
- react-native-b4a
|
||||
|
||||
terser-webpack-plugin@5.6.0(@swc/core@1.15.33(@swc/helpers@0.5.22))(esbuild@0.28.0)(lightningcss@1.32.0)(webpack@5.106.0(@swc/core@1.15.33(@swc/helpers@0.5.22))(esbuild@0.28.0)(lightningcss@1.32.0)):
|
||||
terser-webpack-plugin@5.6.0(@swc/core@1.15.33(@swc/helpers@0.5.23))(esbuild@0.28.0)(lightningcss@1.32.0)(webpack@5.106.0(@swc/core@1.15.33(@swc/helpers@0.5.23))(esbuild@0.28.0)(lightningcss@1.32.0)):
|
||||
dependencies:
|
||||
'@jridgewell/trace-mapping': 0.3.31
|
||||
jest-worker: 27.5.1
|
||||
schema-utils: 4.3.3
|
||||
terser: 5.47.1
|
||||
webpack: 5.106.0(@swc/core@1.15.33(@swc/helpers@0.5.22))(esbuild@0.28.0)(lightningcss@1.32.0)
|
||||
webpack: 5.106.0(@swc/core@1.15.33(@swc/helpers@0.5.23))(esbuild@0.28.0)(lightningcss@1.32.0)
|
||||
optionalDependencies:
|
||||
'@swc/core': 1.15.33(@swc/helpers@0.5.22)
|
||||
'@swc/core': 1.15.33(@swc/helpers@0.5.23)
|
||||
esbuild: 0.28.0
|
||||
lightningcss: 1.32.0
|
||||
|
||||
@@ -25824,13 +25780,7 @@ snapshots:
|
||||
commander: 2.20.3
|
||||
source-map-support: 0.5.21
|
||||
|
||||
test-exclude@7.0.2:
|
||||
dependencies:
|
||||
'@istanbuljs/schema': 0.1.6
|
||||
glob: 10.5.0
|
||||
minimatch: 10.2.5
|
||||
|
||||
testcontainers@11.14.0:
|
||||
testcontainers@12.0.1:
|
||||
dependencies:
|
||||
'@balena/dockerignore': 1.0.2
|
||||
'@types/dockerode': 4.0.1
|
||||
@@ -25839,13 +25789,13 @@ snapshots:
|
||||
byline: 5.0.0
|
||||
debug: 4.4.3
|
||||
docker-compose: 1.4.2
|
||||
dockerode: 4.0.12
|
||||
dockerode: 5.0.0
|
||||
get-port: 7.2.0
|
||||
proper-lockfile: 4.1.2
|
||||
properties-reader: 3.0.1
|
||||
ssh-remote-port-forward: 1.0.4
|
||||
tar-fs: 3.1.2
|
||||
tmp: 0.2.5
|
||||
tmp: 0.2.7
|
||||
undici: 7.25.0
|
||||
transitivePeerDependencies:
|
||||
- bare-abort-controller
|
||||
@@ -25926,7 +25876,7 @@ snapshots:
|
||||
tldts-core: 6.1.86
|
||||
optional: true
|
||||
|
||||
tmp@0.2.5: {}
|
||||
tmp@0.2.7: {}
|
||||
|
||||
to-regex-range@5.0.1:
|
||||
dependencies:
|
||||
@@ -26197,10 +26147,10 @@ snapshots:
|
||||
|
||||
unpipe@1.0.0: {}
|
||||
|
||||
unplugin-swc@1.5.9(@swc/core@1.15.33(@swc/helpers@0.5.22))(rollup@4.60.4):
|
||||
unplugin-swc@1.5.9(@swc/core@1.15.33(@swc/helpers@0.5.23))(rollup@4.60.4):
|
||||
dependencies:
|
||||
'@rollup/pluginutils': 5.3.0(rollup@4.60.4)
|
||||
'@swc/core': 1.15.33(@swc/helpers@0.5.22)
|
||||
'@swc/core': 1.15.33(@swc/helpers@0.5.23)
|
||||
load-tsconfig: 0.2.5
|
||||
unplugin: 2.3.11
|
||||
transitivePeerDependencies:
|
||||
@@ -26276,8 +26226,6 @@ snapshots:
|
||||
- encoding
|
||||
- supports-color
|
||||
|
||||
uuid@10.0.0: {}
|
||||
|
||||
uuid@14.0.0: {}
|
||||
|
||||
uuid@8.3.2: {}
|
||||
@@ -26599,7 +26547,7 @@ snapshots:
|
||||
|
||||
webpack-virtual-modules@0.6.2: {}
|
||||
|
||||
webpack@5.106.0(@swc/core@1.15.33(@swc/helpers@0.5.22))(esbuild@0.28.0)(lightningcss@1.32.0):
|
||||
webpack@5.106.0(@swc/core@1.15.33(@swc/helpers@0.5.23))(esbuild@0.28.0)(lightningcss@1.32.0):
|
||||
dependencies:
|
||||
'@types/eslint-scope': 3.7.7
|
||||
'@types/estree': 1.0.9
|
||||
@@ -26623,7 +26571,7 @@ snapshots:
|
||||
neo-async: 2.6.2
|
||||
schema-utils: 4.3.3
|
||||
tapable: 2.3.3
|
||||
terser-webpack-plugin: 5.6.0(@swc/core@1.15.33(@swc/helpers@0.5.22))(esbuild@0.28.0)(lightningcss@1.32.0)(webpack@5.106.0(@swc/core@1.15.33(@swc/helpers@0.5.22))(esbuild@0.28.0)(lightningcss@1.32.0))
|
||||
terser-webpack-plugin: 5.6.0(@swc/core@1.15.33(@swc/helpers@0.5.23))(esbuild@0.28.0)(lightningcss@1.32.0)(webpack@5.106.0(@swc/core@1.15.33(@swc/helpers@0.5.23))(esbuild@0.28.0)(lightningcss@1.32.0))
|
||||
watchpack: 2.5.1
|
||||
webpack-sources: 3.4.1
|
||||
transitivePeerDependencies:
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
FROM ghcr.io/immich-app/base-server-dev:202605051129@sha256:d07d8fcdb7e9f3ac22a811e87761ebf341ed0bb91956b89097540c2ed3fb9ca3 AS builder
|
||||
FROM ghcr.io/immich-app/base-server-dev:202606021219@sha256:63fa91aa011f6f2921dd32fe6d1be8d637e9bd7f3e3dd0c8e446afb31b282af4 AS builder
|
||||
ENV COREPACK_ENABLE_DOWNLOAD_PROMPT=0 \
|
||||
CI=1 \
|
||||
COREPACK_HOME=/tmp \
|
||||
@@ -80,7 +80,7 @@ RUN --mount=type=cache,id=pnpm-packages,target=/buildcache/pnpm-store \
|
||||
--mount=type=cache,id=mise-tools-${TARGETPLATFORM},target=/buildcache/mise \
|
||||
mise //:plugins
|
||||
|
||||
FROM ghcr.io/immich-app/base-server-prod:202605051129@sha256:50f7ffe4ed31e360c90c4905bd5f6658f2a121297544e3fe9368e338b3f76bcd
|
||||
FROM ghcr.io/immich-app/base-server-prod:202606021219@sha256:6ef9ef5859492149af770a6c884b5e2ddbaeef99f8885ea5f2d9f73625a3d9ec
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
ENV NODE_ENV=production \
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# dev build
|
||||
FROM ghcr.io/immich-app/base-server-dev:202605051129@sha256:d07d8fcdb7e9f3ac22a811e87761ebf341ed0bb91956b89097540c2ed3fb9ca3 AS dev
|
||||
FROM ghcr.io/immich-app/base-server-dev:202606021219@sha256:63fa91aa011f6f2921dd32fe6d1be8d637e9bd7f3e3dd0c8e446afb31b282af4 AS dev
|
||||
|
||||
|
||||
COPY --from=ghcr.io/jdx/mise:2026.5.18@sha256:5bb3311994fa78cef307ca3077cdb18f9551da0886371fc26ea91ab56220ffc5 /usr/local/bin/mise /usr/local/bin/mise
|
||||
|
||||
+2
-2
@@ -147,7 +147,7 @@
|
||||
"@types/supertest": "^7.0.0",
|
||||
"@types/ua-parser-js": "^0.7.36",
|
||||
"@types/validator": "^13.15.2",
|
||||
"@vitest/coverage-v8": "^3.0.0",
|
||||
"@vitest/coverage-v8": "^4.0.0",
|
||||
"eslint": "^10.0.0",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
@@ -160,7 +160,7 @@
|
||||
"sql-formatter": "^15.0.0",
|
||||
"supertest": "^7.1.0",
|
||||
"tailwindcss": "^3.4.0",
|
||||
"testcontainers": "^11.0.0",
|
||||
"testcontainers": "^12.0.0",
|
||||
"typescript": "^6.0.0",
|
||||
"typescript-eslint": "^8.28.0",
|
||||
"unplugin-swc": "^1.4.5",
|
||||
|
||||
@@ -124,6 +124,7 @@ const ServerConfigSchema = z
|
||||
mapDarkStyleUrl: z.string().describe('Map dark style URL'),
|
||||
mapLightStyleUrl: z.string().describe('Map light style URL'),
|
||||
maintenanceMode: z.boolean().describe('Whether maintenance mode is active'),
|
||||
minFaces: z.int().describe('People min faces server default'),
|
||||
})
|
||||
.meta({ id: 'ServerConfigDto' });
|
||||
|
||||
@@ -144,6 +145,7 @@ const ServerFeaturesSchema = z
|
||||
search: z.boolean().describe('Whether search is enabled'),
|
||||
email: z.boolean().describe('Whether email notifications are enabled'),
|
||||
ocr: z.boolean().describe('Whether OCR is enabled'),
|
||||
realtimeTranscoding: z.boolean().describe('Whether real-time transcoding is enabled'),
|
||||
})
|
||||
.meta({ id: 'ServerFeaturesDto' });
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ const PeopleUpdateSchema = z
|
||||
.object({
|
||||
enabled: z.boolean().optional().describe('Whether people are enabled'),
|
||||
sidebarWeb: z.boolean().optional().describe('Whether people appear in web sidebar'),
|
||||
minimumFaces: z.int().min(1).optional().describe('People face threshold'),
|
||||
})
|
||||
.optional()
|
||||
.meta({ id: 'PeopleUpdate' });
|
||||
@@ -138,6 +139,7 @@ const PeopleResponseSchema = z
|
||||
.object({
|
||||
enabled: z.boolean().describe('Whether people are enabled'),
|
||||
sidebarWeb: z.boolean().describe('Whether people appear in web sidebar'),
|
||||
minimumFaces: z.int().min(1).optional().describe('People face threshold'),
|
||||
})
|
||||
.meta({ id: 'PeopleResponse' });
|
||||
|
||||
|
||||
@@ -42,7 +42,18 @@ group by
|
||||
having
|
||||
(
|
||||
"person"."name" != $3
|
||||
or count("asset_face"."assetId") >= $4
|
||||
or count("asset_face"."assetId") >= COALESCE(
|
||||
(
|
||||
SELECT
|
||||
value -> 'people' ->> 'minimumFaces'
|
||||
FROM
|
||||
user_metadata
|
||||
WHERE
|
||||
"userId" = $4
|
||||
AND key = 'preferences'
|
||||
),
|
||||
'3'
|
||||
)::int
|
||||
)
|
||||
order by
|
||||
"person"."isHidden" asc,
|
||||
|
||||
@@ -4,7 +4,7 @@ import { jsonObjectFrom } from 'kysely/helpers/postgres';
|
||||
import { InjectKysely } from 'nestjs-kysely';
|
||||
import { AssetFace } from 'src/database';
|
||||
import { Chunked, ChunkedArray, DummyValue, GenerateSql } from 'src/decorators';
|
||||
import { AssetFileType, AssetVisibility, SourceType } from 'src/enum';
|
||||
import { AssetFileType, AssetVisibility, SourceType, UserMetadataKey } from 'src/enum';
|
||||
import { DB } from 'src/schema';
|
||||
import { AssetFaceTable } from 'src/schema/tables/asset-face.table';
|
||||
import { FaceSearchTable } from 'src/schema/tables/face-search.table';
|
||||
@@ -13,7 +13,6 @@ import { dummy, removeUndefinedKeys, withFilePath } from 'src/utils/database';
|
||||
import { paginationHelper, PaginationOptions } from 'src/utils/pagination';
|
||||
|
||||
export interface PersonSearchOptions {
|
||||
minimumFaceCount: number;
|
||||
withHidden: boolean;
|
||||
closestFaceAssetId?: string;
|
||||
}
|
||||
@@ -168,7 +167,17 @@ export class PersonRepository {
|
||||
.having((eb) =>
|
||||
eb.or([
|
||||
eb('person.name', '!=', ''),
|
||||
eb((innerEb) => innerEb.fn.count('asset_face.assetId'), '>=', options?.minimumFaceCount || 1),
|
||||
eb(
|
||||
(innerEb) => innerEb.fn.count('asset_face.assetId'),
|
||||
'>=',
|
||||
sql<number>`COALESCE(
|
||||
(SELECT value -> 'people' ->> 'minimumFaces'
|
||||
FROM user_metadata
|
||||
WHERE "userId" = ${userId}
|
||||
AND key = ${sql.lit(UserMetadataKey.Preferences)}),
|
||||
'3'
|
||||
)::int `,
|
||||
),
|
||||
]),
|
||||
)
|
||||
.groupBy('person.id')
|
||||
|
||||
@@ -57,7 +57,6 @@ describe(PersonService.name, () => {
|
||||
],
|
||||
});
|
||||
expect(mocks.person.getAllForUser).toHaveBeenCalledWith({ skip: 0, take: 10 }, auth.user.id, {
|
||||
minimumFaceCount: 3,
|
||||
withHidden: true,
|
||||
});
|
||||
});
|
||||
@@ -84,7 +83,6 @@ describe(PersonService.name, () => {
|
||||
],
|
||||
});
|
||||
expect(mocks.person.getAllForUser).toHaveBeenCalledWith({ skip: 0, take: 10 }, auth.user.id, {
|
||||
minimumFaceCount: 3,
|
||||
withHidden: false,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -63,9 +63,7 @@ export class PersonService extends BaseService {
|
||||
}
|
||||
closestFaceAssetId = person.faceAssetId;
|
||||
}
|
||||
const { machineLearning } = await this.getConfig({ withCache: false });
|
||||
const { items, hasNextPage } = await this.personRepository.getAllForUser(pagination, auth.user.id, {
|
||||
minimumFaceCount: machineLearning.facialRecognition.minFaces,
|
||||
withHidden,
|
||||
closestFaceAssetId,
|
||||
});
|
||||
|
||||
@@ -148,6 +148,7 @@ describe(ServerService.name, () => {
|
||||
configFile: false,
|
||||
trash: true,
|
||||
email: false,
|
||||
realtimeTranscoding: false,
|
||||
});
|
||||
expect(mocks.systemMetadata.get).toHaveBeenCalled();
|
||||
});
|
||||
@@ -167,6 +168,7 @@ describe(ServerService.name, () => {
|
||||
mapDarkStyleUrl: 'https://tiles.immich.cloud/v1/style/dark.json',
|
||||
mapLightStyleUrl: 'https://tiles.immich.cloud/v1/style/light.json',
|
||||
maintenanceMode: false,
|
||||
minFaces: 3,
|
||||
});
|
||||
expect(mocks.systemMetadata.get).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -86,7 +86,7 @@ export class ServerService extends BaseService {
|
||||
}
|
||||
|
||||
async getFeatures(): Promise<ServerFeaturesDto> {
|
||||
const { reverseGeocoding, metadata, map, machineLearning, trash, oauth, passwordLogin, notifications } =
|
||||
const { reverseGeocoding, metadata, map, machineLearning, trash, oauth, passwordLogin, notifications, ffmpeg } =
|
||||
await this.getConfig({ withCache: false });
|
||||
const { configFile } = this.configRepository.getEnv();
|
||||
|
||||
@@ -106,6 +106,7 @@ export class ServerService extends BaseService {
|
||||
passwordLogin: passwordLogin.enabled,
|
||||
configFile: !!configFile,
|
||||
email: notifications.smtp.enabled,
|
||||
realtimeTranscoding: ffmpeg.realtime.enabled,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -127,6 +128,7 @@ export class ServerService extends BaseService {
|
||||
mapDarkStyleUrl: config.map.darkStyle,
|
||||
mapLightStyleUrl: config.map.lightStyle,
|
||||
maintenanceMode: false,
|
||||
minFaces: config.machineLearning.facialRecognition.minFaces,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -539,6 +539,7 @@ export type UserPreferences = {
|
||||
people: {
|
||||
enabled: boolean;
|
||||
sidebarWeb: boolean;
|
||||
minimumFaces: number;
|
||||
};
|
||||
ratings: {
|
||||
enabled: boolean;
|
||||
|
||||
@@ -21,6 +21,7 @@ const getDefaultPreferences = (): UserPreferences => {
|
||||
people: {
|
||||
enabled: true,
|
||||
sidebarWeb: false,
|
||||
minimumFaces: 3,
|
||||
},
|
||||
sharedLinks: {
|
||||
enabled: true,
|
||||
|
||||
+3
-1
@@ -46,6 +46,8 @@
|
||||
"geojson": "^0.5.0",
|
||||
"handlebars": "^4.7.8",
|
||||
"happy-dom": "^20.0.0",
|
||||
"hls-video-element": "^1.5.11",
|
||||
"hls.js": "^1.6.16",
|
||||
"intl-messageformat": "^11.0.0",
|
||||
"justified-layout": "^4.1.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
@@ -101,7 +103,7 @@
|
||||
"happy-dom": "^20.0.0",
|
||||
"prettier": "^3.7.4",
|
||||
"prettier-plugin-sort-json": "^4.1.1",
|
||||
"prettier-plugin-svelte": "^3.3.3",
|
||||
"prettier-plugin-svelte": "^4.0.0",
|
||||
"rollup-plugin-visualizer": "^7.0.0",
|
||||
"svelte": "5.55.8",
|
||||
"svelte-check": "^4.4.6",
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
mdiLink,
|
||||
mdiLockOutline,
|
||||
mdiMagnify,
|
||||
mdiMapMarkerOutline,
|
||||
mdiMapOutline,
|
||||
mdiServer,
|
||||
mdiStateMachine,
|
||||
@@ -93,6 +94,11 @@ export const getPagesProvider = ($t: MessageFormatter) => {
|
||||
onAction: () => goto(Route.people()),
|
||||
$if: () => authManager.authenticated && authManager.preferences.people.enabled,
|
||||
},
|
||||
{
|
||||
title: $t('places'),
|
||||
icon: mdiMapMarkerOutline,
|
||||
onAction: () => goto(Route.places()),
|
||||
},
|
||||
{
|
||||
title: $t('shared_links'),
|
||||
icon: mdiLink,
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
import { assetViewerManager } from '$lib/managers/asset-viewer-manager.svelte';
|
||||
import { castManager } from '$lib/managers/cast-manager.svelte';
|
||||
import { autoPlayVideo, lang, loopVideo as loopVideoPreference } from '$lib/stores/preferences.store';
|
||||
import { getAssetMediaUrl, getAssetPlaybackUrl } from '$lib/utils';
|
||||
import { getAssetHlsSessionUrl, getAssetHlsUrl, getAssetMediaUrl, getAssetPlaybackUrl } from '$lib/utils';
|
||||
import { AssetMediaSize, type AssetResponseDto } from '@immich/sdk';
|
||||
import { Icon, LoadingSpinner } from '@immich/ui';
|
||||
import {
|
||||
@@ -21,6 +21,9 @@
|
||||
mdiVolumeMedium,
|
||||
mdiVolumeMute,
|
||||
} from '@mdi/js';
|
||||
import Hls, { AbrController, Events, type FragLoadedData, type FragLoadingData, type HlsConfig } from 'hls.js';
|
||||
import 'hls-video-element';
|
||||
import type HlsVideoElement from 'hls-video-element';
|
||||
import 'media-chrome/media-control-bar';
|
||||
import 'media-chrome/media-controller';
|
||||
import 'media-chrome/media-fullscreen-button';
|
||||
@@ -28,9 +31,10 @@
|
||||
import 'media-chrome/media-play-button';
|
||||
import 'media-chrome/media-playback-rate-button';
|
||||
import 'media-chrome/media-time-display';
|
||||
import 'media-chrome/media-time-range';
|
||||
import './immich-time-range';
|
||||
import 'media-chrome/media-volume-range';
|
||||
import 'media-chrome/menu/media-playback-rate-menu';
|
||||
import 'media-chrome/menu/media-rendition-menu';
|
||||
import 'media-chrome/menu/media-settings-menu';
|
||||
import 'media-chrome/menu/media-settings-menu-button';
|
||||
import 'media-chrome/menu/media-settings-menu-item';
|
||||
@@ -38,6 +42,8 @@
|
||||
import { useSwipe, type SwipeCustomEvent } from 'svelte-gestures';
|
||||
import { t } from 'svelte-i18n';
|
||||
import { fade } from 'svelte/transition';
|
||||
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
||||
import { mediaCapabilitiesManager } from '$lib/managers/media-capabilities-manager.svelte';
|
||||
|
||||
interface Props {
|
||||
asset: AssetResponseDto;
|
||||
@@ -69,14 +75,155 @@
|
||||
|
||||
let videoPlayer: HTMLVideoElement | undefined = $state();
|
||||
let isLoading = $state(true);
|
||||
let assetFileUrl = $derived(
|
||||
playOriginalVideo
|
||||
? getAssetMediaUrl({ id: assetId, size: AssetMediaSize.Original, cacheKey })
|
||||
: getAssetPlaybackUrl({ id: assetId, cacheKey }),
|
||||
);
|
||||
let assetFileUrl = $derived.by(() => {
|
||||
if (featureFlagsManager.value.realtimeTranscoding) {
|
||||
return getAssetHlsUrl(assetId);
|
||||
}
|
||||
|
||||
if (playOriginalVideo) {
|
||||
return getAssetMediaUrl({ id: assetId, size: AssetMediaSize.Original, cacheKey });
|
||||
}
|
||||
|
||||
return getAssetPlaybackUrl({ id: assetId, cacheKey });
|
||||
});
|
||||
const aspectRatio = $derived(asset.width && asset.height ? `${asset.width} / ${asset.height}` : undefined);
|
||||
let showVideo = $state(false);
|
||||
let hasFocused = $state(false);
|
||||
let activeSession: { assetId: string; id: string } | undefined;
|
||||
let rebuildCount = 0;
|
||||
|
||||
const MAX_REBUILDS = 1;
|
||||
const SESSION_ID_REGEX = /\/video\/stream\/([0-9a-f-]{36})\//;
|
||||
|
||||
// hls.js can abandon fetching an in-flight fragment if it thinks it'll take too long, in which case
|
||||
// it emergency switches to a different variant. This extends the delay even further due to
|
||||
// cold starting another transcode, so let the fragment finish and have steady ABR decide the next level.
|
||||
//
|
||||
// It can also emergency switch between fragments: while a switch's first segment is still loading,
|
||||
// it can run out of buffer and drop to a lower level for just one segment before continuing at the switched quality.
|
||||
// This can cause multiple redundant transcoding restarts when it occurs.
|
||||
// Hold the committed level until its first fragment lands, then resume normal ABR.
|
||||
class NoAbandonAbrController extends AbrController {
|
||||
private switchTarget = -1;
|
||||
|
||||
protected override onFragLoading(_event: Events.FRAG_LOADING, data: FragLoadingData) {
|
||||
if (data.frag.sn === 'initSegment') {
|
||||
this.switchTarget = data.frag.level;
|
||||
}
|
||||
}
|
||||
|
||||
protected override onFragLoaded(event: Events.FRAG_LOADED, data: FragLoadedData) {
|
||||
if (data.frag.sn !== 'initSegment') {
|
||||
this.switchTarget = -1;
|
||||
}
|
||||
super.onFragLoaded(event, data);
|
||||
}
|
||||
|
||||
override get nextAutoLevel(): number {
|
||||
const level = super.nextAutoLevel;
|
||||
const target = this.hls.levels[this.switchTarget];
|
||||
// Hold the committed level, but only while hls.js still considers it healthy.
|
||||
if (target && level < this.switchTarget && target.loadError === 0 && target.fragmentError === 0) {
|
||||
return this.switchTarget;
|
||||
}
|
||||
return level;
|
||||
}
|
||||
|
||||
override set nextAutoLevel(level: number) {
|
||||
super.nextAutoLevel = level;
|
||||
}
|
||||
}
|
||||
|
||||
const hlsConfig: Partial<HlsConfig> = {
|
||||
abrController: NoAbandonAbrController,
|
||||
highBufferWatchdogPeriod: 10,
|
||||
detectStallWithCurrentTimeMs: 10_000,
|
||||
maxBufferHole: 0.5,
|
||||
maxBufferLength: 30,
|
||||
maxMaxBufferLength: 60,
|
||||
fragLoadPolicy: {
|
||||
default: {
|
||||
maxTimeToFirstByteMs: 30_000,
|
||||
maxLoadTimeMs: 60_000,
|
||||
timeoutRetry: { maxNumRetry: 5, retryDelayMs: 100, maxRetryDelayMs: 0 },
|
||||
errorRetry: { maxNumRetry: 3, retryDelayMs: 1000, maxRetryDelayMs: 8000 },
|
||||
},
|
||||
},
|
||||
useMediaCapabilities: false,
|
||||
};
|
||||
|
||||
const releaseSession = () => {
|
||||
const session = activeSession;
|
||||
if (!session) {
|
||||
return;
|
||||
}
|
||||
activeSession = undefined;
|
||||
const url = getAssetHlsSessionUrl(session.assetId, session.id);
|
||||
void fetch(url, { method: 'DELETE' }).catch(() => console.warn('Failed to release HLS session', session));
|
||||
};
|
||||
|
||||
const isHlsElement = (el: HTMLVideoElement | undefined): el is HlsVideoElement => {
|
||||
return el?.tagName === 'HLS-VIDEO';
|
||||
};
|
||||
|
||||
const wireHlsListeners = (el: HlsVideoElement, assetId: string, resumeTime?: number) => {
|
||||
const api = el.api;
|
||||
if (!api) {
|
||||
return;
|
||||
}
|
||||
|
||||
// This is a hack to make the rendition menu use `api.currentLevel` instead of `api.nextLevel`.
|
||||
// `api.nextLevel` makes the player request the next segment followed by the current segment.
|
||||
// That backward request causes the server to restart transcoding for no reason.
|
||||
Object.defineProperty(api, 'nextLevel', {
|
||||
configurable: true,
|
||||
get: () => api.currentLevel,
|
||||
set: (level: number) => {
|
||||
api.currentLevel = level;
|
||||
},
|
||||
});
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
api.on(Hls.Events.MANIFEST_PARSED, async () => {
|
||||
// Defer hls.js's first fragment load until we filter out suboptimal variants
|
||||
api.stopLoad();
|
||||
const id = api.levels[0]?.url[0]?.match(SESSION_ID_REGEX)?.[1];
|
||||
if (id) {
|
||||
activeSession = { assetId, id };
|
||||
}
|
||||
|
||||
const keep = await mediaCapabilitiesManager.efficientLevels(api.levels);
|
||||
for (let i = api.levels.length - 1; i >= 0; i--) {
|
||||
if (!keep.has(i)) {
|
||||
api.removeLevel(i);
|
||||
}
|
||||
}
|
||||
|
||||
api.startLoad(resumeTime);
|
||||
});
|
||||
|
||||
api.on(Hls.Events.FRAG_LOADED, () => (rebuildCount = 0));
|
||||
|
||||
api.on(Hls.Events.ERROR, (_, data) => {
|
||||
// 404 on a fragment can mean the server-side session has expired. Refetch
|
||||
// master for a new session, but give up if it still 404s.
|
||||
if (
|
||||
!data.fatal ||
|
||||
data.details !== Hls.ErrorDetails.FRAG_LOAD_ERROR ||
|
||||
data.response?.code !== 404 ||
|
||||
rebuildCount++ >= MAX_REBUILDS
|
||||
) {
|
||||
console.error('HLS error', JSON.stringify(data));
|
||||
return;
|
||||
}
|
||||
console.warn('Error loading segment, starting new session');
|
||||
activeSession = undefined;
|
||||
resumeTime = el.currentTime;
|
||||
el.load();
|
||||
// wireHlsListeners must run after el.api is repopulated.
|
||||
queueMicrotask(() => wireHlsListeners(el, assetId, resumeTime));
|
||||
});
|
||||
};
|
||||
|
||||
onMount(() => {
|
||||
showVideo = true;
|
||||
@@ -84,10 +231,31 @@
|
||||
|
||||
$effect(() => {
|
||||
// reactive on `assetFileUrl` changes
|
||||
if (assetFileUrl) {
|
||||
if (videoPlayer && assetFileUrl) {
|
||||
hasFocused = false;
|
||||
videoPlayer?.load();
|
||||
rebuildCount = 0;
|
||||
releaseSession();
|
||||
if (isHlsElement(videoPlayer)) {
|
||||
videoPlayer.config = hlsConfig;
|
||||
videoPlayer.src = assetFileUrl;
|
||||
const el = videoPlayer;
|
||||
queueMicrotask(() => wireHlsListeners(el, assetId));
|
||||
} else {
|
||||
videoPlayer.load();
|
||||
}
|
||||
}
|
||||
return releaseSession;
|
||||
});
|
||||
|
||||
const onPagehide = (event: PageTransitionEvent) => {
|
||||
if (!event.persisted) {
|
||||
releaseSession();
|
||||
}
|
||||
};
|
||||
|
||||
$effect(() => {
|
||||
window.addEventListener('pagehide', onPagehide);
|
||||
return () => window.removeEventListener('pagehide', onPagehide);
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
@@ -144,6 +312,10 @@
|
||||
videoPlayer?.pause();
|
||||
}
|
||||
});
|
||||
|
||||
// The time is only refreshed on HLS fragment decode by default,
|
||||
// so manually emit events on seek to update it immediately.
|
||||
const onSeeking = (event: Event) => event.currentTarget?.dispatchEvent(new Event('timeupdate'));
|
||||
</script>
|
||||
|
||||
{#if showVideo}
|
||||
@@ -172,27 +344,51 @@
|
||||
style:aspect-ratio={aspectRatio}
|
||||
defaultduration={asset.duration! / 1000}
|
||||
>
|
||||
<video
|
||||
bind:this={videoPlayer}
|
||||
slot="media"
|
||||
loop={$loopVideoPreference && loopVideo}
|
||||
autoplay={$autoPlayVideo}
|
||||
disablePictureInPicture
|
||||
playsinline
|
||||
{...useSwipe(onSwipe)}
|
||||
class="h-full object-contain"
|
||||
oncanplay={(e) => handleCanPlay(e.currentTarget)}
|
||||
onended={onVideoEnded}
|
||||
onplaying={(e) => {
|
||||
if (!hasFocused) {
|
||||
e.currentTarget.focus();
|
||||
hasFocused = true;
|
||||
}
|
||||
}}
|
||||
onclose={onClose}
|
||||
poster={getAssetMediaUrl({ id: asset.id, size: AssetMediaSize.Preview, cacheKey })}
|
||||
src={assetFileUrl}
|
||||
></video>
|
||||
{#if featureFlagsManager.value.realtimeTranscoding}
|
||||
<hls-video
|
||||
bind:this={videoPlayer}
|
||||
slot="media"
|
||||
loop={$loopVideoPreference && loopVideo}
|
||||
autoplay={$autoPlayVideo}
|
||||
disablePictureInPicture
|
||||
playsinline
|
||||
{...useSwipe(onSwipe)}
|
||||
class="h-full object-contain"
|
||||
oncanplay={(e: Event) => handleCanPlay(e.currentTarget as HTMLVideoElement)}
|
||||
onended={onVideoEnded}
|
||||
onseeking={onSeeking}
|
||||
onplaying={(e: Event) => {
|
||||
if (!hasFocused) {
|
||||
(e.currentTarget as HTMLElement).focus();
|
||||
hasFocused = true;
|
||||
}
|
||||
}}
|
||||
onclose={onClose}
|
||||
poster={getAssetMediaUrl({ id: asset.id, size: AssetMediaSize.Preview, cacheKey })}
|
||||
></hls-video>
|
||||
{:else}
|
||||
<video
|
||||
bind:this={videoPlayer}
|
||||
slot="media"
|
||||
loop={$loopVideoPreference && loopVideo}
|
||||
autoplay={$autoPlayVideo}
|
||||
disablePictureInPicture
|
||||
playsinline
|
||||
{...useSwipe(onSwipe)}
|
||||
class="h-full object-contain"
|
||||
oncanplay={(e) => handleCanPlay(e.currentTarget)}
|
||||
onended={onVideoEnded}
|
||||
onseeking={onSeeking}
|
||||
onplaying={(e) => {
|
||||
if (!hasFocused) {
|
||||
e.currentTarget.focus();
|
||||
hasFocused = true;
|
||||
}
|
||||
}}
|
||||
onclose={onClose}
|
||||
poster={getAssetMediaUrl({ id: asset.id, size: AssetMediaSize.Preview, cacheKey })}
|
||||
></video>
|
||||
{/if}
|
||||
|
||||
{#if extendedControls}
|
||||
<media-settings-menu hidden anchor="auto" class="min-w-3xs rounded-xl border border-light-300 shadow-sm">
|
||||
@@ -205,6 +401,16 @@
|
||||
<span slot="title">{$t('media_chrome.playback_rate')}</span>
|
||||
</media-playback-rate-menu>
|
||||
</media-settings-menu-item>
|
||||
{#if featureFlagsManager.value.realtimeTranscoding}
|
||||
<media-settings-menu-item class="mx-1 rounded-lg p-1 ps-2">
|
||||
{$t('video_quality')}
|
||||
<Icon slot="suffix" icon={mdiChevronRight} class="m-2" />
|
||||
<media-rendition-menu slot="submenu" hidden>
|
||||
<Icon slot="back-icon" icon={mdiChevronLeft} class="m-2" />
|
||||
<span slot="title">{$t('video_quality')}</span>
|
||||
</media-rendition-menu>
|
||||
</media-settings-menu-item>
|
||||
{/if}
|
||||
</media-settings-menu>
|
||||
{/if}
|
||||
|
||||
@@ -238,7 +444,7 @@
|
||||
<media-settings-menu-button class="shrink-0 rounded-full p-2 outline-none"></media-settings-menu-button>
|
||||
{/if}
|
||||
</media-control-bar>
|
||||
<media-time-range class="h-8 w-full rounded-lg px-2 pb-3 outline-none"></media-time-range>
|
||||
<immich-time-range class="h-8 w-full rounded-lg px-2 pb-3 outline-none"></immich-time-range>
|
||||
</div>
|
||||
</media-controller>
|
||||
|
||||
@@ -248,7 +454,7 @@
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if assetViewerManager.isFaceEditMode}
|
||||
{#if assetViewerManager.isFaceEditMode && videoPlayer}
|
||||
<FaceEditor htmlElement={videoPlayer} {containerWidth} {containerHeight} {assetId} />
|
||||
{/if}
|
||||
{/if}
|
||||
@@ -291,12 +497,12 @@
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
media-time-range,
|
||||
immich-time-range,
|
||||
media-volume-range {
|
||||
--media-control-hover-background: none;
|
||||
}
|
||||
|
||||
media-time-range:hover,
|
||||
immich-time-range:hover,
|
||||
media-volume-range:hover {
|
||||
--media-range-thumb-opacity: 1;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
import { MediaUIEvents } from 'media-chrome/constants';
|
||||
import MediaTimeRange from 'media-chrome/media-time-range';
|
||||
|
||||
const COMMIT_DELAY_MS = 750;
|
||||
|
||||
/** Custom MediaTimeRange that only seeks after pointer release to avoid hammering the server.
|
||||
* Keyboard input uses timed debouncing instead since there's no release event. */
|
||||
class ImmichTimeRange extends MediaTimeRange {
|
||||
private seeking = false;
|
||||
private pending: number | undefined;
|
||||
private idleTimer: ReturnType<typeof setTimeout> | undefined;
|
||||
|
||||
override connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this.addEventListener('pointerdown', this.hold);
|
||||
this.addEventListener('keydown', this.hold);
|
||||
this.addEventListener('pointerup', this.release);
|
||||
this.addEventListener('pointercancel', this.release);
|
||||
this.addEventListener(MediaUIEvents.MEDIA_SEEK_REQUEST, this.intercept, { capture: true });
|
||||
}
|
||||
|
||||
private hold(event: Event) {
|
||||
if (event instanceof KeyboardEvent) {
|
||||
if (!this.keysUsed.includes(event.key)) {
|
||||
return;
|
||||
}
|
||||
clearTimeout(this.idleTimer);
|
||||
this.idleTimer = setTimeout(this.release, COMMIT_DELAY_MS);
|
||||
}
|
||||
this.seeking = true;
|
||||
}
|
||||
|
||||
private intercept(event: Event) {
|
||||
if (!this.seeking) {
|
||||
return; // not mid-scrub, or this is the request we replay in release()
|
||||
}
|
||||
this.pending = (event as CustomEvent<number>).detail;
|
||||
event.stopImmediatePropagation();
|
||||
}
|
||||
|
||||
private release() {
|
||||
clearTimeout(this.idleTimer);
|
||||
this.seeking = false;
|
||||
if (this.pending !== undefined) {
|
||||
const detail = this.pending;
|
||||
this.pending = undefined;
|
||||
this.dispatchEvent(new CustomEvent(MediaUIEvents.MEDIA_SEEK_REQUEST, { bubbles: true, composed: true, detail }));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!globalThis.customElements.get('immich-time-range')) {
|
||||
globalThis.customElements.define('immich-time-range', ImmichTimeRange);
|
||||
}
|
||||
@@ -78,6 +78,7 @@
|
||||
let mouseOver = $state(false);
|
||||
let loaded = $state(false);
|
||||
let thumbError = $state(false);
|
||||
let skipFade = $state(false);
|
||||
|
||||
let width = $derived(thumbnailSize || thumbnailWidth || 235);
|
||||
let height = $derived(thumbnailSize || thumbnailHeight || 235);
|
||||
@@ -252,7 +253,12 @@
|
||||
widthStyle="{width}px"
|
||||
heightStyle="{height}px"
|
||||
curve={selected}
|
||||
onComplete={(errored) => ((loaded = true), (thumbError = errored))}
|
||||
onComplete={(errored) => {
|
||||
const rect = element?.getBoundingClientRect();
|
||||
skipFade = !rect || rect.bottom < 0 || rect.top > window.innerHeight;
|
||||
loaded = true;
|
||||
thumbError = errored;
|
||||
}}
|
||||
/>
|
||||
{#if asset.isVideo}
|
||||
<div class="pointer-events-none absolute size-full group-focus-visible:rounded-lg">
|
||||
@@ -297,7 +303,10 @@
|
||||
<Thumbhash
|
||||
base64ThumbHash={asset.thumbhash}
|
||||
data-testid="thumbhash"
|
||||
class={['absolute top-0 object-cover group-focus-visible:rounded-lg', { 'rounded-xl': selected }]}
|
||||
class={[
|
||||
'absolute top-0 object-cover group-focus-visible:rounded-lg',
|
||||
{ 'rounded-xl': selected, hidden: skipFade },
|
||||
]}
|
||||
style="width: {width}px; height: {height}px"
|
||||
draggable="false"
|
||||
fadeOut
|
||||
|
||||
@@ -22,11 +22,16 @@
|
||||
import { getJustifiedLayoutFromAssets } from '$lib/utils/layout-utils';
|
||||
import { navigate } from '$lib/utils/navigation';
|
||||
import { isTimelineAsset, toTimelineAsset } from '$lib/utils/timeline-util';
|
||||
import { TUNABLES } from '$lib/utils/tunables';
|
||||
import { AssetVisibility, type AssetResponseDto } from '@immich/sdk';
|
||||
import { modalManager } from '@immich/ui';
|
||||
import { debounce } from 'lodash-es';
|
||||
import { t } from 'svelte-i18n';
|
||||
|
||||
const {
|
||||
TIMELINE: { INTERSECTION_EXPAND_TOP, INTERSECTION_EXPAND_BOTTOM },
|
||||
} = TUNABLES;
|
||||
|
||||
type Props = {
|
||||
assets: AssetResponseDto[];
|
||||
viewerAssets?: AssetResponseDto[];
|
||||
@@ -34,7 +39,7 @@
|
||||
disableAssetSelect?: boolean;
|
||||
showArchiveIcon?: boolean;
|
||||
viewport: Viewport;
|
||||
onIntersected?: (() => void) | undefined;
|
||||
onEndReached?: (() => void) | undefined;
|
||||
showAssetName?: boolean;
|
||||
onReload?: (() => void) | undefined;
|
||||
pageHeaderOffset?: number;
|
||||
@@ -50,7 +55,7 @@
|
||||
disableAssetSelect = false,
|
||||
showArchiveIcon = false,
|
||||
viewport,
|
||||
onIntersected = undefined,
|
||||
onEndReached = undefined,
|
||||
showAssetName = false,
|
||||
onReload = undefined,
|
||||
slidingWindowOffset = 0,
|
||||
@@ -70,24 +75,23 @@
|
||||
}),
|
||||
);
|
||||
|
||||
const getStyle = (i: number) => {
|
||||
const geo = geometry;
|
||||
return `top: ${geo.getTop(i)}px; left: ${geo.getLeft(i)}px; width: ${geo.getWidth(i)}px; height: ${geo.getHeight(i)}px;`;
|
||||
const getStyle = (index: number) => {
|
||||
return `top: ${geometry.getTop(index)}px; left: ${geometry.getLeft(index)}px; width: ${geometry.getWidth(index)}px; height: ${geometry.getHeight(index)}px;`;
|
||||
};
|
||||
|
||||
const isIntersecting = (i: number) => {
|
||||
const geo = geometry;
|
||||
const isInOrNearViewport = (index: number) => {
|
||||
const window = slidingWindow;
|
||||
const top = geo.getTop(i);
|
||||
return top + pageHeaderOffset < window.bottom && top + geo.getHeight(i) > window.top;
|
||||
const top = geometry.getTop(index);
|
||||
return top + pageHeaderOffset < window.bottom && top + geometry.getHeight(index) > window.top;
|
||||
};
|
||||
|
||||
let shiftKeyIsDown = $state(false);
|
||||
let lastAssetMouseEvent: TimelineAsset | null = $state(null);
|
||||
let scrollTop = $state(0);
|
||||
|
||||
let slidingWindow = $derived.by(() => {
|
||||
const top = (scrollTop || 0) - slidingWindowOffset;
|
||||
const bottom = top + viewport.height + slidingWindowOffset;
|
||||
const top = (scrollTop || 0) - slidingWindowOffset - INTERSECTION_EXPAND_TOP;
|
||||
const bottom = top + viewport.height + slidingWindowOffset + INTERSECTION_EXPAND_BOTTOM;
|
||||
return {
|
||||
top,
|
||||
bottom,
|
||||
@@ -101,17 +105,15 @@
|
||||
|
||||
const updateSlidingWindow = () => (scrollTop = document.scrollingElement?.scrollTop ?? 0);
|
||||
|
||||
const debouncedOnIntersected = debounce(() => onIntersected?.(), 750, { maxWait: 100, leading: true });
|
||||
const debouncedOnEndReached = debounce(() => onEndReached?.(), 750, { maxWait: 100, leading: true });
|
||||
|
||||
let lastIntersectedHeight = 0;
|
||||
let lastEndReachedHeight = 0;
|
||||
$effect(() => {
|
||||
// Intersect if there's only one viewport worth of assets left to scroll.
|
||||
if (geometry.containerHeight - slidingWindow.bottom <= viewport.height) {
|
||||
// Notify we got to (near) the end of scroll.
|
||||
const intersectedHeight = geometry.containerHeight;
|
||||
if (lastIntersectedHeight !== intersectedHeight) {
|
||||
debouncedOnIntersected();
|
||||
lastIntersectedHeight = intersectedHeight;
|
||||
const contentHeight = geometry.containerHeight;
|
||||
if (lastEndReachedHeight !== contentHeight) {
|
||||
debouncedOnEndReached();
|
||||
lastEndReachedHeight = contentHeight;
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -362,10 +364,10 @@
|
||||
style:height={geometry.containerHeight + 'px'}
|
||||
style:width={geometry.containerWidth + 'px'}
|
||||
>
|
||||
{#each assets as asset, i (asset.id + '-' + i)}
|
||||
{#if isIntersecting(i)}
|
||||
{#each assets as asset, index (asset.id + '-' + index)}
|
||||
{#if isInOrNearViewport(index)}
|
||||
{@const currentAsset = toTimelineAsset(asset)}
|
||||
<div class="absolute" style:overflow="clip" style={getStyle(i)}>
|
||||
<div class="absolute" style:overflow="clip" style={getStyle(index)}>
|
||||
<Thumbnail
|
||||
readonly={disableAssetSelect}
|
||||
onClick={() => {
|
||||
@@ -382,8 +384,8 @@
|
||||
asset={currentAsset}
|
||||
selected={assetInteraction.hasSelectedAsset(currentAsset.id)}
|
||||
selectionCandidate={assetInteraction.hasSelectionCandidate(currentAsset.id)}
|
||||
thumbnailWidth={geometry.getWidth(i)}
|
||||
thumbnailHeight={geometry.getHeight(i)}
|
||||
thumbnailWidth={geometry.getWidth(index)}
|
||||
thumbnailHeight={geometry.getHeight(index)}
|
||||
/>
|
||||
{#if showAssetName && !isTimelineAsset(asset)}
|
||||
<div
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
export type Level = { videoCodec?: string; width: number; height: number; bitrate: number; frameRate: number };
|
||||
|
||||
export const DEFAULT_DECODING_INFO: MediaCapabilitiesDecodingInfo = {
|
||||
powerEfficient: true,
|
||||
smooth: true,
|
||||
supported: true,
|
||||
keySystemAccess: null,
|
||||
};
|
||||
|
||||
class MediaCapabilitiesManager {
|
||||
private cache = new Map<string, Promise<MediaCapabilitiesDecodingInfo>>();
|
||||
|
||||
init() {
|
||||
for (const level of [
|
||||
{ videoCodec: 'av01.0.04M.08', width: 854, height: 480, bitrate: 1_000_000, frameRate: 60 },
|
||||
{ videoCodec: 'hvc1.1.6.L90.B0', width: 854, height: 480, bitrate: 1_200_000, frameRate: 60 },
|
||||
{ videoCodec: 'av01.0.08M.08', width: 1280, height: 720, bitrate: 2_000_000, frameRate: 60 },
|
||||
{ videoCodec: 'hvc1.1.6.L93.B0', width: 1280, height: 720, bitrate: 2_500_000, frameRate: 60 },
|
||||
{ videoCodec: 'av01.0.09M.08', width: 1920, height: 1080, bitrate: 4_000_000, frameRate: 60 },
|
||||
{ videoCodec: 'hvc1.1.6.L120.B0', width: 1920, height: 1080, bitrate: 4_500_000, frameRate: 60 },
|
||||
{ videoCodec: 'av01.0.12M.08', width: 2560, height: 1440, bitrate: 7_000_000, frameRate: 60 },
|
||||
{ videoCodec: 'hvc1.2.4.L150.B0', width: 2560, height: 1440, bitrate: 8_000_000, frameRate: 60 },
|
||||
]) {
|
||||
this.cache.set(this.cacheKey(level), this.queryDecodingInfo(level));
|
||||
}
|
||||
|
||||
for (const level of [
|
||||
{ videoCodec: 'avc1.64001e', width: 854, height: 480, bitrate: 2_500_000, frameRate: 60 },
|
||||
{ videoCodec: 'avc1.64001f', width: 1280, height: 720, bitrate: 5_000_000, frameRate: 60 },
|
||||
{ videoCodec: 'avc1.640028', width: 1920, height: 1080, bitrate: 8_000_000, frameRate: 60 },
|
||||
{ videoCodec: 'avc1.640032', width: 2560, height: 1440, bitrate: 16_000_000, frameRate: 60 },
|
||||
]) {
|
||||
this.cache.set(this.cacheKey(level), Promise.resolve(DEFAULT_DECODING_INFO));
|
||||
}
|
||||
}
|
||||
|
||||
async efficientLevels(levels: Level[]) {
|
||||
const decodingInfo = await Promise.all(levels.map((level) => this.decodingInfo(level)));
|
||||
// eslint-disable-next-line svelte/prefer-svelte-reactivity
|
||||
const lowestBitrateByHeight = new Map<number, number>();
|
||||
for (let i = 0; i < levels.length; i++) {
|
||||
if (!decodingInfo[i].powerEfficient) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const { bitrate, height } = levels[i];
|
||||
const cur = lowestBitrateByHeight.get(height);
|
||||
if (cur === undefined || bitrate < levels[cur].bitrate) {
|
||||
lowestBitrateByHeight.set(height, i);
|
||||
}
|
||||
}
|
||||
|
||||
return new Set(lowestBitrateByHeight.values());
|
||||
}
|
||||
|
||||
decodingInfo(level: Level) {
|
||||
const key = this.cacheKey(level);
|
||||
const existing = this.cache.get(key);
|
||||
if (existing) {
|
||||
return existing;
|
||||
}
|
||||
const promise = this.queryDecodingInfo(level);
|
||||
this.cache.set(key, promise);
|
||||
return promise;
|
||||
}
|
||||
|
||||
private async queryDecodingInfo(level: Level) {
|
||||
try {
|
||||
return await navigator.mediaCapabilities.decodingInfo({
|
||||
type: 'media-source',
|
||||
video: {
|
||||
contentType: `video/mp4; codecs="${level.videoCodec}"`,
|
||||
width: level.width,
|
||||
height: level.height,
|
||||
bitrate: level.bitrate,
|
||||
framerate: level.frameRate,
|
||||
},
|
||||
});
|
||||
} catch {
|
||||
return DEFAULT_DECODING_INFO;
|
||||
}
|
||||
}
|
||||
|
||||
private cacheKey({ videoCodec, width, height, frameRate }: Level) {
|
||||
const resolution = Math.min(width, height);
|
||||
const fpsBucket = Math.trunc(frameRate / 61) * 60;
|
||||
return `${videoCodec}|${resolution}|${fpsBucket}`;
|
||||
}
|
||||
}
|
||||
|
||||
export const mediaCapabilitiesManager = new MediaCapabilitiesManager();
|
||||
mediaCapabilitiesManager.init();
|
||||
@@ -244,6 +244,14 @@ export const getAssetPlaybackUrl = (options: AssetUrlOptions) => {
|
||||
return createUrl(getAssetPlaybackPath(id), { ...authManager.params, c });
|
||||
};
|
||||
|
||||
export const getAssetHlsUrl = (id: string) => {
|
||||
return createUrl(`/assets/${id}/video/stream/main.m3u8`, authManager.params);
|
||||
};
|
||||
|
||||
export const getAssetHlsSessionUrl = (id: string, sessionId: string) => {
|
||||
return createUrl(`/assets/${id}/video/stream/${sessionId}`, authManager.params);
|
||||
};
|
||||
|
||||
export const getProfileImageUrl = (user: UserResponseDto) =>
|
||||
createUrl(getUserProfileImagePath(user.id), { updatedAt: user.profileChangedAt });
|
||||
|
||||
|
||||
@@ -309,7 +309,7 @@
|
||||
<GalleryViewer
|
||||
assets={searchResultAssets}
|
||||
assetInteraction={assetMultiSelectManager}
|
||||
onIntersected={loadNextPage}
|
||||
onEndReached={loadNextPage}
|
||||
showArchiveIcon={true}
|
||||
{viewport}
|
||||
onReload={onSearchQueryUpdate}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { serverConfigManager } from '$lib/managers/server-config-manager.svelte';
|
||||
import SettingAccordion from '$lib/components/shared-components/settings/SettingAccordion.svelte';
|
||||
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
@@ -21,6 +22,7 @@
|
||||
// People
|
||||
let peopleEnabled = $state(authManager.preferences.people?.enabled ?? false);
|
||||
let peopleSidebar = $state(authManager.preferences.people?.sidebarWeb ?? false);
|
||||
let peopleMinFaces = $state(authManager.preferences.people?.minimumFaces ?? serverConfigManager.value.minFaces);
|
||||
|
||||
// Ratings
|
||||
let ratingsEnabled = $state(authManager.preferences.ratings?.enabled ?? false);
|
||||
@@ -43,7 +45,7 @@
|
||||
albums: { defaultAssetOrder },
|
||||
folders: { enabled: foldersEnabled, sidebarWeb: foldersSidebar },
|
||||
memories: { enabled: memoriesEnabled, duration: memoriesDuration },
|
||||
people: { enabled: peopleEnabled, sidebarWeb: peopleSidebar },
|
||||
people: { enabled: peopleEnabled, sidebarWeb: peopleSidebar, minimumFaces: peopleMinFaces },
|
||||
ratings: { enabled: ratingsEnabled },
|
||||
sharedLinks: { enabled: sharedLinksEnabled, sidebarWeb: sharedLinkSidebar },
|
||||
tags: { enabled: tagsEnabled, sidebarWeb: tagsSidebar },
|
||||
@@ -117,6 +119,9 @@
|
||||
<Field label={$t('sidebar')} description={$t('sidebar_display_description')}>
|
||||
<Switch bind:checked={peopleSidebar} />
|
||||
</Field>
|
||||
<Field label={$t('minFaces')} description={$t('minFaces_description')}>
|
||||
<NumberInput bind:value={peopleMinFaces} />
|
||||
</Field>
|
||||
{/if}
|
||||
</div>
|
||||
</SettingAccordion>
|
||||
|
||||
@@ -38,6 +38,8 @@
|
||||
withCoordinates: true,
|
||||
};
|
||||
|
||||
const isOwnAsset = (asset: TimelineAsset) => asset.ownerId === authManager.user.id;
|
||||
|
||||
const handleUpdate = async () => {
|
||||
if (!point) {
|
||||
return;
|
||||
@@ -54,7 +56,7 @@
|
||||
|
||||
await updateAssets({
|
||||
assetBulkUpdateDto: {
|
||||
ids: assetMultiSelectManager.assets.map((asset) => asset.id),
|
||||
ids: assetMultiSelectManager.assets.filter((asset) => isOwnAsset(asset)).map((asset) => asset.id),
|
||||
latitude: point.lat,
|
||||
longitude: point.lng,
|
||||
},
|
||||
@@ -124,7 +126,7 @@
|
||||
}, 1500);
|
||||
point = { lat: asset.latitude, lng: asset.longitude };
|
||||
void setQueryValue('at', asset.id);
|
||||
} else {
|
||||
} else if (isOwnAsset(asset)) {
|
||||
onClick(timelineManager, timelineDay.getAssets(), timelineDay.groupTitle, asset);
|
||||
}
|
||||
};
|
||||
@@ -199,6 +201,9 @@
|
||||
onThumbnailClick={handleThumbnailClick}
|
||||
>
|
||||
{#snippet customThumbnailLayout(asset: TimelineAsset)}
|
||||
{#if !isOwnAsset(asset)}
|
||||
<div class="pointer-events-none absolute inset-0 rounded-sm bg-black/40"></div>
|
||||
{/if}
|
||||
{#if hasGps(asset)}
|
||||
<div class="absolute inset-e-3 bottom-1 rounded-xl bg-success px-4 py-1 text-xs text-black transition-colors">
|
||||
{asset.city || $t('gps')}
|
||||
|
||||
Reference in New Issue
Block a user