mirror of
https://github.com/immich-app/immich.git
synced 2026-07-21 21:34:17 +03:00
Compare commits
96 Commits
fix-29727
...
fix/admin-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4bd2154cbc | ||
|
|
d5adfb97dd | ||
|
|
b70cb58bc8 | ||
|
|
943bfafb7a | ||
|
|
ee4bd3f833 | ||
|
|
7a7303aceb | ||
|
|
0a7c067b20 | ||
|
|
c8de56c422 | ||
|
|
0e4572c37b | ||
|
|
14a2776453 | ||
|
|
59bc81423c | ||
|
|
e6fff3b15e | ||
|
|
755df23b22 | ||
|
|
899f547053 | ||
|
|
4938fd4c89 | ||
|
|
e81a79224c | ||
|
|
7b0f58b2c1 | ||
|
|
ae8398ffe4 | ||
|
|
aa08dad1f5 | ||
|
|
ce022233ae | ||
|
|
1b4d41324e | ||
|
|
8365cdd59e | ||
|
|
1cec7021e8 | ||
|
|
1b0f473457 | ||
|
|
e918658cc1 | ||
|
|
2e587fc7e8 | ||
|
|
df970da59e | ||
|
|
8061a2e5ff | ||
|
|
77091b0107 | ||
|
|
4c754f2999 | ||
|
|
6fa3f2feac | ||
|
|
3f6897ef80 | ||
|
|
89d0a9d59f | ||
|
|
45a6ea84af | ||
|
|
bf64f3867b | ||
|
|
4a4d468aa2 | ||
|
|
522def1ed6 | ||
|
|
3adc3920fb | ||
|
|
00cb50cc67 | ||
|
|
f2b0b696f6 | ||
|
|
b5401eb120 | ||
|
|
12fc8bac18 | ||
|
|
f2c00c107d | ||
|
|
f19f30ec66 | ||
|
|
297316c7b8 | ||
|
|
f18065b3c2 | ||
|
|
a3dd19cd2f | ||
|
|
9cea3f2375 | ||
|
|
507448d797 | ||
|
|
fe3eb7d865 | ||
|
|
90dc328c22 | ||
|
|
a5623c41ec | ||
|
|
3a016f4451 | ||
|
|
0af8284456 | ||
|
|
ad7a9bd189 | ||
|
|
80cdfebbc7 | ||
|
|
c50a807bc1 | ||
|
|
26e07548a7 | ||
|
|
f73c53c563 | ||
|
|
d2744f52b1 | ||
|
|
d8ed7d7bb9 | ||
|
|
834cc54abc | ||
|
|
eda605c534 | ||
|
|
b75e6520bb | ||
|
|
4c2d2284aa | ||
|
|
5d29f3edc1 | ||
|
|
e00dcf7850 | ||
|
|
e7f773197f | ||
|
|
2237078bd5 | ||
|
|
55dc579059 | ||
|
|
b4530b0671 | ||
|
|
a04e2ddeca | ||
|
|
5f2e8a4fcf | ||
|
|
dc55b234d9 | ||
|
|
f6bf22d9ca | ||
|
|
2cdbd0d00f | ||
|
|
cd308ad930 | ||
|
|
12061f3bf8 | ||
|
|
2460d431af | ||
|
|
24556cd3fc | ||
|
|
c84ab54889 | ||
|
|
19e59ebd98 | ||
|
|
89b0d906e8 | ||
|
|
3c43e8d6c1 | ||
|
|
2ed8b2bddd | ||
|
|
9c8d718ddc | ||
|
|
20e123c839 | ||
|
|
35fcca6254 | ||
|
|
ffafc144c6 | ||
|
|
89d31aaba1 | ||
|
|
f2ddace584 | ||
|
|
99883096d6 | ||
|
|
84dff19ca9 | ||
|
|
da8774801b | ||
|
|
9057ae9759 | ||
|
|
557189d7a8 |
@@ -83,7 +83,7 @@
|
||||
}
|
||||
},
|
||||
"features": {
|
||||
"ghcr.io/devcontainers/features/docker-in-docker:2": {
|
||||
"ghcr.io/devcontainers/features/docker-in-docker:4": {
|
||||
// https://github.com/devcontainers/features/issues/1466
|
||||
"moby": false
|
||||
}
|
||||
|
||||
2
.github/workflows/auto-close.yml
vendored
2
.github/workflows/auto-close.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
||||
outputs:
|
||||
uses_template: ${{ steps.check.outputs.uses_template }}
|
||||
steps:
|
||||
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
sparse-checkout: .github/pull_request_template.md
|
||||
sparse-checkout-cone-mode: false
|
||||
|
||||
24
.github/workflows/build-mobile.yml
vendored
24
.github/workflows/build-mobile.yml
vendored
@@ -51,10 +51,11 @@ jobs:
|
||||
should_run: ${{ steps.check.outputs.should_run }}
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- name: Check what should run
|
||||
id: check
|
||||
@@ -79,12 +80,14 @@ jobs:
|
||||
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
permission-pull-requests: write
|
||||
|
||||
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
ref: ${{ inputs.ref }}
|
||||
persist-credentials: false
|
||||
@@ -103,14 +106,14 @@ jobs:
|
||||
working-directory: ./mobile
|
||||
run: printf "%s" $KEY_JKS | base64 -d > android/key.jks
|
||||
|
||||
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0
|
||||
- uses: actions/setup-java@0f481fcb613427c0f801b606911222b5b6f3083a # v5.5.0
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: '17'
|
||||
|
||||
- name: Restore Gradle Cache
|
||||
id: cache-gradle-restore
|
||||
uses: actions/cache/restore@caa296126883cff596d87d8935842f9db880ef25 # v5.1.0
|
||||
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
@@ -181,7 +184,7 @@ jobs:
|
||||
|
||||
- name: Save Gradle Cache
|
||||
id: cache-gradle-save
|
||||
uses: actions/cache/save@caa296126883cff596d87d8935842f9db880ef25 # v5.1.0
|
||||
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
||||
if: github.ref == 'refs/heads/main'
|
||||
with:
|
||||
path: |
|
||||
@@ -198,20 +201,21 @@ jobs:
|
||||
contents: read
|
||||
# Run on main branch or workflow_dispatch, or on PRs/other branches (build only, no upload)
|
||||
if: ${{ !github.event.pull_request.head.repo.fork && fromJSON(needs.pre-job.outputs.should_run).mobile == true }}
|
||||
runs-on: macos-15
|
||||
runs-on: macos-26
|
||||
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- name: Select Xcode 26
|
||||
run: sudo xcode-select -s /Applications/Xcode_26.2.app/Contents/Developer
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
ref: ${{ inputs.ref || github.sha }}
|
||||
persist-credentials: false
|
||||
@@ -237,7 +241,7 @@ jobs:
|
||||
run: flutter build ios --config-only --no-codesign
|
||||
|
||||
- name: Setup Ruby
|
||||
uses: ruby/setup-ruby@0dafeac902942906541bc140009cdbf32665b601 # v1.315.0
|
||||
uses: ruby/setup-ruby@d45b1a4e94b71acab930e56e79c6aa188764e7f9 # v1.316.0
|
||||
with:
|
||||
ruby-version: '3.3'
|
||||
bundler-cache: true
|
||||
|
||||
6
.github/workflows/cache-cleanup.yml
vendored
6
.github/workflows/cache-cleanup.yml
vendored
@@ -19,13 +19,15 @@ jobs:
|
||||
actions: write
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
permission-actions: write
|
||||
|
||||
- name: Check out code
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
token: ${{ steps.token.outputs.token }}
|
||||
|
||||
6
.github/workflows/check-openapi.yml
vendored
6
.github/workflows/check-openapi.yml
vendored
@@ -20,12 +20,12 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Check for breaking API changes
|
||||
uses: oasdiff/oasdiff-action/breaking@ae5ef6402ebe218ac6e872c59d0fa4557694e75c # v0.1.4
|
||||
uses: oasdiff/oasdiff-action/breaking@291b6594ce617f22f49af5898ee038ed0b46e8fe # v0.1.5
|
||||
with:
|
||||
base: https://raw.githubusercontent.com/${{ github.repository }}/main/open-api/immich-openapi-specs.json
|
||||
revision: open-api/immich-openapi-specs.json
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
|
||||
12
.github/workflows/cli.yml
vendored
12
.github/workflows/cli.yml
vendored
@@ -31,13 +31,14 @@ jobs:
|
||||
working-directory: ./packages/cli
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
token: ${{ steps.token.outputs.token }}
|
||||
@@ -63,13 +64,14 @@ jobs:
|
||||
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
token: ${{ steps.token.outputs.token }}
|
||||
@@ -81,7 +83,7 @@ jobs:
|
||||
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@c99871dec2022cc055c062a10cc1a1310835ceb4 # v4.3.0
|
||||
uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0
|
||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||
with:
|
||||
registry: ghcr.io
|
||||
|
||||
2
.github/workflows/close-duplicates.yml
vendored
2
.github/workflows/close-duplicates.yml
vendored
@@ -39,7 +39,7 @@ jobs:
|
||||
needs: [get_body, should_run]
|
||||
if: ${{ needs.should_run.outputs.should_run == 'true' }}
|
||||
container:
|
||||
image: ghcr.io/immich-app/mdq:main@sha256:2a64c7f045cb7b580fbdf3614d7d1805f5775fec453e3d1023764180efa8c70b
|
||||
image: ghcr.io/immich-app/mdq:main@sha256:e3fe456f346b02c2f479462100763aee3ba8245768dfeac3c4262acb068e5c62
|
||||
outputs:
|
||||
checked: ${{ steps.get_checkbox.outputs.checked }}
|
||||
steps:
|
||||
|
||||
11
.github/workflows/codeql-analysis.yml
vendored
11
.github/workflows/codeql-analysis.yml
vendored
@@ -44,20 +44,21 @@ jobs:
|
||||
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
token: ${{ steps.token.outputs.token }}
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@54f647b7e1bb85c95cddabcd46b0c578ec92bc1a # v4.36.3
|
||||
uses: github/codeql-action/init@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
@@ -70,7 +71,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@54f647b7e1bb85c95cddabcd46b0c578ec92bc1a # v4.36.3
|
||||
uses: github/codeql-action/autobuild@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.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 +84,6 @@ jobs:
|
||||
# ./location_of_script_within_repo/buildscript.sh
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@54f647b7e1bb85c95cddabcd46b0c578ec92bc1a # v4.36.3
|
||||
uses: github/codeql-action/analyze@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0
|
||||
with:
|
||||
category: '/language:${{matrix.language}}'
|
||||
|
||||
7
.github/workflows/docker.yml
vendored
7
.github/workflows/docker.yml
vendored
@@ -23,10 +23,11 @@ jobs:
|
||||
should_run: ${{ steps.check.outputs.should_run }}
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- name: Check what should run
|
||||
id: check
|
||||
@@ -60,7 +61,7 @@ jobs:
|
||||
suffix: ['', '-cuda', '-rocm', '-openvino', '-armnn', '-rknn']
|
||||
steps:
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@c99871dec2022cc055c062a10cc1a1310835ceb4 # v4.3.0
|
||||
uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
@@ -90,7 +91,7 @@ jobs:
|
||||
suffix: ['']
|
||||
steps:
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@c99871dec2022cc055c062a10cc1a1310835ceb4 # v4.3.0
|
||||
uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
|
||||
8
.github/workflows/docs-build.yml
vendored
8
.github/workflows/docs-build.yml
vendored
@@ -21,10 +21,11 @@ jobs:
|
||||
should_run: ${{ steps.check.outputs.should_run }}
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- name: Check what should run
|
||||
id: check
|
||||
@@ -54,13 +55,14 @@ jobs:
|
||||
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
token: ${{ steps.token.outputs.token }}
|
||||
|
||||
11
.github/workflows/docs-deploy.yml
vendored
11
.github/workflows/docs-deploy.yml
vendored
@@ -20,10 +20,12 @@ jobs:
|
||||
artifact: ${{ steps.get-artifact.outputs.result }}
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-actions: read
|
||||
permission-pull-requests: read
|
||||
|
||||
- if: ${{ github.event.workflow_run.conclusion != 'success' }}
|
||||
run: echo 'The triggering workflow did not succeed' && exit 1
|
||||
@@ -126,13 +128,16 @@ 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@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
permission-actions: read
|
||||
permission-pull-requests: write
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
token: ${{ steps.token.outputs.token }}
|
||||
|
||||
6
.github/workflows/docs-destroy.yml
vendored
6
.github/workflows/docs-destroy.yml
vendored
@@ -17,13 +17,15 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
permission-pull-requests: write
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
token: ${{ steps.token.outputs.token }}
|
||||
|
||||
56
.github/workflows/fdroid.yml
vendored
Normal file
56
.github/workflows/fdroid.yml
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
name: Update F-Droid Repo
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
update-index:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ !github.event.release.prerelease }}
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout pubspec for versionCode
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
ref: ${{ github.event.release.tag_name }}
|
||||
persist-credentials: false
|
||||
sparse-checkout: mobile/pubspec.yaml
|
||||
sparse-checkout-cone-mode: false
|
||||
|
||||
- name: Update F-Droid repo
|
||||
env:
|
||||
GITLAB_TOKEN: ${{ secrets.FDROID_REPO_TOKEN }}
|
||||
RELEASE: ${{ toJSON(github.event.release) }}
|
||||
TAG: ${{ github.event.release.tag_name }}
|
||||
run: |
|
||||
VERSION_CODE=$(yq '.version' mobile/pubspec.yaml | cut -d+ -f2)
|
||||
export VERSION_CODE
|
||||
|
||||
NEW_ENTRY=$(yq -P '
|
||||
[.assets[] | select(.name == "app-release.apk")] as $matches |
|
||||
with(select($matches | length == 0); error("app-release.apk not found in release assets")) |
|
||||
{
|
||||
"url": $matches[0].browser_download_url,
|
||||
"sha256sum": ($matches[0].digest | sub("^sha256:", "")),
|
||||
"date": (.published_at | split("T") | .[0]),
|
||||
"version-code": env(VERSION_CODE)
|
||||
} | .date tag= "!!timestamp"
|
||||
' <<< "$RELEASE")
|
||||
export NEW_ENTRY
|
||||
|
||||
git clone --depth 1 "https://oauth2:${GITLAB_TOKEN}@gitlab.futo.org/fdroid/repo-v2.git" fdroid-repo
|
||||
cd fdroid-repo
|
||||
|
||||
yq -i '
|
||||
with(select([.apks[] | select(."version-code" == env(VERSION_CODE))] | length > 0); error("version-code already in index")) |
|
||||
.apks = [env(NEW_ENTRY)] + .apks
|
||||
' apps/Immich/index.yml
|
||||
|
||||
git config user.name "Immich Release Bot"
|
||||
git config user.email "bot@immich.app"
|
||||
git commit -am "Immich: add version-code ${VERSION_CODE} (${TAG})"
|
||||
git push
|
||||
6
.github/workflows/fix-format.yml
vendored
6
.github/workflows/fix-format.yml
vendored
@@ -15,13 +15,15 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: write
|
||||
permission-pull-requests: write
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.ref }}
|
||||
persist-credentials: true
|
||||
|
||||
2
.github/workflows/merge-translations.yml
vendored
2
.github/workflows/merge-translations.yml
vendored
@@ -35,6 +35,8 @@ jobs:
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: write
|
||||
permission-pull-requests: write
|
||||
|
||||
- name: Find translation PR
|
||||
id: find_pr
|
||||
|
||||
5
.github/workflows/pr-label-validation.yml
vendored
5
.github/workflows/pr-label-validation.yml
vendored
@@ -14,13 +14,14 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-pull-requests: write
|
||||
|
||||
- name: Require PR to have a changelog label
|
||||
uses: mheap/github-action-required-labels@0ac283b4e65c1fb28ce6079dea5546ceca98ccbe # v5.5.2
|
||||
uses: mheap/github-action-required-labels@23e10fde7e062233401931a0eece796cd9bf3177 # v5.6.0
|
||||
with:
|
||||
token: ${{ steps.token.outputs.token }}
|
||||
mode: exactly
|
||||
|
||||
6
.github/workflows/pr-labeler.yml
vendored
6
.github/workflows/pr-labeler.yml
vendored
@@ -12,11 +12,13 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
permission-pull-requests: write
|
||||
|
||||
- uses: actions/labeler@f27b608878404679385c85cfa523b85ccb86e213 # v6.1.0
|
||||
- uses: actions/labeler@b8dd2d9be0f68b860e7dae5dae7d772984eacd6d # v6.2.0
|
||||
with:
|
||||
repo-token: ${{ steps.token.outputs.token }}
|
||||
|
||||
11
.github/workflows/prepare-release.yml
vendored
11
.github/workflows/prepare-release.yml
vendored
@@ -54,13 +54,14 @@ 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@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: write
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
token: ${{ steps.token.outputs.token }}
|
||||
persist-credentials: true
|
||||
@@ -73,7 +74,7 @@ jobs:
|
||||
|
||||
# TODO move to mise
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
|
||||
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
|
||||
|
||||
- name: Bump version
|
||||
env:
|
||||
@@ -135,9 +136,11 @@ jobs:
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: write
|
||||
permission-actions: read
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
token: ${{ steps.generate-token.outputs.token }}
|
||||
persist-credentials: false
|
||||
|
||||
6
.github/workflows/preview-label.yaml
vendored
6
.github/workflows/preview-label.yaml
vendored
@@ -14,10 +14,11 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-pull-requests: write
|
||||
|
||||
- uses: immich-app/devtools/actions/sticky-comment@0135acd12ad9f3369b94a2aa3c0ae8c835a4e926 # sticky-comment-action-v1.0.0
|
||||
with:
|
||||
@@ -32,10 +33,11 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-pull-requests: write
|
||||
|
||||
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
with:
|
||||
|
||||
5
.github/workflows/sdk.yml
vendored
5
.github/workflows/sdk.yml
vendored
@@ -16,13 +16,14 @@ jobs:
|
||||
packages: write
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
token: ${{ steps.token.outputs.token }}
|
||||
|
||||
9
.github/workflows/static_analysis.yml
vendored
9
.github/workflows/static_analysis.yml
vendored
@@ -20,10 +20,11 @@ jobs:
|
||||
should_run: ${{ steps.check.outputs.should_run }}
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- name: Check what should run
|
||||
id: check
|
||||
@@ -33,6 +34,7 @@ jobs:
|
||||
filters: |
|
||||
mobile:
|
||||
- 'mobile/**'
|
||||
- 'i18n/en.json'
|
||||
force-filters: |
|
||||
- '.github/workflows/static_analysis.yml'
|
||||
force-events: 'workflow_dispatch,release'
|
||||
@@ -49,13 +51,14 @@ jobs:
|
||||
working-directory: ./mobile
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
token: ${{ steps.token.outputs.token }}
|
||||
|
||||
92
.github/workflows/test.yml
vendored
92
.github/workflows/test.yml
vendored
@@ -17,10 +17,11 @@ jobs:
|
||||
should_run: ${{ steps.check.outputs.should_run }}
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- name: Check what should run
|
||||
id: check
|
||||
@@ -77,13 +78,14 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
token: ${{ steps.token.outputs.token }}
|
||||
@@ -105,13 +107,14 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
token: ${{ steps.token.outputs.token }}
|
||||
@@ -136,13 +139,14 @@ jobs:
|
||||
working-directory: ./packages/cli
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
token: ${{ steps.token.outputs.token }}
|
||||
@@ -167,13 +171,14 @@ jobs:
|
||||
working-directory: ./packages/cli
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
token: ${{ steps.token.outputs.token }}
|
||||
@@ -211,13 +216,14 @@ jobs:
|
||||
working-directory: ./web
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
token: ${{ steps.token.outputs.token }}
|
||||
@@ -249,13 +255,14 @@ jobs:
|
||||
working-directory: ./web
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
token: ${{ steps.token.outputs.token }}
|
||||
@@ -277,13 +284,14 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
token: ${{ steps.token.outputs.token }}
|
||||
@@ -327,13 +335,14 @@ jobs:
|
||||
working-directory: ./e2e
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
token: ${{ steps.token.outputs.token }}
|
||||
@@ -359,13 +368,14 @@ jobs:
|
||||
working-directory: ./server
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: 'recursive'
|
||||
@@ -395,13 +405,14 @@ jobs:
|
||||
runner: [ubuntu-latest, ubuntu-24.04-arm]
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: 'recursive'
|
||||
@@ -411,7 +422,7 @@ jobs:
|
||||
uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'pnpm'
|
||||
@@ -472,13 +483,14 @@ jobs:
|
||||
runner: [ubuntu-latest, ubuntu-24.04-arm]
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: 'recursive'
|
||||
@@ -488,7 +500,7 @@ jobs:
|
||||
uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'pnpm'
|
||||
@@ -580,12 +592,13 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
token: ${{ steps.token.outputs.token }}
|
||||
@@ -618,12 +631,13 @@ jobs:
|
||||
working-directory: ./machine-learning
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
token: ${{ steps.token.outputs.token }}
|
||||
@@ -648,13 +662,14 @@ jobs:
|
||||
working-directory: ./.github
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
token: ${{ steps.token.outputs.token }}
|
||||
@@ -678,12 +693,13 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
token: ${{ steps.token.outputs.token }}
|
||||
@@ -699,13 +715,14 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
token: ${{ steps.token.outputs.token }}
|
||||
@@ -757,13 +774,14 @@ jobs:
|
||||
- 5432:5432
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
token: ${{ steps.token.outputs.token }}
|
||||
|
||||
6
.github/workflows/weblate-lock.yml
vendored
6
.github/workflows/weblate-lock.yml
vendored
@@ -24,10 +24,11 @@ jobs:
|
||||
should_run: ${{ steps.check.outputs.should_run }}
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- name: Check what should run
|
||||
id: check
|
||||
@@ -47,10 +48,11 @@ jobs:
|
||||
if: ${{ fromJSON(needs.pre-job.outputs.should_run).i18n == true }}
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-pull-requests: read
|
||||
|
||||
- name: Bot review status
|
||||
env:
|
||||
|
||||
38
.vscode/settings.json
vendored
38
.vscode/settings.json
vendored
@@ -29,9 +29,6 @@
|
||||
"editor.formatOnSave": true,
|
||||
"tailwindCSS.lint.suggestCanonicalClasses": "ignore"
|
||||
},
|
||||
"svelte.plugin.svelte.compilerWarnings": {
|
||||
"state_referenced_locally": "ignore"
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.formatOnSave": true
|
||||
@@ -43,37 +40,40 @@
|
||||
"eslint.useFlatConfig": true,
|
||||
"eslint.validate": ["javascript", "typescript", "svelte"],
|
||||
"eslint.workingDirectories": [
|
||||
{ "directory": "cli", "changeProcessCWD": true },
|
||||
{ "directory": "e2e", "changeProcessCWD": true },
|
||||
{ "directory": "server", "changeProcessCWD": true },
|
||||
{ "directory": "web", "changeProcessCWD": true }
|
||||
{ "changeProcessCWD": true, "directory": "cli" },
|
||||
{ "changeProcessCWD": true, "directory": "e2e" },
|
||||
{ "changeProcessCWD": true, "directory": "server" },
|
||||
{ "changeProcessCWD": true, "directory": "web" }
|
||||
],
|
||||
"files.watcherExclude": {
|
||||
"**/.jj/**": true,
|
||||
"**/.git/**": true,
|
||||
"**/node_modules/**": true,
|
||||
"**/build/**": true,
|
||||
"**/dist/**": true,
|
||||
"**/.svelte-kit/**": true
|
||||
},
|
||||
"explorer.fileNesting.enabled": true,
|
||||
"explorer.fileNesting.patterns": {
|
||||
"*.dart": "${capture}.g.dart,${capture}.gr.dart,${capture}.drift.dart",
|
||||
"*.ts": "${capture}.spec.ts,${capture}.mock.ts",
|
||||
"*.js": "${capture}.spec.js,${capture}.mock.js",
|
||||
"*.ts": "${capture}.spec.ts,${capture}.mock.ts",
|
||||
"package.json": "package-lock.json, yarn.lock, pnpm-lock.yaml, bun.lockb, bun.lock, pnpm-workspace.yaml, .pnpmfile.cjs"
|
||||
},
|
||||
"files.watcherExclude": {
|
||||
"**/.git/**": true,
|
||||
"**/.jj/**": true,
|
||||
"**/.svelte-kit/**": true,
|
||||
"**/build/**": true,
|
||||
"**/dist/**": true,
|
||||
"**/node_modules/**": true
|
||||
},
|
||||
"js/ts.preferences.importModuleSpecifier": "non-relative",
|
||||
"search.exclude": {
|
||||
"**/node_modules": true,
|
||||
"**/.svelte-kit": true,
|
||||
"**/build": true,
|
||||
"**/dist": true,
|
||||
"**/.svelte-kit": true,
|
||||
"**/node_modules": true,
|
||||
"**/open-api/typescript-sdk/src": true
|
||||
},
|
||||
"svelte.enable-ts-plugin": true,
|
||||
"svelte.plugin.svelte.compilerWarnings": {
|
||||
"state_referenced_locally": "ignore"
|
||||
},
|
||||
"tailwindCSS.experimental.configFile": {
|
||||
"web/src/app.css": "web/src/**"
|
||||
},
|
||||
"js/ts.preferences.importModuleSpecifier": "non-relative",
|
||||
"vitest.maximumConfigs": 10
|
||||
}
|
||||
|
||||
@@ -4,4 +4,4 @@
|
||||
/web/ @danieldietzler
|
||||
/machine-learning/ @mertalev
|
||||
/e2e/ @danieldietzler
|
||||
/mobile/ @shenlong-tanwen @santoshakil
|
||||
/mobile/ @shenlong-tanwen @santoshakil @agg23
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
<a href="readme_i18n/README_zh_TW.md">正體中文</a>
|
||||
<a href="readme_i18n/README_uk_UA.md">Українська</a>
|
||||
<a href="readme_i18n/README_ru_RU.md">Русский</a>
|
||||
<a href="readme_i18n/README_bg_BG.md">Български</a>
|
||||
<a href="readme_i18n/README_pt_BR.md">Português Brasileiro</a>
|
||||
<a href="readme_i18n/README_sv_SE.md">Svenska</a>
|
||||
<a href="readme_i18n/README_ar_JO.md">العربية</a>
|
||||
|
||||
@@ -154,7 +154,7 @@ services:
|
||||
|
||||
redis:
|
||||
container_name: immich_redis
|
||||
image: docker.io/valkey/valkey:9@sha256:4963247afc4cd33c7d3b2d2816b9f7f8eeebab148d29056c2ca4d7cbc966f2d9
|
||||
image: docker.io/valkey/valkey:9@sha256:8e8d64b405ce18f41b8e5ee20aa4687a8ed0022d1298f2ce31cdcf3a76e09411
|
||||
healthcheck:
|
||||
test: redis-cli ping || exit 1
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ services:
|
||||
|
||||
redis:
|
||||
container_name: immich_redis
|
||||
image: docker.io/valkey/valkey:9@sha256:4963247afc4cd33c7d3b2d2816b9f7f8eeebab148d29056c2ca4d7cbc966f2d9
|
||||
image: docker.io/valkey/valkey:9@sha256:8e8d64b405ce18f41b8e5ee20aa4687a8ed0022d1298f2ce31cdcf3a76e09411
|
||||
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:a75c5a35bc21d7afe69551eefa3cb1e1fb1775fe759408007a66b54ec3de1f29
|
||||
image: prom/prometheus@sha256:3c42b892cf723fa54d2f262c37a0e1f80aa8c8ddb1da7b9b0df9455a35a7f893
|
||||
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:4963247afc4cd33c7d3b2d2816b9f7f8eeebab148d29056c2ca4d7cbc966f2d9
|
||||
image: docker.io/valkey/valkey:9@sha256:8e8d64b405ce18f41b8e5ee20aa4687a8ed0022d1298f2ce31cdcf3a76e09411
|
||||
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:4963247afc4cd33c7d3b2d2816b9f7f8eeebab148d29056c2ca4d7cbc966f2d9
|
||||
image: docker.io/valkey/valkey:9@sha256:8e8d64b405ce18f41b8e5ee20aa4687a8ed0022d1298f2ce31cdcf3a76e09411
|
||||
healthcheck:
|
||||
test: redis-cli ping || exit 1
|
||||
restart: always
|
||||
|
||||
@@ -1,5 +1,30 @@
|
||||
# System Integrity
|
||||
|
||||
## Integrity report
|
||||
|
||||
At a [customizable interval](https://my.immich.app/admin/system-settings?isOpen=integrity-checks), Immich runs integrity checks to ensure that your library is still intact and there are no corrupt files.
|
||||
There are three kind of issues Immich checks for:
|
||||
|
||||
- Untracked files: the path was found in Immich's directories but it is not referenced in Immich's database
|
||||
- Missing files: the path is found in the Immich internal database, but does not actually exist on disk
|
||||
- Checksum mismatches: the checksum of the file stored in Immich's database does not match the actual file's checksum anymore
|
||||
|
||||
All three run nightly at 3am by default. For the "Checksum files" check, there are additional time and progress limits, as those are the most taxing checks. With these additional limits, Immich can slowly check checksums of your files over the course of a couple of days.
|
||||
|
||||
You can see the results of these checks on the [maintenance](https://my.immich.app/admin/maintenance) page. Here, you can also trigger a full scan (a _check_) for specific jobs, or all of them. In addition, you can also _refresh_ checks. This will only look at items that have currently been reported on, and check if those have been fixed.
|
||||
|
||||
### Common causes
|
||||
|
||||
Most common are untracked files. In many cases those are corrupted thumbnails or encoded videos that have been partially generated at some point and never got cleaned up properly. These are usually fine to delete, as both can always be regenerated at a later point. Other files will need to be investigated on a case-by-case basis by checking they already exist in Immich and thinking about how they might have gotten untracked.
|
||||
|
||||
:::info
|
||||
You might want to run the _missing_ jobs for thumbnails and encoded videos (https://my.immich.app/admin/queues) to make sure all your assets have proper thumbnails and encoded videos. Watch the server logs while running the jobs, in case there are any issues with some assets.
|
||||
:::
|
||||
|
||||
Missing files are files where Immich references them internally, but they don't actually exist on disk in that location. It could be that you deleted a file on disk from the internal library (don't do that, Immich doesn't support it). It could also be that there are issues with your file storage. Please carefully investigate missing files, and never hesitate to reach out on [our Discord](https://discord.immich.app) if you have any questions!
|
||||
|
||||
Checksum mismatches are often indicative of file system corruption. It could also be that you previously edited a file from the internal library on the disk, which is also not supported and will cause a checksum mismatch. Again, the recommended action is to look at any reported item individually, check it out, try to remember if you changed it or some metadata on it at some point. If you edited the file, the supported resolution is to delete the mismatched asset from Immich and reupload it as a new asset.
|
||||
|
||||
## Folder checks
|
||||
|
||||
:::info
|
||||
@@ -40,7 +65,7 @@ The above error messages show that the server has previously (successfully) writ
|
||||
|
||||
### Ignoring the checks
|
||||
|
||||
:::warning
|
||||
:::danger
|
||||
The checks are designed to catch common problems that we have seen users have in the past, and often indicate there's something wrong that you should solve. If you know what you're doing and you want to disable them you can set the following environment variable:
|
||||
:::
|
||||
|
||||
|
||||
@@ -99,6 +99,21 @@ To see local changes to `@immich/ui` in Immich, do the following:
|
||||
2. Run `mise //mobile:translation` to generate the translation file.
|
||||
3. Change to the `mobile/` directory and run `flutter run` to start the app.
|
||||
|
||||
##### iOS Code Signing
|
||||
|
||||
The Immich Apple Team ID and bundle IDs are specified in `mobile/ios/Signing.xcconfig`. For local development, we provide an override mechanism.
|
||||
|
||||
Create `mobile/ios/Signing.local.xcconfig` and populate it with the necessary values needed to build and sign Immich yourself. This local override file is gitignored.
|
||||
|
||||
```
|
||||
IMMICH_TEAM_ID = ABCDE12345
|
||||
IMMICH_BUNDLE_ID_PROD = com.customuniqueid.immich
|
||||
IMMICH_BUNDLE_ID_DEV = com.customuniqueid.immichdev
|
||||
IMMICH_GROUP_ID = group.com.customuniqueid.immich
|
||||
```
|
||||
|
||||
The environment values are used across Immich's targets and schemes to prevent redundant edits by contributors.
|
||||
|
||||
#### Translation
|
||||
|
||||
To add a new translation text, enter the key-value pair in the `i18n/en.json` in the root of the immich project. Then run:
|
||||
|
||||
@@ -99,6 +99,7 @@ Options:
|
||||
-H, --include-hidden Include hidden folders (default: false, env: IMMICH_INCLUDE_HIDDEN)
|
||||
-a, --album Automatically create albums based on folder name (default: false, env: IMMICH_AUTO_CREATE_ALBUM)
|
||||
-A, --album-name <name> Add all assets to specified album (env: IMMICH_ALBUM_NAME)
|
||||
--visibility <visibility> Set the visibility of uploaded assets (choices: "archive", "timeline", "hidden", "locked", env: IMMICH_VISIBILITY)
|
||||
-n, --dry-run Don't perform any actions, just show what will be done (default: false, env: IMMICH_DRY_RUN)
|
||||
-c, --concurrency <number> Number of assets to upload at the same time (default: 4, env: IMMICH_UPLOAD_CONCURRENCY)
|
||||
-j, --json-output Output detailed information in json format (default: false, env: IMMICH_JSON_OUTPUT)
|
||||
@@ -176,6 +177,12 @@ By default, hidden files are skipped. If you want to include hidden files, use t
|
||||
immich upload --include-hidden --recursive directory/
|
||||
```
|
||||
|
||||
You can set the visibility of uploaded assets to `archive`, `timeline`, `hidden`, or `locked` with the `--visibility` option:
|
||||
|
||||
```bash
|
||||
immich upload --visibility archive --recursive directory/
|
||||
```
|
||||
|
||||
You can use the `--json-output` option to get a json printed which includes
|
||||
three keys: `newFiles`, `duplicates` and `newAssets`. Due to some logging
|
||||
output you will need to strip the first three lines of output to get the json.
|
||||
|
||||
@@ -7,7 +7,7 @@ sidebar_position: 85
|
||||
:::note
|
||||
This is a community contribution and not officially supported by the Immich team, but included here for convenience.
|
||||
|
||||
Community support can be found in the dedicated channel on the [Discord Server](https://discord.immich.app/).
|
||||
Community support should be directed to Synology-specific support platforms.
|
||||
:::
|
||||
|
||||
Immich can easily be installed on a Synology NAS using Container Manager within DSM. If you have not installed Container Manager already, you can install it in the Packages Center. Refer to the [Container Manager docs](https://kb.synology.com/en-us/DSM/help/ContainerManager/docker_desc?version=7) for more information on using Container Manager.
|
||||
|
||||
@@ -121,7 +121,7 @@ alt="Go to Docker Tab and visit the address listed next to immich-web"
|
||||
width="90%"
|
||||
alt="Go to Docker Tab and visit the address listed next to immich-web"
|
||||
/>
|
||||
|
||||
|
||||
</details>
|
||||
|
||||
:::tip
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
The mobile app can be downloaded from the following places:
|
||||
|
||||
- Obtainium: You can get your Obtainium config link from the [Utilities page of your Immich server](https://my.immich.app/utilities).
|
||||
- [Google Play Store](https://play.google.com/store/apps/details?id=app.alextran.immich)
|
||||
- [Apple App Store](https://apps.apple.com/us/app/immich/id1613945652)
|
||||
- [F-Droid](https://f-droid.org/packages/app.alextran.immich)
|
||||
- [GitHub Releases (apk)](https://github.com/immich-app/immich/releases)
|
||||
- [Google Play Store](https://play.google.com/store/apps/details?id=app.alextran.immich)
|
||||
- [GitHub Releases (APK)](https://github.com/immich-app/immich/releases)
|
||||
- Obtainium: You can get your Obtainium config link from the [Utilities page of your Immich server](https://my.immich.app/utilities).
|
||||
- [F-Droid](https://app.futo.org/fdroid/repo/)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# @generated - this file is auto-generated by `mise lock` https://mise.en.dev/dev-tools/mise-lock.html
|
||||
|
||||
[[tools.wrangler]]
|
||||
version = "4.100.0"
|
||||
version = "4.110.0"
|
||||
backend = "npm:wrangler"
|
||||
|
||||
[tools.wrangler.options]
|
||||
|
||||
@@ -28,4 +28,4 @@ run = "prettier --write ."
|
||||
run = "wrangler pages deploy build --project-name=${PROJECT_NAME} --branch=${BRANCH_NAME}"
|
||||
|
||||
[tools]
|
||||
wrangler = "4.100.0"
|
||||
wrangler = "4.111.0"
|
||||
|
||||
@@ -40,7 +40,8 @@
|
||||
"@docusaurus/tsconfig": "^3.10.0",
|
||||
"@docusaurus/types": "^3.10.0",
|
||||
"prettier": "^3.7.4",
|
||||
"typescript": "^6.0.0"
|
||||
"@typescript/native": "npm:typescript@^7.0.2",
|
||||
"typescript": "npm:@typescript/typescript6@^6.0.2"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
|
||||
4
docs/static/archived-versions.json
vendored
4
docs/static/archived-versions.json
vendored
@@ -1,7 +1,7 @@
|
||||
[
|
||||
{
|
||||
"label": "v3.0.2",
|
||||
"url": "https://docs.v3.0.2.archive.immich.app"
|
||||
"label": "v3.0.3",
|
||||
"url": "https://docs.v3.0.3.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v2.7.5",
|
||||
|
||||
@@ -45,7 +45,7 @@ services:
|
||||
|
||||
redis:
|
||||
container_name: immich-e2e-redis
|
||||
image: docker.io/valkey/valkey:9@sha256:4963247afc4cd33c7d3b2d2816b9f7f8eeebab148d29056c2ca4d7cbc966f2d9
|
||||
image: docker.io/valkey/valkey:9@sha256:8e8d64b405ce18f41b8e5ee20aa4687a8ed0022d1298f2ce31cdcf3a76e09411
|
||||
healthcheck:
|
||||
test: redis-cli ping || exit 1
|
||||
|
||||
|
||||
@@ -41,9 +41,18 @@ export default typescriptEslint.config([
|
||||
'@typescript-eslint/no-floating-promises': 'error',
|
||||
'unicorn/prefer-module': 'off',
|
||||
'unicorn/import-style': 'off',
|
||||
'unicorn/consistent-boolean-name': 'off',
|
||||
'unicorn/no-non-function-verb-prefix': 'off',
|
||||
'unicorn/no-unreadable-for-of-expression': 'off',
|
||||
'unicorn/max-nested-calls': 'off',
|
||||
'unicorn/prefer-uint8array-base64': 'off',
|
||||
'unicorn/isolated-functions': 'off',
|
||||
'unicorn/prefer-promise-with-resolvers': 'off',
|
||||
'unicorn/no-declarations-before-early-exit': 'off',
|
||||
'unicorn/prefer-simple-condition-first': 'off',
|
||||
curly: 2,
|
||||
'prettier/prettier': 0,
|
||||
'unicorn/prevent-abbreviations': 'off',
|
||||
'unicorn/name-replacements': 'off',
|
||||
'unicorn/filename-case': 'off',
|
||||
'unicorn/no-null': 'off',
|
||||
'unicorn/prefer-top-level-await': 'off',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "immich-e2e",
|
||||
"version": "3.0.2",
|
||||
"version": "3.0.3",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"type": "module",
|
||||
@@ -32,7 +32,7 @@
|
||||
"@playwright/test": "^1.44.1",
|
||||
"@socket.io/component-emitter": "^3.1.2",
|
||||
"@types/luxon": "^3.4.2",
|
||||
"@types/node": "^24.13.2",
|
||||
"@types/node": "^24.13.3",
|
||||
"@types/pg": "^8.15.1",
|
||||
"@types/pngjs": "^6.0.4",
|
||||
"@types/supertest": "^7.0.0",
|
||||
@@ -40,7 +40,7 @@
|
||||
"eslint": "^10.0.0",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"eslint-plugin-unicorn": "^64.0.0",
|
||||
"eslint-plugin-unicorn": "^72.0.0",
|
||||
"exiftool-vendored": "^35.0.0",
|
||||
"globals": "^17.0.0",
|
||||
"luxon": "^3.4.4",
|
||||
@@ -51,7 +51,8 @@
|
||||
"sharp": "^0.34.5",
|
||||
"socket.io-client": "^4.7.4",
|
||||
"supertest": "^7.0.0",
|
||||
"typescript": "^6.0.0",
|
||||
"@typescript/native": "npm:typescript@^7.0.2",
|
||||
"typescript": "npm:@typescript/typescript6@^6.0.2",
|
||||
"typescript-eslint": "^8.28.0",
|
||||
"utimes": "^5.2.1",
|
||||
"vitest": "^4.0.0"
|
||||
|
||||
@@ -118,7 +118,7 @@ describe('/admin/database-backups', () => {
|
||||
|
||||
expect(status).toBe(201);
|
||||
|
||||
cookie = headers['set-cookie'][0].split(';')[0];
|
||||
cookie = headers['set-cookie'][0].split(';', 1)[0];
|
||||
|
||||
await expect
|
||||
.poll(
|
||||
@@ -224,7 +224,7 @@ describe('/admin/database-backups', () => {
|
||||
});
|
||||
|
||||
expect(status).toBe(201);
|
||||
cookie = headers['set-cookie'][0].split(';')[0];
|
||||
cookie = headers['set-cookie'][0].split(';', 1)[0];
|
||||
|
||||
await expect
|
||||
.poll(
|
||||
@@ -295,7 +295,7 @@ describe('/admin/database-backups', () => {
|
||||
});
|
||||
|
||||
expect(status).toBe(201);
|
||||
cookie = headers['set-cookie'][0].split(';')[0];
|
||||
cookie = headers['set-cookie'][0].split(';', 1)[0];
|
||||
|
||||
await expect
|
||||
.poll(
|
||||
|
||||
@@ -85,7 +85,7 @@ describe('/admin/maintenance', () => {
|
||||
|
||||
expect(status).toBe(201);
|
||||
|
||||
cookie = headers['set-cookie'][0].split(';')[0];
|
||||
cookie = headers['set-cookie'][0].split(';', 1)[0];
|
||||
expect(cookie).toEqual(
|
||||
expect.stringMatching(/^immich_maintenance_token=[A-Za-z0-9-_]*\.[A-Za-z0-9-_]*\.[A-Za-z0-9-_]*$/),
|
||||
);
|
||||
@@ -149,7 +149,7 @@ describe('/admin/maintenance', () => {
|
||||
const { status, body } = await request(app)
|
||||
.post('/admin/maintenance/login')
|
||||
.send({
|
||||
token: cookie!.split('=')[1].trim(),
|
||||
token: cookie!.split('=', 2)[1].trim(),
|
||||
});
|
||||
expect(status).toBe(201);
|
||||
expect(body).toEqual(
|
||||
|
||||
@@ -27,7 +27,7 @@ test.describe('Maintenance', () => {
|
||||
test('maintenance shows no options to users until they authenticate', async ({ page }) => {
|
||||
const setCookie = await utils.enterMaintenance(admin.accessToken);
|
||||
const cookie = setCookie
|
||||
?.map((cookie) => cookie.split(';')[0].split('='))
|
||||
?.map((cookie) => cookie.split(';', 1)[0].split('='))
|
||||
?.find(([name]) => name === 'immich_maintenance_token');
|
||||
|
||||
expect(cookie).toBeTruthy();
|
||||
|
||||
@@ -120,6 +120,7 @@ describe('/albums', () => {
|
||||
}),
|
||||
]);
|
||||
|
||||
// eslint-disable-next-line unicorn/no-unreadable-array-destructuring
|
||||
[user2Albums[0]] = await Promise.all([
|
||||
getAlbumInfo({ id: user2Albums[0].id }, { headers: asBearerAuth(user2.accessToken) }),
|
||||
deleteUserAdmin({ id: user3.userId, userAdminDeleteDto: {} }, { headers: asBearerAuth(admin.accessToken) }),
|
||||
@@ -796,5 +797,22 @@ describe('/albums', () => {
|
||||
expect(status).toBe(400);
|
||||
expect(body).toEqual(errorDto.badRequest('Not found or no album.share access'));
|
||||
});
|
||||
|
||||
it('should not allow an editor to change the role of an owner', async () => {
|
||||
const album = await utils.createAlbum(user1.accessToken, {
|
||||
albumName: 'testAlbum',
|
||||
albumUsers: [{ userId: user2.userId, role: AlbumUserRole.Editor }],
|
||||
});
|
||||
|
||||
expect(album.albumUsers[1].role).toEqual(AlbumUserRole.Editor);
|
||||
|
||||
const { status, body } = await request(app)
|
||||
.put(`/albums/${album.id}/user/${user1.userId}`)
|
||||
.set('Authorization', `Bearer ${user2.accessToken}`)
|
||||
.send({ role: AlbumUserRole.Editor });
|
||||
|
||||
expect(status).toBe(400);
|
||||
expect(body).toEqual(errorDto.badRequest('User is owner'));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -781,7 +781,7 @@ describe('/asset', () => {
|
||||
exifImageWidth: 4032,
|
||||
exifImageHeight: 3024,
|
||||
latitude: 41.2203,
|
||||
longitude: -96.071_625,
|
||||
longitude: -96.071625,
|
||||
make: 'Apple',
|
||||
model: 'iPhone 7',
|
||||
lensModel: 'iPhone 7 back camera 3.99mm f/1.8',
|
||||
@@ -973,9 +973,9 @@ describe('/asset', () => {
|
||||
fileSizeInByte: 31_175_472,
|
||||
focalLength: 18.3,
|
||||
iso: 100,
|
||||
latitude: 36.613_24,
|
||||
latitude: 36.61324,
|
||||
lensModel: '18.3mm F2.8',
|
||||
longitude: -121.897_85,
|
||||
longitude: -121.89785,
|
||||
make: 'RICOH IMAGING COMPANY, LTD.',
|
||||
model: 'RICOH GR III',
|
||||
orientation: '1',
|
||||
|
||||
@@ -75,7 +75,7 @@ describe('/map', () => {
|
||||
country: 'United States of America',
|
||||
id: expect.any(String),
|
||||
lat: expect.closeTo(39.115),
|
||||
lon: expect.closeTo(-108.400_968),
|
||||
lon: expect.closeTo(-108.400968),
|
||||
state: 'Colorado',
|
||||
},
|
||||
{
|
||||
@@ -83,7 +83,7 @@ describe('/map', () => {
|
||||
country: 'United States of America',
|
||||
id: expect.any(String),
|
||||
lat: expect.closeTo(41.2203),
|
||||
lon: expect.closeTo(-96.071_625),
|
||||
lon: expect.closeTo(-96.071625),
|
||||
state: 'Nebraska',
|
||||
},
|
||||
]);
|
||||
@@ -123,7 +123,7 @@ describe('/map', () => {
|
||||
country: 'United States of America',
|
||||
id: expect.any(String),
|
||||
lat: expect.closeTo(39.115),
|
||||
lon: expect.closeTo(-108.400_968),
|
||||
lon: expect.closeTo(-108.400968),
|
||||
state: 'Colorado',
|
||||
},
|
||||
{
|
||||
@@ -131,7 +131,7 @@ describe('/map', () => {
|
||||
country: 'United States of America',
|
||||
id: expect.any(String),
|
||||
lat: expect.closeTo(41.2203),
|
||||
lon: expect.closeTo(-96.071_625),
|
||||
lon: expect.closeTo(-96.071625),
|
||||
state: 'Nebraska',
|
||||
},
|
||||
]);
|
||||
@@ -188,20 +188,20 @@ describe('/map', () => {
|
||||
const reverseGeocodeTestCases = [
|
||||
{
|
||||
name: 'Vaucluse',
|
||||
lat: -33.858_977_058_663_13,
|
||||
lon: 151.278_490_730_270_48,
|
||||
lat: -33.85897705866313,
|
||||
lon: 151.27849073027048,
|
||||
results: [{ city: 'Vaucluse', state: 'New South Wales', country: 'Australia' }],
|
||||
},
|
||||
{
|
||||
name: 'Ravenhall',
|
||||
lat: -37.765_732_399_174_75,
|
||||
lon: 144.752_453_164_883_3,
|
||||
lat: -37.76573239917475,
|
||||
lon: 144.7524531648833,
|
||||
results: [{ city: 'Ravenhall', state: 'Victoria', country: 'Australia' }],
|
||||
},
|
||||
{
|
||||
name: 'Scarborough',
|
||||
lat: -31.894_346_156_789_997,
|
||||
lon: 115.757_617_103_904_64,
|
||||
lat: -31.894346156789997,
|
||||
lon: 115.75761710390464,
|
||||
results: [{ city: 'Scarborough', state: 'Western Australia', country: 'Australia' }],
|
||||
},
|
||||
];
|
||||
|
||||
@@ -44,7 +44,7 @@ const loginWithOAuth = async (sub: OAuthUser | string, redirectUri?: string) =>
|
||||
});
|
||||
|
||||
// login
|
||||
const response1 = await redirect(url.replace(authServer.internal, authServer.external));
|
||||
const response1 = await redirect(url.replace(authServer.internal, () => authServer.external));
|
||||
const response2 = await request(authServer.external + response1.location)
|
||||
.post('')
|
||||
.set('Cookie', response1.cookies)
|
||||
|
||||
@@ -87,23 +87,23 @@ describe('/search', () => {
|
||||
|
||||
// note: the coordinates here are not the actual coordinates of the images and are random for most of them
|
||||
const coordinates = [
|
||||
{ latitude: 48.853_41, longitude: 2.3488 }, // paris
|
||||
{ latitude: 35.6895, longitude: 139.691_71 }, // tokyo
|
||||
{ latitude: 52.524_37, longitude: 13.410_53 }, // berlin
|
||||
{ latitude: 1.314_663_1, longitude: 103.845_409_3 }, // singapore
|
||||
{ latitude: 41.013_84, longitude: 28.949_66 }, // istanbul
|
||||
{ latitude: 5.556_02, longitude: -0.1969 }, // accra
|
||||
{ latitude: 37.544_270_6, longitude: -4.727_752_8 }, // andalusia
|
||||
{ latitude: 23.133_02, longitude: -82.383_04 }, // havana
|
||||
{ latitude: 41.694_11, longitude: 44.833_68 }, // tbilisi
|
||||
{ latitude: 31.222_22, longitude: 121.458_06 }, // shanghai
|
||||
{ latitude: 48.85341, longitude: 2.3488 }, // paris
|
||||
{ latitude: 35.6895, longitude: 139.69171 }, // tokyo
|
||||
{ latitude: 52.52437, longitude: 13.41053 }, // berlin
|
||||
{ latitude: 1.3146631, longitude: 103.8454093 }, // singapore
|
||||
{ latitude: 41.01384, longitude: 28.94966 }, // istanbul
|
||||
{ latitude: 5.55602, longitude: -0.1969 }, // accra
|
||||
{ latitude: 37.5442706, longitude: -4.7277528 }, // andalusia
|
||||
{ latitude: 23.13302, longitude: -82.38304 }, // havana
|
||||
{ latitude: 41.69411, longitude: 44.83368 }, // tbilisi
|
||||
{ latitude: 31.22222, longitude: 121.45806 }, // shanghai
|
||||
{ latitude: 38.9711, longitude: -109.7137 }, // thompson springs
|
||||
{ latitude: 40.714_27, longitude: -74.005_97 }, // new york
|
||||
{ latitude: 47.040_57, longitude: 9.068_04 }, // glarus
|
||||
{ latitude: 32.771_52, longitude: -89.116_73 }, // philadelphia
|
||||
{ latitude: 31.634_16, longitude: -7.999_94 }, // marrakesh
|
||||
{ latitude: 38.523_735_4, longitude: -78.488_619_4 }, // tanners ridge
|
||||
{ latitude: 59.938_63, longitude: 30.314_13 }, // st. petersburg
|
||||
{ latitude: 40.71427, longitude: -74.00597 }, // new york
|
||||
{ latitude: 47.04057, longitude: 9.06804 }, // glarus
|
||||
{ latitude: 32.77152, longitude: -89.11673 }, // philadelphia
|
||||
{ latitude: 31.63416, longitude: -7.99994 }, // marrakesh
|
||||
{ latitude: 38.5237354, longitude: -78.4886194 }, // tanners ridge
|
||||
{ latitude: 59.93863, longitude: 30.31413 }, // st. petersburg
|
||||
{ latitude: 0, longitude: 0 }, // null island
|
||||
];
|
||||
|
||||
@@ -112,7 +112,7 @@ describe('/search', () => {
|
||||
);
|
||||
|
||||
await Promise.all(updates);
|
||||
for (const [i] of coordinates.entries()) {
|
||||
for (const i of coordinates.keys()) {
|
||||
await utils.waitForWebsocketEvent({ event: 'assetUpdate', id: assets[i].id });
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ describe(`immich login`, () => {
|
||||
it('should login and save auth.yml with 600', async () => {
|
||||
const admin = await utils.adminSetup();
|
||||
const key = await utils.createApiKey(admin.accessToken, [Permission.All]);
|
||||
const { stdout, stderr, exitCode } = await immichCli(['login', app, `${key.secret}`]);
|
||||
const { stdout, stderr, exitCode } = await immichCli(['login', app, key.secret]);
|
||||
expect(stdout.split('\n')).toEqual([
|
||||
'Logging in to http://127.0.0.1:2285/api',
|
||||
'Logged in as admin@immich.cloud',
|
||||
@@ -48,7 +48,7 @@ describe(`immich login`, () => {
|
||||
it('should login without /api in the url', async () => {
|
||||
const admin = await utils.adminSetup();
|
||||
const key = await utils.createApiKey(admin.accessToken, [Permission.All]);
|
||||
const { stdout, stderr, exitCode } = await immichCli(['login', app.replaceAll('/api', ''), `${key.secret}`]);
|
||||
const { stdout, stderr, exitCode } = await immichCli(['login', app.replaceAll('/api', ''), key.secret]);
|
||||
expect(stdout.split('\n')).toEqual([
|
||||
'Logging in to http://127.0.0.1:2285',
|
||||
'Discovered API at http://127.0.0.1:2285/api',
|
||||
|
||||
@@ -119,7 +119,9 @@ describe(`immich upload`, () => {
|
||||
const baseDir = `/tmp/upload/`;
|
||||
|
||||
const testPaths = Object.keys(files).map((filePath) => `${baseDir}/${filePath}`);
|
||||
testPaths.map((filePath) => utils.createImageFile(filePath));
|
||||
for (const filePath of testPaths) {
|
||||
utils.createImageFile(filePath);
|
||||
}
|
||||
|
||||
const commandLine = paths.map((argument) => `${baseDir}/${argument}`);
|
||||
|
||||
@@ -135,7 +137,9 @@ describe(`immich upload`, () => {
|
||||
const assets = await getAssetStatistics({}, { headers: asKeyAuth(key) });
|
||||
expect(assets.total).toBe(expectedCount);
|
||||
|
||||
testPaths.map((filePath) => utils.removeImageFile(filePath));
|
||||
for (const filePath of testPaths) {
|
||||
utils.removeImageFile(filePath);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -21,7 +21,7 @@ export const randomImageFromString = async (
|
||||
let seedNumber = 0;
|
||||
for (let i = 0; i < seed.length; i++) {
|
||||
seedNumber = (seedNumber << 5) - seedNumber + (seed.codePointAt(i) ?? 0);
|
||||
seedNumber = seedNumber & seedNumber; // Convert to 32bit integer
|
||||
seedNumber &= seedNumber; // Convert to 32bit integer
|
||||
}
|
||||
return randomImage(new SeededRandom(Math.abs(seedNumber)), { width, height });
|
||||
};
|
||||
|
||||
@@ -64,7 +64,7 @@ export function generateAsset(
|
||||
const asset: MockTimelineAsset = {
|
||||
id: assetId,
|
||||
ownerId,
|
||||
ratio: Number.parseFloat(ratio.split(':')[0]) / Number.parseFloat(ratio.split(':')[1]),
|
||||
ratio: Number(ratio.split(':', 1)[0]) / Number(ratio.split(':', 2)[1]),
|
||||
thumbhash: generateThumbhash(rng),
|
||||
localDateTime: date.toISOString(),
|
||||
fileCreatedAt: date.toISOString(),
|
||||
@@ -214,7 +214,7 @@ export function generateTimelineData(config: TimelineConfig): MockTimelineData {
|
||||
}
|
||||
|
||||
// Create a mock album from random assets
|
||||
const allAssets = [...buckets.values()].flat();
|
||||
const allAssets = buckets.values().toArray().flat();
|
||||
|
||||
// Select 10-30 random assets for the album (or all assets if less than 10)
|
||||
const albumSize = Math.min(allAssets.length, globalRng.nextInt(10, 31));
|
||||
|
||||
@@ -172,11 +172,7 @@ function shouldIncludeAsset(
|
||||
if (isArchived !== undefined && actuallyArchived !== isArchived) {
|
||||
return false;
|
||||
}
|
||||
if (isFavorite !== undefined && actuallyFavorited !== isFavorite) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return isFavorite === undefined || actuallyFavorited === isFavorite;
|
||||
}
|
||||
/**
|
||||
* Get summary for all buckets (mimics getTimeBuckets API)
|
||||
@@ -361,7 +357,7 @@ export function getAsset(
|
||||
owner?: UserResponseDto,
|
||||
): AssetResponseDto | undefined {
|
||||
// Search through all buckets for the asset
|
||||
const buckets = [...timelineData.buckets.values()];
|
||||
const buckets = timelineData.buckets.values().toArray();
|
||||
for (const assets of buckets) {
|
||||
const asset = assets.find((a) => a.id === assetId);
|
||||
if (asset) {
|
||||
@@ -395,7 +391,7 @@ export function getAlbum(
|
||||
|
||||
// Get the actual asset objects from the timeline data
|
||||
const albumAssets: AssetResponseDto[] = [];
|
||||
const allAssets = [...timelineData.buckets.values()].flat();
|
||||
const allAssets = timelineData.buckets.values().toArray().flat();
|
||||
|
||||
for (const assetId of album.assetIds) {
|
||||
const assetConfig = allAssets.find((a) => a.id === assetId);
|
||||
|
||||
@@ -143,7 +143,7 @@ export function validateTimelineConfig(config: TimelineConfig): void {
|
||||
}
|
||||
|
||||
// Validate seed if provided
|
||||
if (config.seed !== undefined && (config.seed < 0 || !Number.isInteger(config.seed))) {
|
||||
if (config.seed !== undefined && (config.seed < 0 || !Number.isSafeInteger(config.seed))) {
|
||||
throw new Error('Seed must be a non-negative integer');
|
||||
}
|
||||
|
||||
|
||||
@@ -153,11 +153,8 @@ export function getMockAsset(
|
||||
const isInDifferentPeriod = (date1: DateTime, date2: DateTime): boolean => {
|
||||
if (unit === 'day') {
|
||||
return !date1.startOf('day').equals(date2.startOf('day'));
|
||||
} else if (unit === 'month') {
|
||||
return date1.year !== date2.year || date1.month !== date2.month;
|
||||
} else {
|
||||
return date1.year !== date2.year;
|
||||
}
|
||||
return unit === 'month' ? date1.year !== date2.year || date1.month !== date2.month : date1.year !== date2.year;
|
||||
};
|
||||
|
||||
if (direction === 'next') {
|
||||
|
||||
@@ -40,7 +40,8 @@ export const setupTimelineMockApiRoutes = async (
|
||||
contentType: 'application/json',
|
||||
json: getTimeBuckets(timelineRestData, isTrashed, isArchived, isFavorite, albumId, changes),
|
||||
});
|
||||
} else if (pathname === '/api/timeline/bucket') {
|
||||
}
|
||||
if (pathname === '/api/timeline/bucket') {
|
||||
const timeBucket = url.searchParams.get('timeBucket');
|
||||
if (!timeBucket) {
|
||||
return route.continue();
|
||||
|
||||
@@ -38,7 +38,7 @@ test.describe('asset-viewer', () => {
|
||||
assets.push(...timeBucket);
|
||||
}
|
||||
for (const yearMonth of timelineRestData.buckets.keys()) {
|
||||
const [year, month] = yearMonth.split('-');
|
||||
const [year, month] = yearMonth.split('-', 2);
|
||||
yearMonths.push(`${year}-${Number(month)}`);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable unicorn/no-this-outside-of-class */
|
||||
import type { AssetResponseDto } from '@immich/sdk';
|
||||
import { expect, Page } from '@playwright/test';
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ test.describe('Timeline', () => {
|
||||
assets.push(...timeBucket);
|
||||
}
|
||||
for (const yearMonth of timelineRestData.buckets.keys()) {
|
||||
const [year, month] = yearMonth.split('-');
|
||||
const [year, month] = yearMonth.split('-', 2);
|
||||
yearMonths.push(`${year}-${Number(month)}`);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -7,7 +7,7 @@ export const sleep = (ms: number) => {
|
||||
};
|
||||
|
||||
export const padYearMonth = (yearMonth: string) => {
|
||||
const [year, month] = yearMonth.split('-');
|
||||
const [year, month] = yearMonth.split('-', 2);
|
||||
return `${year}-${month.padStart(2, '0')}`;
|
||||
};
|
||||
|
||||
@@ -72,6 +72,7 @@ export const thumbnailUtils = {
|
||||
},
|
||||
async queryThumbnailInViewport(page: Page, collector: (assetId: string) => boolean) {
|
||||
const assetIds: string[] = [];
|
||||
// eslint-disable-next-line unicorn/no-this-outside-of-class
|
||||
for (const thumb of await this.locator(page).all()) {
|
||||
const box = await thumb.boundingBox();
|
||||
if (box) {
|
||||
@@ -151,6 +152,7 @@ export const timelineUtils = {
|
||||
page.evaluate(() => {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
|
||||
return document.querySelector('#asset-grid').scrollTop;
|
||||
});
|
||||
await expect.poll(queryTop).toBeGreaterThan(0);
|
||||
@@ -177,6 +179,7 @@ export const assetViewerUtils = {
|
||||
page.evaluate(() => {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
// eslint-disable-next-line unicorn/no-optional-chaining-on-undeclared-variable
|
||||
return document.activeElement?.dataset?.asset;
|
||||
});
|
||||
await expect(poll(page, activeElement, (result) => result === assetId)).resolves.toBe(assetId);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable unicorn/no-top-level-assignment-in-function */
|
||||
import {
|
||||
AssetMediaCreateDto,
|
||||
AssetMediaResponseDto,
|
||||
@@ -177,7 +178,7 @@ export const utils = {
|
||||
resetDatabase: async (tables?: string[]) => {
|
||||
client = await utils.connectDatabase();
|
||||
|
||||
tables = tables || [
|
||||
tables ||= [
|
||||
// TODO e2e test for deleting a stack, since it is quite complex
|
||||
'stack',
|
||||
'library',
|
||||
@@ -304,7 +305,7 @@ export const utils = {
|
||||
},
|
||||
|
||||
adminSetup: async (options?: AdminSetupOptions) => {
|
||||
options = options || { onboarding: true };
|
||||
options ||= { onboarding: true };
|
||||
|
||||
await signUpAdmin({ signUpDto: signupDto.admin });
|
||||
const response = await login({ loginCredentialDto: loginDto.admin });
|
||||
@@ -545,6 +546,7 @@ export const utils = {
|
||||
{
|
||||
headers: asBearerAuth(accessToken),
|
||||
fetch: (...args: Parameters<typeof fetch>) =>
|
||||
// eslint-disable-next-line unicorn/no-invalid-argument-count, unicorn/prefer-await
|
||||
fetch(...args).then((response) => {
|
||||
setCookie = response.headers.getSetCookie();
|
||||
return response;
|
||||
@@ -674,7 +676,7 @@ export const utils = {
|
||||
|
||||
cliLogin: async (accessToken: string) => {
|
||||
const key = await utils.createApiKey(accessToken, [Permission.All]);
|
||||
await immichCli(['login', app, `${key.secret}`]);
|
||||
await immichCli(['login', app, key.secret]);
|
||||
return key.secret;
|
||||
},
|
||||
|
||||
@@ -706,6 +708,7 @@ export const utils = {
|
||||
},
|
||||
};
|
||||
|
||||
// eslint-disable-next-line unicorn/no-top-level-side-effects
|
||||
utils.initSdk();
|
||||
|
||||
if (!existsSync(`${testAssetDir}/albums`)) {
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"resolveJsonModule": true,
|
||||
"target": "es2023",
|
||||
"lib": ["dom", "ESNext"],
|
||||
"sourceMap": true,
|
||||
"outDir": "./dist",
|
||||
"incremental": true,
|
||||
|
||||
@@ -814,7 +814,6 @@
|
||||
"custom_date": "Custom date",
|
||||
"custom_locale": "Custom locale",
|
||||
"custom_locale_description": "Format dates, times, and numbers based on the selected language and region",
|
||||
"custom_url": "Custom URL",
|
||||
"cutoff_date_description": "Keep photos from the last…",
|
||||
"cutoff_day": "{count, plural, one {day} other {days}}",
|
||||
"cutoff_year": "{count, plural, one {year} other {years}}",
|
||||
@@ -1621,7 +1620,7 @@
|
||||
"person": "Person",
|
||||
"person_age_months": "{months, plural, one {# month} other {# months}} old",
|
||||
"person_age_year_months": "1 year, {months, plural, one {# month} other {# months}} old",
|
||||
"person_age_years": "{years, plural, other {# years}} old",
|
||||
"person_age_years": "{years, plural, one {# year} other {# years}} old",
|
||||
"person_birthdate": "Born on {date}",
|
||||
"person_hidden": "{name}{hidden, select, true { (hidden)} other {}}",
|
||||
"person_recognized": "Person recognized",
|
||||
@@ -1948,7 +1947,9 @@
|
||||
"shared_link_app_bar_title": "Shared Links",
|
||||
"shared_link_clipboard_copied_massage": "Copied to clipboard",
|
||||
"shared_link_create_error": "Error while creating shared link",
|
||||
"shared_link_custom_url_description": "Access this shared link with a custom URL",
|
||||
"shared_link_custom_url_description": "Access this shared link with a custom URL name",
|
||||
"shared_link_custom_url_title": "Custom URL",
|
||||
"shared_link_custom_url_warning": "Warning: a custom URL name with a forward slash may not behave as you expect.",
|
||||
"shared_link_edit_description_hint": "Enter the share description",
|
||||
"shared_link_edit_expire_after_option_day": "1 day",
|
||||
"shared_link_edit_expire_after_option_days": "{count} days",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "immich-ml"
|
||||
version = "3.0.2"
|
||||
version = "3.0.3"
|
||||
description = ""
|
||||
authors = [{ name = "Hau Tran", email = "alex.tran1502@gmail.com" }]
|
||||
requires-python = ">=3.11,<4.0"
|
||||
|
||||
2
machine-learning/uv.lock
generated
2
machine-learning/uv.lock
generated
@@ -974,7 +974,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "immich-ml"
|
||||
version = "3.0.2"
|
||||
version = "3.0.3"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "aiocache" },
|
||||
|
||||
38
mise.lock
38
mise.lock
@@ -287,43 +287,43 @@ url = "https://github.com/opentofu/opentofu/releases/download/v1.11.6/tofu_1.11.
|
||||
url_api = "https://api.github.com/repos/opentofu/opentofu/releases/assets/391536406"
|
||||
|
||||
[[tools.pnpm]]
|
||||
version = "11.6.0"
|
||||
version = "11.13.1"
|
||||
backend = "aqua:pnpm/pnpm"
|
||||
|
||||
[tools.pnpm."platforms.linux-arm64"]
|
||||
checksum = "sha256:2fec653ff6dadab340d1c3d2214688a7451cc471f39710839440b293ca7c53b0"
|
||||
url = "https://github.com/pnpm/pnpm/releases/download/v11.6.0/pnpm-linux-arm64.tar.gz"
|
||||
url_api = "https://api.github.com/repos/pnpm/pnpm/releases/assets/445120174"
|
||||
checksum = "sha256:b52db99d215ed7dc9563aed815953c62a6c1ffd7cd75803d3a07ad7e4f246aed"
|
||||
url = "https://github.com/pnpm/pnpm/releases/download/v11.13.1/pnpm-linux-arm64.tar.gz"
|
||||
url_api = "https://api.github.com/repos/pnpm/pnpm/releases/assets/478563983"
|
||||
provenance = "github-attestations"
|
||||
|
||||
[tools.pnpm."platforms.linux-arm64-musl"]
|
||||
checksum = "sha256:56a78c08cf22adf29e7dacb6f7f100139731693863d20fb94a7883463a62169e"
|
||||
url = "https://github.com/pnpm/pnpm/releases/download/v11.6.0/pnpm-linux-arm64-musl.tar.gz"
|
||||
url_api = "https://api.github.com/repos/pnpm/pnpm/releases/assets/445120177"
|
||||
checksum = "sha256:2cadd4fc815c591f498a0a84c9e74a836e3e8c1236275f6e1cfd355bae6ae957"
|
||||
url = "https://github.com/pnpm/pnpm/releases/download/v11.13.1/pnpm-linux-arm64-musl.tar.gz"
|
||||
url_api = "https://api.github.com/repos/pnpm/pnpm/releases/assets/478563985"
|
||||
provenance = "github-attestations"
|
||||
|
||||
[tools.pnpm."platforms.linux-x64"]
|
||||
checksum = "sha256:74d64c1646385fb21691f32f0ab6aca1a9f5c829ba54d3cda3a24838a228e68c"
|
||||
url = "https://github.com/pnpm/pnpm/releases/download/v11.6.0/pnpm-linux-x64.tar.gz"
|
||||
url_api = "https://api.github.com/repos/pnpm/pnpm/releases/assets/445120176"
|
||||
checksum = "sha256:bd6d4b0e14536207ad76bc838f5980cecd968da15f69aae0b207380cca3f2e98"
|
||||
url = "https://github.com/pnpm/pnpm/releases/download/v11.13.1/pnpm-linux-x64.tar.gz"
|
||||
url_api = "https://api.github.com/repos/pnpm/pnpm/releases/assets/478563986"
|
||||
provenance = "github-attestations"
|
||||
|
||||
[tools.pnpm."platforms.linux-x64-musl"]
|
||||
checksum = "sha256:7a0c463a09d912fba6b7d9eca0a946bc228ea50f3015a05c09a29e7e85a932d7"
|
||||
url = "https://github.com/pnpm/pnpm/releases/download/v11.6.0/pnpm-linux-x64-musl.tar.gz"
|
||||
url_api = "https://api.github.com/repos/pnpm/pnpm/releases/assets/445120175"
|
||||
checksum = "sha256:ba19690f4ed1b64f1203ade14e9216352b46232d5582468b26a0160e0c9618c5"
|
||||
url = "https://github.com/pnpm/pnpm/releases/download/v11.13.1/pnpm-linux-x64-musl.tar.gz"
|
||||
url_api = "https://api.github.com/repos/pnpm/pnpm/releases/assets/478563980"
|
||||
provenance = "github-attestations"
|
||||
|
||||
[tools.pnpm."platforms.macos-arm64"]
|
||||
checksum = "sha256:87c901635a14481fb30566a3749041134ffd4317bc6fe866c345b69fdf9b6b85"
|
||||
url = "https://github.com/pnpm/pnpm/releases/download/v11.6.0/pnpm-darwin-arm64.tar.gz"
|
||||
url_api = "https://api.github.com/repos/pnpm/pnpm/releases/assets/445120180"
|
||||
checksum = "sha256:765c2bf04e8129cb58c0f946e324262e418370b35a203b50b1f06a0567ef8bc1"
|
||||
url = "https://github.com/pnpm/pnpm/releases/download/v11.13.1/pnpm-darwin-arm64.tar.gz"
|
||||
url_api = "https://api.github.com/repos/pnpm/pnpm/releases/assets/478563984"
|
||||
provenance = "github-attestations"
|
||||
|
||||
[tools.pnpm."platforms.windows-x64"]
|
||||
checksum = "sha256:91c753435542b04859c689304fae0dd64eba6b40937cfa426a48485b712e4e9c"
|
||||
url = "https://github.com/pnpm/pnpm/releases/download/v11.6.0/pnpm-win32-x64.zip"
|
||||
url_api = "https://api.github.com/repos/pnpm/pnpm/releases/assets/445120173"
|
||||
checksum = "sha256:d8bebbc71df2702961c1d34a5e61196bc0aa3bbde33c33253f6afa3dd4546a6d"
|
||||
url = "https://github.com/pnpm/pnpm/releases/download/v11.13.1/pnpm-win32-x64.zip"
|
||||
url_api = "https://api.github.com/repos/pnpm/pnpm/releases/assets/478563981"
|
||||
provenance = "github-attestations"
|
||||
|
||||
[[tools.terragrunt]]
|
||||
|
||||
@@ -16,7 +16,7 @@ config_roots = [
|
||||
|
||||
[tools]
|
||||
node = "24.15.0"
|
||||
pnpm = "11.6.0"
|
||||
pnpm = "11.13.1"
|
||||
terragrunt = "1.0.3"
|
||||
opentofu = "1.11.6"
|
||||
"npm:oazapfts" = "7.5.0"
|
||||
|
||||
@@ -89,13 +89,6 @@ flutter {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
constraints {
|
||||
implementation("androidx.glance:glance-appwidget") {
|
||||
version { strictly libs.versions.glance.get() }
|
||||
because 'home_widget requests 1.+ which can resolve to pre-releases incompatible with our compileSdk/AGP'
|
||||
}
|
||||
}
|
||||
|
||||
implementation libs.okhttp
|
||||
implementation libs.cronet.embedded
|
||||
implementation libs.media3.datasource.okhttp
|
||||
|
||||
@@ -23,6 +23,6 @@ class ImmichApp : Application() {
|
||||
// as the previous start might have been killed without unlocking.
|
||||
if (BackgroundEngineLock.connectEngines > 0) return@postDelayed
|
||||
BackgroundWorkerApiImpl.enqueueBackgroundWorker(this)
|
||||
}, 5000)
|
||||
}, 15000)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import androidx.work.ListenableWorker
|
||||
import androidx.work.WorkerParameters
|
||||
import app.alextran.immich.MainActivity
|
||||
import app.alextran.immich.R
|
||||
import com.google.common.util.concurrent.Futures
|
||||
import com.google.common.util.concurrent.ListenableFuture
|
||||
import com.google.common.util.concurrent.SettableFuture
|
||||
import io.flutter.FlutterInjector
|
||||
@@ -61,6 +62,11 @@ class BackgroundWorker(context: Context, params: WorkerParameters) :
|
||||
}
|
||||
|
||||
override fun startWork(): ListenableFuture<Result> {
|
||||
if (BackgroundWorkerPreferences(ctx).isLocked() && BackgroundEngineLock.connectEngines > 0) {
|
||||
Log.i(TAG, "Foreground engine active, skipping background worker")
|
||||
return Futures.immediateFuture(Result.success())
|
||||
}
|
||||
|
||||
Log.i(TAG, "Starting background upload worker")
|
||||
|
||||
if (!loader.initialized()) {
|
||||
@@ -77,6 +83,10 @@ class BackgroundWorker(context: Context, params: WorkerParameters) :
|
||||
showNotification(notificationConfig.first, notificationConfig.second)
|
||||
|
||||
loader.ensureInitializationCompleteAsync(ctx, null, Handler(Looper.getMainLooper())) {
|
||||
if (isStopped || isComplete) {
|
||||
return@ensureInitializationCompleteAsync
|
||||
}
|
||||
|
||||
engine = FlutterEngine(ctx)
|
||||
FlutterEngineCache.getInstance().put(BackgroundWorkerApiImpl.ENGINE_CACHE_KEY, engine!!)
|
||||
|
||||
@@ -143,11 +153,17 @@ class BackgroundWorker(context: Context, params: WorkerParameters) :
|
||||
return
|
||||
}
|
||||
|
||||
val api = flutterApi
|
||||
if (api == null) {
|
||||
Handler(Looper.getMainLooper()).postAtFrontOfQueue {
|
||||
complete(Result.failure())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
Handler(Looper.getMainLooper()).postAtFrontOfQueue {
|
||||
if (flutterApi != null) {
|
||||
flutterApi?.cancel {
|
||||
complete(Result.failure())
|
||||
}
|
||||
api.cancel {
|
||||
complete(Result.failure())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -64,13 +64,7 @@ fun Bitmap.toNativeBuffer(): Map<String, Long> {
|
||||
// native convert declined (OOM/lock) -> fall through to the Skia copy path below.
|
||||
}
|
||||
// Other non-8888 configs (e.g. HDR F16) still need Skia's convert; 8-bit is copied as-is.
|
||||
val bitmap = if (config != Bitmap.Config.ARGB_8888) {
|
||||
val converted = copy(Bitmap.Config.ARGB_8888, false)
|
||||
recycle()
|
||||
converted ?: throw IOException("could not convert bitmap to ARGB_8888")
|
||||
} else {
|
||||
this
|
||||
}
|
||||
val bitmap = toArgb8888()
|
||||
val size = bitmap.width * bitmap.height * 4
|
||||
val pointer = NativeBuffer.allocate(size)
|
||||
try {
|
||||
@@ -217,7 +211,7 @@ class LocalImagesImpl(context: Context) : LocalImageApi {
|
||||
if (orientation == ExifInterface.ORIENTATION_NORMAL || orientation == ExifInterface.ORIENTATION_UNDEFINED) {
|
||||
bitmap.toNativeBuffer()
|
||||
} else {
|
||||
rotateToNativeBuffer(bitmap, orientation, signal)
|
||||
rotateToNativeBuffer(bitmap, orientation)
|
||||
}
|
||||
}
|
||||
// Don't re-check cancellation here: res owns a malloc'd buffer, and bailing to CANCELLED would
|
||||
@@ -257,39 +251,7 @@ class LocalImagesImpl(context: Context) : LocalImageApi {
|
||||
}
|
||||
|
||||
private fun rawOrientation(uri: Uri): Int {
|
||||
return resolver.openInputStream(uri)?.use {
|
||||
ExifInterface(it).getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL)
|
||||
} ?: ExifInterface.ORIENTATION_NORMAL
|
||||
}
|
||||
|
||||
// ImageDecoder / loadThumbnail skip EXIF orientation for raw (e.g. DNG) on Q+, so the decoded
|
||||
// bitmap comes back unrotated. Rotate it into the output buffer in native code (one pass, no
|
||||
// intermediate rotated bitmap).
|
||||
private fun rotateToNativeBuffer(bitmap: Bitmap, orientation: Int, signal: CancellationSignal): Map<String, Long> {
|
||||
signal.throwIfCanceled()
|
||||
// Force ARGB_8888: the native rotate needs a lockable 8888 buffer, and toNativeBuffer() below
|
||||
// allocates width*height*4 (an F16/HDR decode would otherwise under-allocate). No-op for the
|
||||
// common already-8888 case.
|
||||
val src = if (bitmap.config != Bitmap.Config.ARGB_8888) {
|
||||
val converted = bitmap.copy(Bitmap.Config.ARGB_8888, false)
|
||||
bitmap.recycle()
|
||||
converted ?: throw IOException("could not convert bitmap to ARGB_8888")
|
||||
} else {
|
||||
bitmap
|
||||
}
|
||||
try {
|
||||
val info = IntArray(3)
|
||||
val pointer = NativeImage.rotate(src, orientation, info)
|
||||
if (pointer == 0L) throw IOException("native rotate failed for orientation $orientation")
|
||||
return mapOf(
|
||||
"pointer" to pointer,
|
||||
"width" to info[0].toLong(),
|
||||
"height" to info[1].toLong(),
|
||||
"rowBytes" to info[2].toLong()
|
||||
)
|
||||
} finally {
|
||||
if (!src.isRecycled) src.recycle()
|
||||
}
|
||||
return resolver.openInputStream(uri)?.use { readOrientation(it) } ?: ExifInterface.ORIENTATION_NORMAL
|
||||
}
|
||||
|
||||
private fun decodeVideoThumbnail(id: Long, target: Size, signal: CancellationSignal): Bitmap {
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
package app.alextran.immich.images
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import androidx.exifinterface.media.ExifInterface
|
||||
import app.alextran.immich.NativeImage
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.ByteOrder
|
||||
|
||||
fun readOrientation(input: InputStream): Int =
|
||||
ExifInterface(input).getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL)
|
||||
|
||||
// Reads the EXIF orientation (TIFF tag 0x0112) from a raw file's IFD0.
|
||||
fun readRawOrientation(buffer: ByteBuffer, len: Int): Int {
|
||||
try {
|
||||
if (len < 8) return ExifInterface.ORIENTATION_NORMAL
|
||||
buffer.order(
|
||||
when (buffer.get(0).toInt() and 0xFF) {
|
||||
0x49 -> ByteOrder.LITTLE_ENDIAN
|
||||
0x4D -> ByteOrder.BIG_ENDIAN
|
||||
else -> return ExifInterface.ORIENTATION_NORMAL
|
||||
}
|
||||
)
|
||||
val ifd0 = buffer.getInt(4).toLong() and 0xFFFFFFFFL
|
||||
if (ifd0 < 8 || ifd0 + 2 > len) return ExifInterface.ORIENTATION_NORMAL
|
||||
val start = ifd0.toInt()
|
||||
val entries = buffer.getShort(start).toInt() and 0xFFFF
|
||||
if (entries <= 0 || entries > 512) return ExifInterface.ORIENTATION_NORMAL
|
||||
// Each IFD entry is 12 bytes: tag(2) type(2) count(4) value(4); orientation is a SHORT in value.
|
||||
for (i in 0 until entries) {
|
||||
val p = start + 2 + i * 12
|
||||
if (p + 12 > len) return ExifInterface.ORIENTATION_NORMAL
|
||||
if ((buffer.getShort(p).toInt() and 0xFFFF) == 0x0112) {
|
||||
val value = buffer.getShort(p + 8).toInt() and 0xFFFF
|
||||
return if (value in 1..8) value else ExifInterface.ORIENTATION_NORMAL
|
||||
}
|
||||
}
|
||||
return ExifInterface.ORIENTATION_NORMAL
|
||||
} catch (_: Exception) {
|
||||
return ExifInterface.ORIENTATION_NORMAL
|
||||
}
|
||||
}
|
||||
|
||||
fun Bitmap.toArgb8888(): Bitmap {
|
||||
if (config == Bitmap.Config.ARGB_8888) return this
|
||||
val converted = copy(Bitmap.Config.ARGB_8888, false)
|
||||
recycle()
|
||||
return converted ?: throw IOException("could not convert bitmap to ARGB_8888")
|
||||
}
|
||||
|
||||
// Force ARGB_8888 first: the native rotate needs a lockable 8888 buffer and allocates w*h*4.
|
||||
fun rotateToNativeBuffer(bitmap: Bitmap, orientation: Int): Map<String, Long> {
|
||||
val src = bitmap.toArgb8888()
|
||||
try {
|
||||
val info = IntArray(3)
|
||||
val pointer = NativeImage.rotate(src, orientation, info)
|
||||
if (pointer == 0L) throw IOException("native rotate failed for orientation $orientation")
|
||||
return mapOf(
|
||||
"pointer" to pointer,
|
||||
"width" to info[0].toLong(),
|
||||
"height" to info[1].toLong(),
|
||||
"rowBytes" to info[2].toLong()
|
||||
)
|
||||
} finally {
|
||||
if (!src.isRecycled) src.recycle()
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import android.graphics.ImageDecoder
|
||||
import android.os.Build
|
||||
import android.os.CancellationSignal
|
||||
import android.os.OperationCanceledException
|
||||
import androidx.exifinterface.media.ExifInterface
|
||||
import app.alextran.immich.INITIAL_BUFFER_SIZE
|
||||
import app.alextran.immich.NativeBuffer
|
||||
import app.alextran.immich.NativeByteBuffer
|
||||
@@ -30,6 +31,35 @@ private const val MAX_PREALLOC_BYTES = 128 * 1024 * 1024
|
||||
|
||||
private class RemoteRequest(val cancellationSignal: CancellationSignal)
|
||||
|
||||
// The full mimeTypes.raw set the server can serve (short + vendor forms).
|
||||
private val RAW_MIME_TYPES = setOf(
|
||||
"image/3fr", "image/ari", "image/arw",
|
||||
"image/cap", "image/cin", "image/cr2",
|
||||
"image/cr3", "image/crw", "image/dcr",
|
||||
"image/dng", "image/erf", "image/fff",
|
||||
"image/iiq", "image/k25", "image/kdc",
|
||||
"image/mrw", "image/nef", "image/nrw",
|
||||
"image/orf", "image/ori", "image/pef",
|
||||
"image/psd", "image/raf", "image/raw",
|
||||
"image/rw2", "image/rwl", "image/sr2",
|
||||
"image/srf", "image/srw", "image/vnd.adobe.photoshop",
|
||||
"image/x-adobe-dng", "image/x-arriflex-ari", "image/x-canon-cr2",
|
||||
"image/x-canon-cr3", "image/x-canon-crw", "image/x-epson-erf",
|
||||
"image/x-fuji-raf", "image/x-hasselblad-3fr", "image/x-hasselblad-fff",
|
||||
"image/x-kodak-dcr", "image/x-kodak-k25", "image/x-kodak-kdc",
|
||||
"image/x-leica-rwl", "image/x-minolta-mrw", "image/x-nikon-nef",
|
||||
"image/x-nikon-nrw", "image/x-olympus-orf", "image/x-olympus-ori",
|
||||
"image/x-panasonic-raw", "image/x-panasonic-rw2", "image/x-pentax-pef",
|
||||
"image/x-phantom-cin", "image/x-phaseone-cap", "image/x-phaseone-iiq",
|
||||
"image/x-samsung-srw", "image/x-sigma-x3f", "image/x-sony-arw",
|
||||
"image/x-sony-sr2", "image/x-sony-srf", "image/x3f",
|
||||
)
|
||||
|
||||
private fun isRawMime(contentType: String?): Boolean {
|
||||
val mime = contentType?.substringBefore(';')?.trim()?.lowercase() ?: return false
|
||||
return mime in RAW_MIME_TYPES
|
||||
}
|
||||
|
||||
class RemoteImagesImpl(context: Context) : RemoteImageApi {
|
||||
private val requestMap = ConcurrentHashMap<Long, RemoteRequest>()
|
||||
|
||||
@@ -57,7 +87,7 @@ class RemoteImagesImpl(context: Context) : RemoteImageApi {
|
||||
ImageFetcherManager.fetch(
|
||||
url,
|
||||
signal,
|
||||
onSuccess = { buffer ->
|
||||
onSuccess = { buffer, contentType ->
|
||||
if (signal.isCanceled) {
|
||||
requestMap.remove(requestId)
|
||||
buffer.free()
|
||||
@@ -72,8 +102,18 @@ class RemoteImagesImpl(context: Context) : RemoteImageApi {
|
||||
if (!preferEncoded && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
decodeExecutor.execute {
|
||||
val res = if (signal.isCanceled) null else try {
|
||||
val source = ImageDecoder.createSource(NativeBuffer.wrap(buffer.pointer, buffer.offset))
|
||||
source.decodeBitmap().toNativeBuffer()
|
||||
val bitmap = ImageDecoder.createSource(NativeBuffer.wrap(buffer.pointer, buffer.offset)).decodeBitmap()
|
||||
// The embedded preview a raw decodes to has no orientation, so read the container's.
|
||||
val orientation = if (isRawMime(contentType)) {
|
||||
readRawOrientation(NativeBuffer.wrap(buffer.pointer, buffer.offset), buffer.offset)
|
||||
} else {
|
||||
ExifInterface.ORIENTATION_NORMAL
|
||||
}
|
||||
if (orientation == ExifInterface.ORIENTATION_NORMAL || orientation == ExifInterface.ORIENTATION_UNDEFINED) {
|
||||
bitmap.toNativeBuffer()
|
||||
} else {
|
||||
rotateToNativeBuffer(bitmap, orientation)
|
||||
}
|
||||
} catch (_: Throwable) {
|
||||
null
|
||||
}
|
||||
@@ -154,7 +194,7 @@ private object ImageFetcherManager {
|
||||
fun fetch(
|
||||
url: String,
|
||||
signal: CancellationSignal,
|
||||
onSuccess: (NativeByteBuffer) -> Unit,
|
||||
onSuccess: (NativeByteBuffer, String?) -> Unit,
|
||||
onFailure: (Exception) -> Unit,
|
||||
) {
|
||||
fetcher.fetch(url, signal, onSuccess, onFailure)
|
||||
@@ -185,7 +225,7 @@ private sealed interface ImageFetcher {
|
||||
fun fetch(
|
||||
url: String,
|
||||
signal: CancellationSignal,
|
||||
onSuccess: (NativeByteBuffer) -> Unit,
|
||||
onSuccess: (NativeByteBuffer, String?) -> Unit,
|
||||
onFailure: (Exception) -> Unit,
|
||||
)
|
||||
|
||||
@@ -203,7 +243,7 @@ private class CronetImageFetcher : ImageFetcher {
|
||||
override fun fetch(
|
||||
url: String,
|
||||
signal: CancellationSignal,
|
||||
onSuccess: (NativeByteBuffer) -> Unit,
|
||||
onSuccess: (NativeByteBuffer, String?) -> Unit,
|
||||
onFailure: (Exception) -> Unit,
|
||||
) {
|
||||
synchronized(stateLock) {
|
||||
@@ -271,7 +311,7 @@ private class CronetImageFetcher : ImageFetcher {
|
||||
}
|
||||
|
||||
private class FetchCallback(
|
||||
private val onSuccess: (NativeByteBuffer) -> Unit,
|
||||
private val onSuccess: (NativeByteBuffer, String?) -> Unit,
|
||||
private val onFailure: (Exception) -> Unit,
|
||||
private val onComplete: () -> Unit,
|
||||
) : UrlRequest.Callback() {
|
||||
@@ -328,7 +368,9 @@ private class CronetImageFetcher : ImageFetcher {
|
||||
}
|
||||
|
||||
override fun onSucceeded(request: UrlRequest, info: UrlResponseInfo) {
|
||||
onSuccess(buffer!!)
|
||||
val contentType = info.allHeaders.entries
|
||||
.firstOrNull { it.key.equals("content-type", ignoreCase = true) }?.value?.firstOrNull()
|
||||
onSuccess(buffer!!, contentType)
|
||||
onComplete()
|
||||
}
|
||||
|
||||
@@ -379,7 +421,7 @@ private class OkHttpImageFetcher private constructor(
|
||||
override fun fetch(
|
||||
url: String,
|
||||
signal: CancellationSignal,
|
||||
onSuccess: (NativeByteBuffer) -> Unit,
|
||||
onSuccess: (NativeByteBuffer, String?) -> Unit,
|
||||
onFailure: (Exception) -> Unit,
|
||||
) {
|
||||
synchronized(stateLock) {
|
||||
@@ -433,7 +475,7 @@ private class OkHttpImageFetcher private constructor(
|
||||
buffer.ensureHeadroom()
|
||||
}
|
||||
}
|
||||
onSuccess(buffer)
|
||||
onSuccess(buffer, response.header("Content-Type"))
|
||||
} catch (e: Exception) {
|
||||
buffer.free()
|
||||
onFailure(e)
|
||||
|
||||
@@ -22,8 +22,8 @@ platform :android do
|
||||
task: 'bundle',
|
||||
build_type: 'Release',
|
||||
properties: {
|
||||
"android.injected.version.code" => 3055,
|
||||
"android.injected.version.name" => "3.0.2",
|
||||
"android.injected.version.code" => 3056,
|
||||
"android.injected.version.name" => "3.0.3",
|
||||
}
|
||||
)
|
||||
upload_to_play_store(skip_upload_apk: true, skip_upload_images: true, skip_upload_screenshots: true, aab: '../build/app/outputs/bundle/release/app-release.aab', track: 'beta')
|
||||
@@ -35,8 +35,8 @@ platform :android do
|
||||
task: 'bundle',
|
||||
build_type: 'Release',
|
||||
properties: {
|
||||
"android.injected.version.code" => 3055,
|
||||
"android.injected.version.name" => "3.0.2",
|
||||
"android.injected.version.code" => 3056,
|
||||
"android.injected.version.name" => "3.0.3",
|
||||
}
|
||||
)
|
||||
upload_to_play_store(skip_upload_apk: true, skip_upload_images: true, skip_upload_screenshots: true, aab: '../build/app/outputs/bundle/release/app-release.aab')
|
||||
|
||||
@@ -46,7 +46,7 @@ void main() {
|
||||
|
||||
void sendUser(SyncStream stream, String id, String name) {
|
||||
stream.send(
|
||||
type: SyncEntityType.userV1.value,
|
||||
type: SyncEntityType.userV1.toString(),
|
||||
data: SyncUserV1(
|
||||
id: id,
|
||||
name: name,
|
||||
|
||||
3
mobile/ios/.gitignore
vendored
3
mobile/ios/.gitignore
vendored
@@ -26,6 +26,9 @@ Flutter/flutter_export_environment.sh
|
||||
ServiceDefinitions.json
|
||||
Runner/GeneratedPluginRegistrant.*
|
||||
|
||||
# Local signing overrides.
|
||||
Signing.local.xcconfig
|
||||
|
||||
# Exceptions to above rules.
|
||||
!default.mode1v3
|
||||
!default.mode2v3
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Uncomment this line to define a global platform for your project
|
||||
platform :ios, '14.0'
|
||||
platform :ios, '15.0'
|
||||
|
||||
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
|
||||
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
|
||||
@@ -45,7 +45,7 @@ post_install do |installer|
|
||||
installer.generated_projects.each do |project|
|
||||
project.targets.each do |target|
|
||||
target.build_configurations.each do |config|
|
||||
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '14.0'
|
||||
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '15.0'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -12,12 +12,8 @@ PODS:
|
||||
- Flutter
|
||||
- fluttertoast (0.0.2):
|
||||
- Flutter
|
||||
- home_widget (0.0.1):
|
||||
- Flutter
|
||||
- native_video_player (1.0.0):
|
||||
- Flutter
|
||||
- permission_handler_apple (9.3.0):
|
||||
- Flutter
|
||||
- share_handler_ios (0.0.14):
|
||||
- Flutter
|
||||
- share_handler_ios/share_handler_ios_models (= 0.0.14)
|
||||
@@ -34,9 +30,7 @@ DEPENDENCIES:
|
||||
- flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`)
|
||||
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
|
||||
- fluttertoast (from `.symlinks/plugins/fluttertoast/ios`)
|
||||
- home_widget (from `.symlinks/plugins/home_widget/ios`)
|
||||
- native_video_player (from `.symlinks/plugins/native_video_player/ios`)
|
||||
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
||||
- share_handler_ios (from `.symlinks/plugins/share_handler_ios/ios`)
|
||||
- share_handler_ios_models (from `.symlinks/plugins/share_handler_ios/ios/Models`)
|
||||
|
||||
@@ -53,12 +47,8 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/flutter_secure_storage/ios"
|
||||
fluttertoast:
|
||||
:path: ".symlinks/plugins/fluttertoast/ios"
|
||||
home_widget:
|
||||
:path: ".symlinks/plugins/home_widget/ios"
|
||||
native_video_player:
|
||||
:path: ".symlinks/plugins/native_video_player/ios"
|
||||
permission_handler_apple:
|
||||
:path: ".symlinks/plugins/permission_handler_apple/ios"
|
||||
share_handler_ios:
|
||||
:path: ".symlinks/plugins/share_handler_ios/ios"
|
||||
share_handler_ios_models:
|
||||
@@ -71,12 +61,10 @@ SPEC CHECKSUMS:
|
||||
flutter_local_notifications: ad39620c743ea4c15127860f4b5641649a988100
|
||||
flutter_secure_storage: 1ed9476fba7e7a782b22888f956cce43e2c62f13
|
||||
fluttertoast: 2c67e14dce98bbdb200df9e1acf610d7a6264ea1
|
||||
home_widget: f169fc41fd807b4d46ab6615dc44d62adbf9f64f
|
||||
native_video_player: b65c58951ede2f93d103a25366bdebca95081265
|
||||
permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
|
||||
share_handler_ios: e2244e990f826b2c8eaa291ac3831569438ba0fb
|
||||
share_handler_ios_models: fc638c9b4330dc7f082586c92aee9dfa0b87b871
|
||||
|
||||
PODFILE CHECKSUM: 938abbae4114b9c2140c550a2a0d8f7c674f5dfe
|
||||
PODFILE CHECKSUM: 3c43a700a4bffb4120bf696cad263aefd4bb3c8c
|
||||
|
||||
COCOAPODS: 1.16.2
|
||||
|
||||
@@ -96,6 +96,7 @@
|
||||
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterGeneratedPluginSwiftPackage; path = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; sourceTree = "<group>"; };
|
||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
|
||||
51516E0F2A0000000000C0F1 /* Signing.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Signing.xcconfig; sourceTree = "<group>"; };
|
||||
8AB817AA297EDEC88B23F3F6 /* Pods_ShareExtension.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ShareExtension.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
937632897A02DE9C249F20A6 /* Pods-ShareExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ShareExtension.debug.xcconfig"; path = "Target Support Files/Pods-ShareExtension/Pods-ShareExtension.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
|
||||
@@ -153,11 +154,15 @@
|
||||
/* Begin PBXFileSystemSynchronizedRootGroup section */
|
||||
B231F52D2E93A44A00BC45D1 /* Core */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
exceptions = (
|
||||
);
|
||||
path = Core;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B2CF7F8C2DDE4EBB00744BF6 /* Sync */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
exceptions = (
|
||||
);
|
||||
path = Sync;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
@@ -179,6 +184,8 @@
|
||||
};
|
||||
FEE084F22EC172080045228E /* Schemas */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
exceptions = (
|
||||
);
|
||||
path = Schemas;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
@@ -256,6 +263,7 @@
|
||||
97C146E51CF9000F007C117D = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
51516E0F2A0000000000C0F1 /* Signing.xcconfig */,
|
||||
9740EEB11CF90186004384FC /* Flutter */,
|
||||
97C146F01CF9000F007C117D /* Runner */,
|
||||
FAC6F8B62D287F120078CB2F /* ShareExtension */,
|
||||
@@ -370,7 +378,6 @@
|
||||
FAC6F89A2D287C890078CB2F /* Embed Foundation Extensions */,
|
||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
||||
513DA7292DED6106813332F4 /* [CP] Embed Pods Frameworks */,
|
||||
2FA39DEC809D6D7C4A01EFCB /* [CP] Copy Pods Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@@ -464,7 +471,7 @@
|
||||
);
|
||||
mainGroup = 97C146E51CF9000F007C117D;
|
||||
packageReferences = (
|
||||
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */,
|
||||
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */,
|
||||
FEE084F62EC172460045228E /* XCRemoteSwiftPackageReference "sqlite-data" */,
|
||||
FEE084F92EC1725A0045228E /* XCRemoteSwiftPackageReference "swift-http-structured-headers" */,
|
||||
);
|
||||
@@ -511,27 +518,6 @@
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
2FA39DEC809D6D7C4A01EFCB /* [CP] Copy Pods Resources */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
@@ -556,14 +542,10 @@
|
||||
inputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
|
||||
@@ -604,7 +586,7 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n";
|
||||
shellScript = "/bin/bash \"$SRCROOT/scripts/xcode_flutter_build.sh\"\n";
|
||||
};
|
||||
BAEA01ACA3F5C9CD3D732370 /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
@@ -714,6 +696,7 @@
|
||||
/* Begin XCBuildConfiguration section */
|
||||
249021D3217E4FDB00AE95B9 /* Profile */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 51516E0F2A0000000000C0F1 /* Signing.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
@@ -744,7 +727,7 @@
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CUSTOM_GROUP_ID = group.app.immich.share.profile;
|
||||
CUSTOM_GROUP_ID = "$(IMMICH_GROUP_ID).profile";
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
@@ -757,7 +740,7 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
NEW_SETTING = "";
|
||||
SDKROOT = iphoneos;
|
||||
@@ -777,16 +760,16 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 4;
|
||||
DEVELOPMENT_TEAM = 2W7AC6T8T5;
|
||||
DEVELOPMENT_TEAM = "$(IMMICH_TEAM_ID)";
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 3.0.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.futo.immich.profile;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "$(IMMICH_BUNDLE_ID_DEV).profile";
|
||||
PRODUCT_NAME = "Immich-Profile";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
@@ -797,6 +780,7 @@
|
||||
};
|
||||
97C147031CF9000F007C117D /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 51516E0F2A0000000000C0F1 /* Signing.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
|
||||
@@ -827,7 +811,7 @@
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CUSTOM_GROUP_ID = group.app.immich.share.debug;
|
||||
CUSTOM_GROUP_ID = "$(IMMICH_GROUP_ID).debug";
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
@@ -846,7 +830,7 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
NEW_SETTING = "";
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
@@ -857,6 +841,7 @@
|
||||
};
|
||||
97C147041CF9000F007C117D /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 51516E0F2A0000000000C0F1 /* Signing.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
|
||||
@@ -887,7 +872,7 @@
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CUSTOM_GROUP_ID = group.app.immich.share;
|
||||
CUSTOM_GROUP_ID = "$(IMMICH_GROUP_ID)";
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
@@ -900,7 +885,7 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
NEW_SETTING = "";
|
||||
SDKROOT = iphoneos;
|
||||
@@ -922,16 +907,16 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 4;
|
||||
DEVELOPMENT_TEAM = 2W7AC6T8T5;
|
||||
DEVELOPMENT_TEAM = "$(IMMICH_TEAM_ID)";
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 3.0.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.futo.immich.debug;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "$(IMMICH_BUNDLE_ID_DEV).debug";
|
||||
PRODUCT_NAME = "Immich-Debug";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
@@ -951,16 +936,16 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 4;
|
||||
DEVELOPMENT_TEAM = 2W7AC6T8T5;
|
||||
DEVELOPMENT_TEAM = "$(IMMICH_TEAM_ID)";
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 3.0.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.alextran.immich;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "$(IMMICH_BUNDLE_ID_PROD)";
|
||||
PRODUCT_NAME = Immich;
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
@@ -984,7 +969,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 240;
|
||||
DEVELOPMENT_TEAM = 2W7AC6T8T5;
|
||||
DEVELOPMENT_TEAM = "$(IMMICH_TEAM_ID)";
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -1001,7 +986,7 @@
|
||||
MARKETING_VERSION = 1.0;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.futo.immich.debug.Widget;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "$(IMMICH_BUNDLE_ID_DEV).debug.Widget";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
|
||||
@@ -1027,7 +1012,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 240;
|
||||
DEVELOPMENT_TEAM = 2W7AC6T8T5;
|
||||
DEVELOPMENT_TEAM = "$(IMMICH_TEAM_ID)";
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -1043,7 +1028,7 @@
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MARKETING_VERSION = 1.0;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.alextran.immich.Widget;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "$(IMMICH_BUNDLE_ID_PROD).Widget";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
@@ -1067,7 +1052,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 240;
|
||||
DEVELOPMENT_TEAM = 2W7AC6T8T5;
|
||||
DEVELOPMENT_TEAM = "$(IMMICH_TEAM_ID)";
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -1083,7 +1068,7 @@
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MARKETING_VERSION = 1.0;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.futo.immich.profile.Widget;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "$(IMMICH_BUNDLE_ID_DEV).profile.Widget";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
@@ -1106,7 +1091,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 240;
|
||||
DEVELOPMENT_TEAM = 2W7AC6T8T5;
|
||||
DEVELOPMENT_TEAM = "$(IMMICH_TEAM_ID)";
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -1123,7 +1108,7 @@
|
||||
MARKETING_VERSION = 1.0;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.futo.immich.debug.ShareExtension;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "$(IMMICH_BUNDLE_ID_DEV).debug.ShareExtension";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SKIP_INSTALL = YES;
|
||||
@@ -1149,7 +1134,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 240;
|
||||
DEVELOPMENT_TEAM = 2W7AC6T8T5;
|
||||
DEVELOPMENT_TEAM = "$(IMMICH_TEAM_ID)";
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -1165,7 +1150,7 @@
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MARKETING_VERSION = 1.0;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.alextran.immich.ShareExtension;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "$(IMMICH_BUNDLE_ID_PROD).ShareExtension";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SKIP_INSTALL = YES;
|
||||
@@ -1189,7 +1174,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 240;
|
||||
DEVELOPMENT_TEAM = 2W7AC6T8T5;
|
||||
DEVELOPMENT_TEAM = "$(IMMICH_TEAM_ID)";
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -1205,7 +1190,7 @@
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MARKETING_VERSION = 1.0;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.futo.immich.profile.ShareExtension;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "$(IMMICH_BUNDLE_ID_DEV).profile.ShareExtension";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SKIP_INSTALL = YES;
|
||||
@@ -1261,7 +1246,7 @@
|
||||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCLocalSwiftPackageReference section */
|
||||
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */ = {
|
||||
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = {
|
||||
isa = XCLocalSwiftPackageReference;
|
||||
relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage;
|
||||
};
|
||||
|
||||
@@ -32,8 +32,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/maplibre/maplibre-gl-native-distribution.git",
|
||||
"state" : {
|
||||
"revision" : "60d9bb85c94ce6e7fc4406cd32529fd12bdb7809",
|
||||
"version" : "6.14.0"
|
||||
"revision" : "84a79bc375a301169390ac110c868f06c857b83f",
|
||||
"version" : "6.27.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -122,8 +122,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/pointfreeco/swift-sharing",
|
||||
"state" : {
|
||||
"revision" : "3bfc408cc2d0bee2287c174da6b1c76768377818",
|
||||
"version" : "2.7.4"
|
||||
"revision" : "8244fe63bf43e58188ab13851ad693eecf6a9e90",
|
||||
"version" : "2.9.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -43,15 +43,16 @@ class ConnectivityApiImpl: ConnectivityApi {
|
||||
capabilities.append(.vpn)
|
||||
}
|
||||
|
||||
// Determine if connection is unmetered:
|
||||
// - Must be on WiFi (not cellular)
|
||||
// - Must not be expensive (rules out personal hotspot)
|
||||
// - Must not be constrained (Low Data Mode)
|
||||
// Note: VPN over cellular should still be considered metered
|
||||
// Determine if connection is unmetered from the OS metered flags rather than
|
||||
// the interface type, so wired ethernet (iPhone USB adapters, Apple Silicon
|
||||
// Macs) is treated as unmetered like Wi-Fi:
|
||||
// - Not on cellular
|
||||
// - Not expensive (also rules out cellular and personal hotspot)
|
||||
// - Not constrained (Low Data Mode)
|
||||
// Note: VPN over cellular stays metered because the path is still expensive.
|
||||
let isOnCellular = path.usesInterfaceType(.cellular)
|
||||
let isOnWifi = path.usesInterfaceType(.wifi)
|
||||
|
||||
if isOnWifi && !isOnCellular && !path.isExpensive && !path.isConstrained {
|
||||
|
||||
if !isOnCellular && !path.isExpensive && !path.isConstrained {
|
||||
capabilities.append(.unmetered)
|
||||
}
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>3.0.2</string>
|
||||
<string>3.0.3</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
|
||||
29
mobile/ios/Signing.xcconfig
Normal file
29
mobile/ios/Signing.xcconfig
Normal file
@@ -0,0 +1,29 @@
|
||||
// Xcode Signing Constants
|
||||
//
|
||||
// These are integrated into the Xcode project at build time to allow for dynamic signing configs
|
||||
// across our many targets.
|
||||
//
|
||||
// Create `Signing.local.xcconfig` overriding each variable to provide your own personal signing
|
||||
// overrides during local development:
|
||||
// IMMICH_TEAM_ID = ABCDE12345
|
||||
// IMMICH_BUNDLE_ID_PROD = com.customuniqueid.immich
|
||||
// IMMICH_BUNDLE_ID_DEV = com.customuniqueid.immichdev
|
||||
// IMMICH_GROUP_ID = group.com.customuniqueid.immich
|
||||
|
||||
// Apple Developer Team ID used for automatically provisioning signing credentials
|
||||
IMMICH_TEAM_ID = 2W7AC6T8T5
|
||||
|
||||
// Root bundle identifier for the Release configuration.
|
||||
// Extension suffixes (`.ShareExtension`/`.Widget`) will be appended
|
||||
IMMICH_BUNDLE_ID_PROD = app.alextran.immich
|
||||
|
||||
// Root bundle identifier for the Debug and Profile configurations.
|
||||
// Build scheme suffixes (`.debug`/`.profile`) will be appended
|
||||
IMMICH_BUNDLE_ID_DEV = app.futo.immich
|
||||
|
||||
// Root App Group identifier.
|
||||
// Build scheme suffixes (`.debug`/`.profile`) will be appended
|
||||
IMMICH_GROUP_ID = group.app.immich.share
|
||||
|
||||
// Optional per-developer configuration override
|
||||
#include? "Signing.local.xcconfig"
|
||||
45
mobile/ios/scripts/xcode_flutter_build.sh
Executable file
45
mobile/ios/scripts/xcode_flutter_build.sh
Executable file
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env bash
|
||||
# Makes Flutter's builds through the Xcode GUI properly display errors and warnings
|
||||
# in the Issue navigator
|
||||
#
|
||||
# Flutter's `xcode_backend.dart` runs `flutter assemble` with `allowFail: true`,
|
||||
# which intentionally does not prefix output with `error:`. Unsure why they do this,
|
||||
# but this script rebuilds the expected output so Xcode can parse and display the errors
|
||||
|
||||
set -o pipefail
|
||||
|
||||
# The Immich mobile root (containing the Dart `lib` directory). This is used to make
|
||||
# absolute paths for Xcode linking
|
||||
app_root="${FLUTTER_APPLICATION_PATH:-$SRCROOT/..}"
|
||||
|
||||
/bin/sh "$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" build 2>&1 \
|
||||
| awk -v app_root="$app_root" '
|
||||
# Match Dart CFE diagnostics: <path>.dart:<line>:<col>: <Kind>: <message>
|
||||
# Written for macOS/POSIX/BSD awk
|
||||
{
|
||||
# Always pass the original line through to preserve the original build log
|
||||
print
|
||||
|
||||
if ($0 ~ /^.*\.dart:[0-9]+:[0-9]+: (Error|Warning|Context|Info):/) {
|
||||
# Locate the ": Kind:" separator to split location from message.
|
||||
rest = $0
|
||||
if (match(rest, /: Error:/)) { kind = "Error"; keyword = "error" }
|
||||
else if (match(rest, /: Warning:/)) { kind = "Warning"; keyword = "warning" }
|
||||
else if (match(rest, /: Context:/)) { kind = "Context"; keyword = "note" }
|
||||
else if (match(rest, /: Info:/)) { kind = "Info"; keyword = "note" }
|
||||
|
||||
# location = everything before ": Kind:" (e.g. "lib/foo.dart:12:5")
|
||||
location = substr(rest, 1, RSTART - 1)
|
||||
# message = everything after ": Kind:" (leading space preserved)
|
||||
message = substr(rest, RSTART + length(": " kind ":"))
|
||||
|
||||
# Make the path absolute so Xcode links to it
|
||||
if (location !~ /^\//)
|
||||
location = app_root "/" location
|
||||
|
||||
printf "%s: %s:%s\n", location, keyword, message
|
||||
}
|
||||
}
|
||||
'
|
||||
|
||||
exit "${PIPESTATUS[0]}"
|
||||
@@ -26,7 +26,6 @@ const String kDownloadGroupLivePhoto = 'group_livephoto';
|
||||
const String kShareDownloadGroup = 'group_share';
|
||||
|
||||
// Timeline constants
|
||||
const int kTimelineNoneSegmentSize = 120;
|
||||
const int kTimelineAssetLoadBatchSize = 1024;
|
||||
const int kTimelineAssetLoadOppositeSize = 64;
|
||||
|
||||
|
||||
@@ -70,6 +70,10 @@ class RemoteAsset extends BaseAsset {
|
||||
|
||||
bool get isTrashed => deletedAt != null;
|
||||
|
||||
bool get isStacked => stackId != null;
|
||||
|
||||
bool get isArchived => visibility == .archive;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '''Asset {
|
||||
|
||||
@@ -159,8 +159,8 @@ class RemoteAlbumService {
|
||||
return updatedAlbum;
|
||||
}
|
||||
|
||||
FutureOr<(DateTime, DateTime)> getDateRange(String albumId) {
|
||||
return _repository.getDateRange(albumId);
|
||||
Stream<(DateTime, DateTime)> watchDateRange(String albumId) {
|
||||
return _repository.watchDateRange(albumId);
|
||||
}
|
||||
|
||||
Future<List<UserDto>> getSharedUsers(String albumId) {
|
||||
@@ -175,12 +175,12 @@ class RemoteAlbumService {
|
||||
return _repository.getAssets(albumId);
|
||||
}
|
||||
|
||||
Future<int> addAssets({required String albumId, required List<String> assetIds}) async {
|
||||
Future<({int added, int failed})> addAssets({required String albumId, required List<String> assetIds}) async {
|
||||
final album = await _albumApiRepository.addAssets(albumId, assetIds);
|
||||
|
||||
await _repository.addAssets(albumId, album.added);
|
||||
|
||||
return album.added.length;
|
||||
return (added: album.added.length, failed: album.failed.length);
|
||||
}
|
||||
|
||||
/// !TODO The name here is not clear as we have addAssets method above,
|
||||
@@ -196,7 +196,7 @@ class RemoteAlbumService {
|
||||
}) async {
|
||||
int addedCount = 0;
|
||||
if (candidates.remoteAssetIds.isNotEmpty) {
|
||||
addedCount += await addAssets(albumId: albumId, assetIds: candidates.remoteAssetIds);
|
||||
addedCount += (await addAssets(albumId: albumId, assetIds: candidates.remoteAssetIds)).added;
|
||||
}
|
||||
if (candidates.localAssetsToUpload.isNotEmpty) {
|
||||
addedCount += await _uploadAndAddLocals(
|
||||
|
||||
@@ -328,8 +328,6 @@ class SyncStreamService {
|
||||
return _syncStreamRepository.updateAssetOcrV1(data.cast());
|
||||
case SyncEntityType.assetOcrDeleteV1:
|
||||
return _syncStreamRepository.deleteAssetOcrV1(data.cast());
|
||||
default:
|
||||
_logger.warning("Unknown sync data type: $type");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -58,7 +58,6 @@ extension on api.AssetVisibility {
|
||||
api.AssetVisibility.hidden => AssetVisibility.hidden,
|
||||
api.AssetVisibility.archive => AssetVisibility.archive,
|
||||
api.AssetVisibility.locked => AssetVisibility.locked,
|
||||
_ => AssetVisibility.timeline,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -68,6 +67,5 @@ extension on api.AssetTypeEnum {
|
||||
api.AssetTypeEnum.VIDEO => AssetType.video,
|
||||
api.AssetTypeEnum.AUDIO => AssetType.audio,
|
||||
api.AssetTypeEnum.OTHER => AssetType.other,
|
||||
_ => throw Exception('Unknown AssetType value: $this'),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -44,16 +44,7 @@ final class $$AssetEditEntityTableReferences
|
||||
static i5.$RemoteAssetEntityTable _assetIdTable(i0.GeneratedDatabase db) =>
|
||||
i6.ReadDatabaseContainer(db)
|
||||
.resultSet<i5.$RemoteAssetEntityTable>('remote_asset_entity')
|
||||
.createAlias(
|
||||
i0.$_aliasNameGenerator(
|
||||
i6.ReadDatabaseContainer(db)
|
||||
.resultSet<i1.$AssetEditEntityTable>('asset_edit_entity')
|
||||
.assetId,
|
||||
i6.ReadDatabaseContainer(
|
||||
db,
|
||||
).resultSet<i5.$RemoteAssetEntityTable>('remote_asset_entity').id,
|
||||
),
|
||||
);
|
||||
.createAlias('asset_edit_entity__asset_id__remote_asset_entity__id');
|
||||
|
||||
i5.$$RemoteAssetEntityTableProcessedTableManager get assetId {
|
||||
final $_column = $_itemColumn<String>('asset_id')!;
|
||||
|
||||
@@ -59,16 +59,7 @@ final class $$AssetFaceEntityTableReferences
|
||||
static i4.$RemoteAssetEntityTable _assetIdTable(i0.GeneratedDatabase db) =>
|
||||
i5.ReadDatabaseContainer(db)
|
||||
.resultSet<i4.$RemoteAssetEntityTable>('remote_asset_entity')
|
||||
.createAlias(
|
||||
i0.$_aliasNameGenerator(
|
||||
i5.ReadDatabaseContainer(db)
|
||||
.resultSet<i1.$AssetFaceEntityTable>('asset_face_entity')
|
||||
.assetId,
|
||||
i5.ReadDatabaseContainer(
|
||||
db,
|
||||
).resultSet<i4.$RemoteAssetEntityTable>('remote_asset_entity').id,
|
||||
),
|
||||
);
|
||||
.createAlias('asset_face_entity__asset_id__remote_asset_entity__id');
|
||||
|
||||
i4.$$RemoteAssetEntityTableProcessedTableManager get assetId {
|
||||
final $_column = $_itemColumn<String>('asset_id')!;
|
||||
@@ -91,16 +82,7 @@ final class $$AssetFaceEntityTableReferences
|
||||
static i6.$PersonEntityTable _personIdTable(i0.GeneratedDatabase db) =>
|
||||
i5.ReadDatabaseContainer(db)
|
||||
.resultSet<i6.$PersonEntityTable>('person_entity')
|
||||
.createAlias(
|
||||
i0.$_aliasNameGenerator(
|
||||
i5.ReadDatabaseContainer(db)
|
||||
.resultSet<i1.$AssetFaceEntityTable>('asset_face_entity')
|
||||
.personId,
|
||||
i5.ReadDatabaseContainer(
|
||||
db,
|
||||
).resultSet<i6.$PersonEntityTable>('person_entity').id,
|
||||
),
|
||||
);
|
||||
.createAlias('asset_face_entity__person_id__person_entity__id');
|
||||
|
||||
i6.$$PersonEntityTableProcessedTableManager? get personId {
|
||||
final $_column = $_itemColumn<String>('person_id');
|
||||
|
||||
@@ -61,16 +61,7 @@ final class $$AssetOcrEntityTableReferences
|
||||
static i4.$RemoteAssetEntityTable _assetIdTable(i0.GeneratedDatabase db) =>
|
||||
i5.ReadDatabaseContainer(db)
|
||||
.resultSet<i4.$RemoteAssetEntityTable>('remote_asset_entity')
|
||||
.createAlias(
|
||||
i0.$_aliasNameGenerator(
|
||||
i5.ReadDatabaseContainer(
|
||||
db,
|
||||
).resultSet<i1.$AssetOcrEntityTable>('asset_ocr_entity').assetId,
|
||||
i5.ReadDatabaseContainer(
|
||||
db,
|
||||
).resultSet<i4.$RemoteAssetEntityTable>('remote_asset_entity').id,
|
||||
),
|
||||
);
|
||||
.createAlias('asset_ocr_entity__asset_id__remote_asset_entity__id');
|
||||
|
||||
i4.$$RemoteAssetEntityTableProcessedTableManager get assetId {
|
||||
final $_column = $_itemColumn<String>('asset_id')!;
|
||||
|
||||
@@ -75,16 +75,7 @@ final class $$RemoteExifEntityTableReferences
|
||||
static i3.$RemoteAssetEntityTable _assetIdTable(i0.GeneratedDatabase db) =>
|
||||
i4.ReadDatabaseContainer(db)
|
||||
.resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity')
|
||||
.createAlias(
|
||||
i0.$_aliasNameGenerator(
|
||||
i4.ReadDatabaseContainer(db)
|
||||
.resultSet<i1.$RemoteExifEntityTable>('remote_exif_entity')
|
||||
.assetId,
|
||||
i4.ReadDatabaseContainer(
|
||||
db,
|
||||
).resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity').id,
|
||||
),
|
||||
);
|
||||
.createAlias('remote_exif_entity__asset_id__remote_asset_entity__id');
|
||||
|
||||
i3.$$RemoteAssetEntityTableProcessedTableManager get assetId {
|
||||
final $_column = $_itemColumn<String>('asset_id')!;
|
||||
|
||||
@@ -50,14 +50,7 @@ final class $$LocalAlbumEntityTableReferences
|
||||
) => i6.ReadDatabaseContainer(db)
|
||||
.resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity')
|
||||
.createAlias(
|
||||
i0.$_aliasNameGenerator(
|
||||
i6.ReadDatabaseContainer(db)
|
||||
.resultSet<i1.$LocalAlbumEntityTable>('local_album_entity')
|
||||
.linkedRemoteAlbumId,
|
||||
i6.ReadDatabaseContainer(
|
||||
db,
|
||||
).resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity').id,
|
||||
),
|
||||
'local_album_entity__linked_remote_album_id__remote_album_entity__id',
|
||||
);
|
||||
|
||||
i5.$$RemoteAlbumEntityTableProcessedTableManager? get linkedRemoteAlbumId {
|
||||
|
||||
@@ -41,16 +41,7 @@ final class $$LocalAlbumAssetEntityTableReferences
|
||||
i4.ReadDatabaseContainer(db)
|
||||
.resultSet<i3.$LocalAssetEntityTable>('local_asset_entity')
|
||||
.createAlias(
|
||||
i0.$_aliasNameGenerator(
|
||||
i4.ReadDatabaseContainer(db)
|
||||
.resultSet<i1.$LocalAlbumAssetEntityTable>(
|
||||
'local_album_asset_entity',
|
||||
)
|
||||
.assetId,
|
||||
i4.ReadDatabaseContainer(
|
||||
db,
|
||||
).resultSet<i3.$LocalAssetEntityTable>('local_asset_entity').id,
|
||||
),
|
||||
'local_album_asset_entity__asset_id__local_asset_entity__id',
|
||||
);
|
||||
|
||||
i3.$$LocalAssetEntityTableProcessedTableManager get assetId {
|
||||
@@ -75,16 +66,7 @@ final class $$LocalAlbumAssetEntityTableReferences
|
||||
i4.ReadDatabaseContainer(db)
|
||||
.resultSet<i5.$LocalAlbumEntityTable>('local_album_entity')
|
||||
.createAlias(
|
||||
i0.$_aliasNameGenerator(
|
||||
i4.ReadDatabaseContainer(db)
|
||||
.resultSet<i1.$LocalAlbumAssetEntityTable>(
|
||||
'local_album_asset_entity',
|
||||
)
|
||||
.albumId,
|
||||
i4.ReadDatabaseContainer(
|
||||
db,
|
||||
).resultSet<i5.$LocalAlbumEntityTable>('local_album_entity').id,
|
||||
),
|
||||
'local_album_asset_entity__album_id__local_album_entity__id',
|
||||
);
|
||||
|
||||
i5.$$LocalAlbumEntityTableProcessedTableManager get albumId {
|
||||
|
||||
@@ -53,16 +53,7 @@ final class $$MemoryEntityTableReferences
|
||||
static i5.$UserEntityTable _ownerIdTable(i0.GeneratedDatabase db) =>
|
||||
i6.ReadDatabaseContainer(db)
|
||||
.resultSet<i5.$UserEntityTable>('user_entity')
|
||||
.createAlias(
|
||||
i0.$_aliasNameGenerator(
|
||||
i6.ReadDatabaseContainer(
|
||||
db,
|
||||
).resultSet<i1.$MemoryEntityTable>('memory_entity').ownerId,
|
||||
i6.ReadDatabaseContainer(
|
||||
db,
|
||||
).resultSet<i5.$UserEntityTable>('user_entity').id,
|
||||
),
|
||||
);
|
||||
.createAlias('memory_entity__owner_id__user_entity__id');
|
||||
|
||||
i5.$$UserEntityTableProcessedTableManager get ownerId {
|
||||
final $_column = $_itemColumn<String>('owner_id')!;
|
||||
|
||||
@@ -39,14 +39,7 @@ final class $$MemoryAssetEntityTableReferences
|
||||
i4.ReadDatabaseContainer(db)
|
||||
.resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity')
|
||||
.createAlias(
|
||||
i0.$_aliasNameGenerator(
|
||||
i4.ReadDatabaseContainer(db)
|
||||
.resultSet<i1.$MemoryAssetEntityTable>('memory_asset_entity')
|
||||
.assetId,
|
||||
i4.ReadDatabaseContainer(
|
||||
db,
|
||||
).resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity').id,
|
||||
),
|
||||
'memory_asset_entity__asset_id__remote_asset_entity__id',
|
||||
);
|
||||
|
||||
i3.$$RemoteAssetEntityTableProcessedTableManager get assetId {
|
||||
@@ -70,16 +63,7 @@ final class $$MemoryAssetEntityTableReferences
|
||||
static i5.$MemoryEntityTable _memoryIdTable(i0.GeneratedDatabase db) =>
|
||||
i4.ReadDatabaseContainer(db)
|
||||
.resultSet<i5.$MemoryEntityTable>('memory_entity')
|
||||
.createAlias(
|
||||
i0.$_aliasNameGenerator(
|
||||
i4.ReadDatabaseContainer(db)
|
||||
.resultSet<i1.$MemoryAssetEntityTable>('memory_asset_entity')
|
||||
.memoryId,
|
||||
i4.ReadDatabaseContainer(
|
||||
db,
|
||||
).resultSet<i5.$MemoryEntityTable>('memory_entity').id,
|
||||
),
|
||||
);
|
||||
.createAlias('memory_asset_entity__memory_id__memory_entity__id');
|
||||
|
||||
i5.$$MemoryEntityTableProcessedTableManager get memoryId {
|
||||
final $_column = $_itemColumn<String>('memory_id')!;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user