mirror of
https://github.com/immich-app/immich.git
synced 2026-03-03 23:17:01 +00:00
Compare commits
16 Commits
refactor/r
...
feat/pg-qu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3af4b8d7a7 | ||
|
|
1989a8bec2 | ||
|
|
0e1f61176a | ||
|
|
0160e6fd5f | ||
|
|
839fb61340 | ||
|
|
6b04fa3f94 | ||
|
|
947f00ac9d | ||
|
|
8bfacda3da | ||
|
|
b9fceeef75 | ||
|
|
b05848f5ab | ||
|
|
27de6e7c1a | ||
|
|
880b2ab665 | ||
|
|
5a7b298d02 | ||
|
|
4ee7a39e7a | ||
|
|
c5c8fc56a5 | ||
|
|
295ab7a11a |
@@ -2,7 +2,6 @@
|
||||
"name": "Immich - Backend, Frontend and ML",
|
||||
"service": "immich-server",
|
||||
"runServices": [
|
||||
"immich-init",
|
||||
"immich-server",
|
||||
"redis",
|
||||
"database",
|
||||
@@ -32,8 +31,29 @@
|
||||
"tasks": {
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "Fix Permissions, Install Dependencies",
|
||||
"type": "shell",
|
||||
"command": "[ -f /immich-devcontainer/container-start.sh ] && /immich-devcontainer/container-start.sh || exit 0",
|
||||
"isBackground": true,
|
||||
"presentation": {
|
||||
"echo": true,
|
||||
"reveal": "always",
|
||||
"focus": false,
|
||||
"panel": "dedicated",
|
||||
"showReuseMessage": true,
|
||||
"clear": false,
|
||||
"group": "Devcontainer tasks",
|
||||
"close": true
|
||||
},
|
||||
"runOptions": {
|
||||
"runOn": "default"
|
||||
},
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "Immich API Server (Nest)",
|
||||
"dependsOn": ["Fix Permissions, Install Dependencies"],
|
||||
"type": "shell",
|
||||
"command": "[ -f /immich-devcontainer/container-start-backend.sh ] && /immich-devcontainer/container-start-backend.sh || exit 0",
|
||||
"isBackground": true,
|
||||
@@ -54,6 +74,7 @@
|
||||
},
|
||||
{
|
||||
"label": "Immich Web Server (Vite)",
|
||||
"dependsOn": ["Fix Permissions, Install Dependencies"],
|
||||
"type": "shell",
|
||||
"command": "[ -f /immich-devcontainer/container-start-frontend.sh ] && /immich-devcontainer/container-start-frontend.sh || exit 0",
|
||||
"isBackground": true,
|
||||
@@ -109,8 +130,8 @@
|
||||
}
|
||||
},
|
||||
"overrideCommand": true,
|
||||
"workspaceFolder": "/usr/src/app",
|
||||
"remoteUser": "root",
|
||||
"workspaceFolder": "/workspaces/immich",
|
||||
"remoteUser": "node",
|
||||
"userEnvProbe": "loginInteractiveShell",
|
||||
"remoteEnv": {
|
||||
// The location where your uploaded files are stored
|
||||
|
||||
@@ -1,17 +1,23 @@
|
||||
services:
|
||||
immich-app-base:
|
||||
image: busybox
|
||||
immich-server:
|
||||
extends:
|
||||
service: immich-app-base
|
||||
profiles: !reset []
|
||||
image: immich-server-dev:latest
|
||||
build:
|
||||
target: dev-container-mobile
|
||||
environment:
|
||||
- IMMICH_SERVER_URL=http://127.0.0.1:2283/
|
||||
volumes:
|
||||
volumes: !override # bind mount host to /workspaces/immich
|
||||
- ..:/workspaces/immich
|
||||
- ${UPLOAD_LOCATION:-upload-devcontainer-volume}${UPLOAD_LOCATION:+/photos}:/data
|
||||
- pnpm-store:/usr/src/app/.pnpm-store
|
||||
- server-node_modules:/usr/src/app/server/node_modules
|
||||
- web-node_modules:/usr/src/app/web/node_modules
|
||||
- github-node_modules:/usr/src/app/.github/node_modules
|
||||
- cli-node_modules:/usr/src/app/cli/node_modules
|
||||
- docs-node_modules:/usr/src/app/docs/node_modules
|
||||
- e2e-node_modules:/usr/src/app/e2e/node_modules
|
||||
- sdk-node_modules:/usr/src/app/open-api/typescript-sdk/node_modules
|
||||
- app-node_modules:/usr/src/app/node_modules
|
||||
- sveltekit:/usr/src/app/web/.svelte-kit
|
||||
- coverage:/usr/src/app/web/coverage
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
immich-web:
|
||||
env_file: !reset []
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
"name": "Immich - Mobile",
|
||||
"service": "immich-server",
|
||||
"runServices": [
|
||||
"immich-init",
|
||||
"immich-server",
|
||||
"redis",
|
||||
"database",
|
||||
@@ -36,7 +35,7 @@
|
||||
},
|
||||
"forwardPorts": [],
|
||||
"overrideCommand": true,
|
||||
"workspaceFolder": "/usr/src/app",
|
||||
"workspaceFolder": "/workspaces/immich",
|
||||
"remoteUser": "node",
|
||||
"userEnvProbe": "loginInteractiveShell",
|
||||
"remoteEnv": {
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
export IMMICH_PORT="${DEV_SERVER_PORT:-2283}"
|
||||
export DEV_PORT="${DEV_PORT:-3000}"
|
||||
|
||||
# search for immich directory inside workspace.
|
||||
# /workspaces/immich is the bind mount, but other directories can be mounted if runing
|
||||
# Devcontainer: Clone [repository|pull request] in container volumne
|
||||
WORKSPACES_DIR="/workspaces"
|
||||
IMMICH_DIR="$WORKSPACES_DIR/immich"
|
||||
IMMICH_DEVCONTAINER_LOG="$HOME/immich-devcontainer.log"
|
||||
|
||||
log() {
|
||||
@@ -25,8 +30,52 @@ run_cmd() {
|
||||
return "${PIPESTATUS[0]}"
|
||||
}
|
||||
|
||||
export IMMICH_WORKSPACE="/usr/src/app"
|
||||
# Find directories excluding /workspaces/immich
|
||||
mapfile -t other_dirs < <(find "$WORKSPACES_DIR" -mindepth 1 -maxdepth 1 -type d ! -path "$IMMICH_DIR" ! -name ".*")
|
||||
|
||||
if [ ${#other_dirs[@]} -gt 1 ]; then
|
||||
log "Error: More than one directory found in $WORKSPACES_DIR other than $IMMICH_DIR."
|
||||
exit 1
|
||||
elif [ ${#other_dirs[@]} -eq 1 ]; then
|
||||
export IMMICH_WORKSPACE="${other_dirs[0]}"
|
||||
else
|
||||
export IMMICH_WORKSPACE="$IMMICH_DIR"
|
||||
fi
|
||||
|
||||
log "Found immich workspace in $IMMICH_WORKSPACE"
|
||||
log ""
|
||||
|
||||
fix_permissions() {
|
||||
|
||||
log "Fixing permissions for ${IMMICH_WORKSPACE}"
|
||||
|
||||
# Change ownership for directories that exist
|
||||
for dir in "${IMMICH_WORKSPACE}/.vscode" \
|
||||
"${IMMICH_WORKSPACE}/server/upload" \
|
||||
"${IMMICH_WORKSPACE}/.pnpm-store" \
|
||||
"${IMMICH_WORKSPACE}/.github/node_modules" \
|
||||
"${IMMICH_WORKSPACE}/cli/node_modules" \
|
||||
"${IMMICH_WORKSPACE}/e2e/node_modules" \
|
||||
"${IMMICH_WORKSPACE}/open-api/typescript-sdk/node_modules" \
|
||||
"${IMMICH_WORKSPACE}/server/node_modules" \
|
||||
"${IMMICH_WORKSPACE}/server/dist" \
|
||||
"${IMMICH_WORKSPACE}/web/node_modules" \
|
||||
"${IMMICH_WORKSPACE}/web/dist"; do
|
||||
if [ -d "$dir" ]; then
|
||||
run_cmd sudo chown node -R "$dir"
|
||||
fi
|
||||
done
|
||||
|
||||
log ""
|
||||
}
|
||||
|
||||
install_dependencies() {
|
||||
|
||||
log "Installing dependencies"
|
||||
(
|
||||
cd "${IMMICH_WORKSPACE}" || exit 1
|
||||
export CI=1 FROZEN=1 OFFLINE=1
|
||||
run_cmd make setup-web-dev setup-server-dev
|
||||
)
|
||||
log ""
|
||||
}
|
||||
|
||||
@@ -1,21 +1,26 @@
|
||||
services:
|
||||
immich-app-base:
|
||||
image: busybox
|
||||
immich-server:
|
||||
extends:
|
||||
service: immich-app-base
|
||||
profiles: !reset []
|
||||
image: immich-server-dev:latest
|
||||
build:
|
||||
target: dev-container-server
|
||||
env_file: !reset []
|
||||
hostname: immich-dev
|
||||
environment:
|
||||
- IMMICH_SERVER_URL=http://127.0.0.1:2283/
|
||||
volumes:
|
||||
volumes: !override
|
||||
- ..:/workspaces/immich
|
||||
- ${UPLOAD_LOCATION:-upload-devcontainer-volume}${UPLOAD_LOCATION:+/photos}:/data
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- pnpm_store_server:/buildcache/pnpm-store
|
||||
- pnpm-store:/usr/src/app/.pnpm-store
|
||||
- server-node_modules:/usr/src/app/server/node_modules
|
||||
- web-node_modules:/usr/src/app/web/node_modules
|
||||
- github-node_modules:/usr/src/app/.github/node_modules
|
||||
- cli-node_modules:/usr/src/app/cli/node_modules
|
||||
- docs-node_modules:/usr/src/app/docs/node_modules
|
||||
- e2e-node_modules:/usr/src/app/e2e/node_modules
|
||||
- sdk-node_modules:/usr/src/app/open-api/typescript-sdk/node_modules
|
||||
- app-node_modules:/usr/src/app/node_modules
|
||||
- sveltekit:/usr/src/app/web/.svelte-kit
|
||||
- coverage:/usr/src/app/web/coverage
|
||||
- ../plugins:/build/corePlugin
|
||||
immich-web:
|
||||
env_file: !reset []
|
||||
|
||||
17
.devcontainer/server/container-start.sh
Executable file
17
.devcontainer/server/container-start.sh
Executable file
@@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
# shellcheck source=common.sh
|
||||
# shellcheck disable=SC1091
|
||||
source /immich-devcontainer/container-common.sh
|
||||
|
||||
log "Setting up Immich dev container..."
|
||||
fix_permissions
|
||||
|
||||
log "Setup complete, please wait while backend and frontend services automatically start"
|
||||
log
|
||||
log "If necessary, the services may be manually started using"
|
||||
log
|
||||
log "$ /immich-devcontainer/container-start-backend.sh"
|
||||
log "$ /immich-devcontainer/container-start-frontend.sh"
|
||||
log
|
||||
log "From different terminal windows, as these scripts automatically restart the server"
|
||||
log "on error, and will continuously run in a loop"
|
||||
2
.github/.nvmrc
vendored
2
.github/.nvmrc
vendored
@@ -1 +1 @@
|
||||
24.13.1
|
||||
24.13.0
|
||||
|
||||
1
.github/pull_request_template.md
vendored
1
.github/pull_request_template.md
vendored
@@ -26,7 +26,6 @@ The `/api/something` endpoint is now `/api/something-else`
|
||||
|
||||
## Checklist:
|
||||
|
||||
- [ ] I have carefully read CONTRIBUTING.md
|
||||
- [ ] I have performed a self-review of my own code
|
||||
- [ ] I have made corresponding changes to the documentation if applicable
|
||||
- [ ] I have no unrelated changes in the PR.
|
||||
|
||||
16
.github/workflows/build-mobile.yml
vendored
16
.github/workflows/build-mobile.yml
vendored
@@ -51,14 +51,14 @@ jobs:
|
||||
should_run: ${{ steps.check.outputs.should_run }}
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
|
||||
with:
|
||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
- name: Check what should run
|
||||
id: check
|
||||
uses: immich-app/devtools/actions/pre-job@eed0f8b8165ffcb951f2ba854b2dd031935e1d73 # pre-job-action-v2.0.2
|
||||
uses: immich-app/devtools/actions/pre-job@08bac802a312fc89808e0dd589271ca0974087b5 # pre-job-action-v2.0.0
|
||||
with:
|
||||
github-token: ${{ steps.token.outputs.token }}
|
||||
filters: |
|
||||
@@ -79,12 +79,12 @@ jobs:
|
||||
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
|
||||
with:
|
||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
ref: ${{ inputs.ref || github.sha }}
|
||||
persist-credentials: false
|
||||
@@ -96,14 +96,14 @@ jobs:
|
||||
working-directory: ./mobile
|
||||
run: printf "%s" $KEY_JKS | base64 -d > android/key.jks
|
||||
|
||||
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||
- uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: '17'
|
||||
|
||||
- name: Restore Gradle Cache
|
||||
id: cache-gradle-restore
|
||||
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
|
||||
uses: actions/cache/restore@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
@@ -160,7 +160,7 @@ jobs:
|
||||
|
||||
- name: Save Gradle Cache
|
||||
id: cache-gradle-save
|
||||
uses: actions/cache/save@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
|
||||
uses: actions/cache/save@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
|
||||
if: github.ref == 'refs/heads/main'
|
||||
with:
|
||||
path: |
|
||||
@@ -185,7 +185,7 @@ jobs:
|
||||
run: sudo xcode-select -s /Applications/Xcode_26.2.app/Contents/Developer
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
|
||||
with:
|
||||
ref: ${{ inputs.ref || github.sha }}
|
||||
persist-credentials: false
|
||||
|
||||
4
.github/workflows/cache-cleanup.yml
vendored
4
.github/workflows/cache-cleanup.yml
vendored
@@ -19,13 +19,13 @@ jobs:
|
||||
actions: write
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
|
||||
with:
|
||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
- name: Check out code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
token: ${{ steps.token.outputs.token }}
|
||||
|
||||
32
.github/workflows/check-openapi.yml
vendored
32
.github/workflows/check-openapi.yml
vendored
@@ -1,32 +0,0 @@
|
||||
name: Check OpenAPI
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'open-api/**'
|
||||
- '.github/workflows/check-openapi.yml'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
check-openapi:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Check for breaking API changes
|
||||
# sha is pinning to a commit instead of a tag since the action does not tag versions
|
||||
uses: oasdiff/oasdiff-action/breaking@ccb863950ce437a50f8f1a40d2a1112117e06ce4
|
||||
with:
|
||||
base: https://raw.githubusercontent.com/${{ github.repository }}/main/open-api/immich-openapi-specs.json
|
||||
revision: open-api/immich-openapi-specs.json
|
||||
fail-on: ERR
|
||||
14
.github/workflows/cli.yml
vendored
14
.github/workflows/cli.yml
vendored
@@ -31,12 +31,12 @@ jobs:
|
||||
working-directory: ./cli
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
|
||||
with:
|
||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
token: ${{ steps.token.outputs.token }}
|
||||
@@ -45,7 +45,7 @@ jobs:
|
||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
||||
with:
|
||||
node-version-file: './cli/.nvmrc'
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
@@ -71,13 +71,13 @@ jobs:
|
||||
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
|
||||
with:
|
||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
token: ${{ steps.token.outputs.token }}
|
||||
@@ -89,7 +89,7 @@ jobs:
|
||||
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
|
||||
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
|
||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||
with:
|
||||
registry: ghcr.io
|
||||
@@ -115,7 +115,7 @@ jobs:
|
||||
type=raw,value=latest,enable=${{ github.event_name == 'release' }}
|
||||
|
||||
- name: Build and push image
|
||||
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2
|
||||
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
|
||||
with:
|
||||
file: cli/Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
|
||||
2
.github/workflows/close-duplicates.yml
vendored
2
.github/workflows/close-duplicates.yml
vendored
@@ -35,7 +35,7 @@ jobs:
|
||||
needs: [get_body, should_run]
|
||||
if: ${{ needs.should_run.outputs.should_run == 'true' }}
|
||||
container:
|
||||
image: ghcr.io/immich-app/mdq:main@sha256:4f9860d04c88f7f87861f8ee84bfeedaec15ed7ca5ca87bc7db44b036f81645f
|
||||
image: ghcr.io/immich-app/mdq:main@sha256:ab9f163cd5d5cec42704a26ca2769ecf3f10aa8e7bae847f1d527cdf075946e6
|
||||
outputs:
|
||||
checked: ${{ steps.get_checkbox.outputs.checked }}
|
||||
steps:
|
||||
|
||||
38
.github/workflows/close-llm-pr.yml
vendored
38
.github/workflows/close-llm-pr.yml
vendored
@@ -1,38 +0,0 @@
|
||||
name: Close LLM-generated PRs
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [labeled]
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
comment_and_close:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event.label.name == 'llm-generated' }}
|
||||
permissions:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Comment and close
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
NODE_ID: ${{ github.event.pull_request.node_id }}
|
||||
run: |
|
||||
gh api graphql \
|
||||
-f prId="$NODE_ID" \
|
||||
-f body="Thank you for your interest in contributing to Immich! Unfortunately this PR looks like it was generated using an LLM. As noted in our [CONTRIBUTING.md](https://github.com/immich-app/immich/blob/main/CONTRIBUTING.md#use-of-generative-ai), we request that you don't use LLMs to generate PRs as those are not a good use of maintainer time." \
|
||||
-f query='
|
||||
mutation CommentAndClosePR($prId: ID!, $body: String!) {
|
||||
addComment(input: {
|
||||
subjectId: $prId,
|
||||
body: $body
|
||||
}) {
|
||||
__typename
|
||||
}
|
||||
|
||||
closePullRequest(input: {
|
||||
pullRequestId: $prId
|
||||
}) {
|
||||
__typename
|
||||
}
|
||||
}'
|
||||
10
.github/workflows/codeql-analysis.yml
vendored
10
.github/workflows/codeql-analysis.yml
vendored
@@ -44,20 +44,20 @@ jobs:
|
||||
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
|
||||
with:
|
||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
token: ${{ steps.token.outputs.token }}
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@9e907b5e64f6b83e7804b09294d44122997950d6 # v4.32.3
|
||||
uses: github/codeql-action/init@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v4.31.9
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
@@ -70,7 +70,7 @@ jobs:
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@9e907b5e64f6b83e7804b09294d44122997950d6 # v4.32.3
|
||||
uses: github/codeql-action/autobuild@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v4.31.9
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
@@ -83,6 +83,6 @@ jobs:
|
||||
# ./location_of_script_within_repo/buildscript.sh
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@9e907b5e64f6b83e7804b09294d44122997950d6 # v4.32.3
|
||||
uses: github/codeql-action/analyze@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v4.31.9
|
||||
with:
|
||||
category: '/language:${{matrix.language}}'
|
||||
|
||||
12
.github/workflows/docker.yml
vendored
12
.github/workflows/docker.yml
vendored
@@ -23,14 +23,14 @@ jobs:
|
||||
should_run: ${{ steps.check.outputs.should_run }}
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
|
||||
with:
|
||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
- name: Check what should run
|
||||
id: check
|
||||
uses: immich-app/devtools/actions/pre-job@eed0f8b8165ffcb951f2ba854b2dd031935e1d73 # pre-job-action-v2.0.2
|
||||
uses: immich-app/devtools/actions/pre-job@08bac802a312fc89808e0dd589271ca0974087b5 # pre-job-action-v2.0.0
|
||||
with:
|
||||
github-token: ${{ steps.token.outputs.token }}
|
||||
filters: |
|
||||
@@ -60,7 +60,7 @@ jobs:
|
||||
suffix: ['', '-cuda', '-rocm', '-openvino', '-armnn', '-rknn']
|
||||
steps:
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
|
||||
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
@@ -90,7 +90,7 @@ jobs:
|
||||
suffix: ['']
|
||||
steps:
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
|
||||
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
@@ -132,7 +132,7 @@ jobs:
|
||||
suffixes: '-rocm'
|
||||
platforms: linux/amd64
|
||||
runner-mapping: '{"linux/amd64": "pokedex-giant"}'
|
||||
uses: immich-app/devtools/.github/workflows/multi-runner-build.yml@bd49ed7a5a6022149f79b6564df48177476a822b # multi-runner-build-workflow-v2.2.1
|
||||
uses: immich-app/devtools/.github/workflows/multi-runner-build.yml@0477486d82313fba68f7c82c034120a4b8981297 # multi-runner-build-workflow-v2.1.0
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
@@ -155,7 +155,7 @@ jobs:
|
||||
name: Build and Push Server
|
||||
needs: pre-job
|
||||
if: ${{ fromJSON(needs.pre-job.outputs.should_run).server == true }}
|
||||
uses: immich-app/devtools/.github/workflows/multi-runner-build.yml@bd49ed7a5a6022149f79b6564df48177476a822b # multi-runner-build-workflow-v2.2.1
|
||||
uses: immich-app/devtools/.github/workflows/multi-runner-build.yml@0477486d82313fba68f7c82c034120a4b8981297 # multi-runner-build-workflow-v2.1.0
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
|
||||
10
.github/workflows/docs-build.yml
vendored
10
.github/workflows/docs-build.yml
vendored
@@ -21,14 +21,14 @@ jobs:
|
||||
should_run: ${{ steps.check.outputs.should_run }}
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
|
||||
with:
|
||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
- name: Check what should run
|
||||
id: check
|
||||
uses: immich-app/devtools/actions/pre-job@eed0f8b8165ffcb951f2ba854b2dd031935e1d73 # pre-job-action-v2.0.2
|
||||
uses: immich-app/devtools/actions/pre-job@08bac802a312fc89808e0dd589271ca0974087b5 # pre-job-action-v2.0.0
|
||||
with:
|
||||
github-token: ${{ steps.token.outputs.token }}
|
||||
filters: |
|
||||
@@ -54,13 +54,13 @@ jobs:
|
||||
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
|
||||
with:
|
||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
token: ${{ steps.token.outputs.token }}
|
||||
@@ -70,7 +70,7 @@ jobs:
|
||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
||||
with:
|
||||
node-version-file: './docs/.nvmrc'
|
||||
cache: 'pnpm'
|
||||
|
||||
25
.github/workflows/docs-deploy.yml
vendored
25
.github/workflows/docs-deploy.yml
vendored
@@ -20,7 +20,7 @@ jobs:
|
||||
artifact: ${{ steps.get-artifact.outputs.result }}
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
|
||||
with:
|
||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
@@ -119,19 +119,19 @@ 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@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
|
||||
with:
|
||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
token: ${{ steps.token.outputs.token }}
|
||||
|
||||
- name: Setup Mise
|
||||
uses: immich-app/devtools/actions/use-mise@dab18118da6476e8237ac94080fd937983fecd42 # use-mise-action-v1.1.2
|
||||
uses: immich-app/devtools/actions/use-mise@cd24790a7f5f6439ac32cc94f5523cb2de8bfa8c # use-mise-action-v1.1.0
|
||||
|
||||
- name: Load parameters
|
||||
id: parameters
|
||||
@@ -192,13 +192,16 @@ jobs:
|
||||
' >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Publish to Cloudflare Pages
|
||||
working-directory: docs
|
||||
env:
|
||||
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN_PAGES_UPLOAD }}
|
||||
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
PROJECT_NAME: ${{ steps.docs-output.outputs.projectName }}
|
||||
BRANCH_NAME: ${{ steps.parameters.outputs.name }}
|
||||
run: mise run //docs:deploy
|
||||
# TODO: Action is deprecated
|
||||
uses: cloudflare/pages-action@f0a1cd58cd66095dee69bfa18fa5efd1dde93bca # v1.5.0
|
||||
with:
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN_PAGES_UPLOAD }}
|
||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
projectName: ${{ steps.docs-output.outputs.projectName }}
|
||||
workingDirectory: 'docs'
|
||||
directory: 'build'
|
||||
branch: ${{ steps.parameters.outputs.name }}
|
||||
wranglerVersion: '3'
|
||||
|
||||
- name: Deploy Docs Release Domain
|
||||
if: ${{ steps.parameters.outputs.event == 'release' }}
|
||||
|
||||
6
.github/workflows/docs-destroy.yml
vendored
6
.github/workflows/docs-destroy.yml
vendored
@@ -17,19 +17,19 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
|
||||
with:
|
||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
token: ${{ steps.token.outputs.token }}
|
||||
|
||||
- name: Setup Mise
|
||||
uses: immich-app/devtools/actions/use-mise@dab18118da6476e8237ac94080fd937983fecd42 # use-mise-action-v1.1.2
|
||||
uses: immich-app/devtools/actions/use-mise@cd24790a7f5f6439ac32cc94f5523cb2de8bfa8c # use-mise-action-v1.1.0
|
||||
|
||||
- name: Destroy Docs Subdomain
|
||||
env:
|
||||
|
||||
6
.github/workflows/fix-format.yml
vendored
6
.github/workflows/fix-format.yml
vendored
@@ -22,7 +22,7 @@ jobs:
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
- name: 'Checkout'
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.ref }}
|
||||
token: ${{ steps.generate-token.outputs.token }}
|
||||
@@ -32,14 +32,14 @@ jobs:
|
||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
||||
with:
|
||||
node-version-file: './server/.nvmrc'
|
||||
cache: 'pnpm'
|
||||
cache-dependency-path: '**/pnpm-lock.yaml'
|
||||
|
||||
- name: Fix formatting
|
||||
run: pnpm --recursive install && pnpm run --recursive --if-present --parallel format:fix
|
||||
run: pnpm --recursive install && pnpm run --recursive --parallel fix:format
|
||||
|
||||
- name: Commit and push
|
||||
uses: EndBug/add-and-commit@a94899bca583c204427a224a7af87c02f9b325d5 # v9.1.4
|
||||
|
||||
2
.github/workflows/pr-label-validation.yml
vendored
2
.github/workflows/pr-label-validation.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
|
||||
with:
|
||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
2
.github/workflows/pr-labeler.yml
vendored
2
.github/workflows/pr-labeler.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
|
||||
with:
|
||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
8
.github/workflows/prepare-release.yml
vendored
8
.github/workflows/prepare-release.yml
vendored
@@ -56,20 +56,20 @@ jobs:
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
token: ${{ steps.generate-token.outputs.token }}
|
||||
persist-credentials: true
|
||||
ref: main
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@eac588ad8def6316056a12d4907a9d4d84ff7a3b # v7.3.0
|
||||
uses: astral-sh/setup-uv@681c641aba71e4a1c380be3ab5e12ad51f415867 # v7.1.6
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
||||
with:
|
||||
node-version-file: './server/.nvmrc'
|
||||
cache: 'pnpm'
|
||||
@@ -130,7 +130,7 @@ jobs:
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
token: ${{ steps.generate-token.outputs.token }}
|
||||
persist-credentials: false
|
||||
|
||||
4
.github/workflows/preview-label.yaml
vendored
4
.github/workflows/preview-label.yaml
vendored
@@ -14,7 +14,7 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
|
||||
with:
|
||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
@@ -32,7 +32,7 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
|
||||
with:
|
||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
8
.github/workflows/release-pr.yml
vendored
8
.github/workflows/release-pr.yml
vendored
@@ -23,20 +23,20 @@ jobs:
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
token: ${{ steps.generate-token.outputs.token }}
|
||||
persist-credentials: true
|
||||
ref: main
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@eac588ad8def6316056a12d4907a9d4d84ff7a3b # v7.3.0
|
||||
uses: astral-sh/setup-uv@681c641aba71e4a1c380be3ab5e12ad51f415867 # v7.1.6
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
||||
with:
|
||||
node-version-file: './server/.nvmrc'
|
||||
cache: 'pnpm'
|
||||
@@ -159,7 +159,7 @@ jobs:
|
||||
|
||||
- name: Create PR
|
||||
id: create-pr
|
||||
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0
|
||||
uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725 # v8.0.0
|
||||
with:
|
||||
token: ${{ steps.generate-token.outputs.token }}
|
||||
commit-message: 'chore: release ${{ steps.bump-type.outputs.next }}'
|
||||
|
||||
3
.github/workflows/release.yml
vendored
3
.github/workflows/release.yml
vendored
@@ -58,7 +58,7 @@ jobs:
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
token: ${{ steps.generate-token.outputs.token }}
|
||||
persist-credentials: false
|
||||
@@ -88,7 +88,6 @@ jobs:
|
||||
draft: true
|
||||
files: |
|
||||
docker/docker-compose.yml
|
||||
docker/docker-compose.rootless.yml
|
||||
docker/example.env
|
||||
docker/hwaccel.ml.yml
|
||||
docker/hwaccel.transcoding.yml
|
||||
|
||||
6
.github/workflows/sdk.yml
vendored
6
.github/workflows/sdk.yml
vendored
@@ -19,12 +19,12 @@ jobs:
|
||||
working-directory: ./open-api/typescript-sdk
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
|
||||
with:
|
||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
token: ${{ steps.token.outputs.token }}
|
||||
@@ -33,7 +33,7 @@ jobs:
|
||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||
|
||||
# Setup .npmrc file to publish to npm
|
||||
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
||||
with:
|
||||
node-version-file: './open-api/typescript-sdk/.nvmrc'
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
|
||||
16
.github/workflows/static_analysis.yml
vendored
16
.github/workflows/static_analysis.yml
vendored
@@ -20,14 +20,14 @@ jobs:
|
||||
should_run: ${{ steps.check.outputs.should_run }}
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
|
||||
with:
|
||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
- name: Check what should run
|
||||
id: check
|
||||
uses: immich-app/devtools/actions/pre-job@eed0f8b8165ffcb951f2ba854b2dd031935e1d73 # pre-job-action-v2.0.2
|
||||
uses: immich-app/devtools/actions/pre-job@08bac802a312fc89808e0dd589271ca0974087b5 # pre-job-action-v2.0.0
|
||||
with:
|
||||
github-token: ${{ steps.token.outputs.token }}
|
||||
filters: |
|
||||
@@ -49,13 +49,13 @@ jobs:
|
||||
working-directory: ./mobile
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
|
||||
with:
|
||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
token: ${{ steps.token.outputs.token }}
|
||||
@@ -69,14 +69,6 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: dart pub get
|
||||
|
||||
- name: Install dependencies for UI package
|
||||
run: dart pub get
|
||||
working-directory: ./mobile/packages/ui
|
||||
|
||||
- name: Install dependencies for UI Showcase
|
||||
run: dart pub get
|
||||
working-directory: ./mobile/packages/ui/showcase
|
||||
|
||||
- name: Install DCM
|
||||
uses: CQLabs/setup-dcm@8697ae0790c0852e964a6ef1d768d62a6675481a # v2.0.1
|
||||
with:
|
||||
|
||||
130
.github/workflows/test.yml
vendored
130
.github/workflows/test.yml
vendored
@@ -17,14 +17,14 @@ jobs:
|
||||
should_run: ${{ steps.check.outputs.should_run }}
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
|
||||
with:
|
||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
- name: Check what should run
|
||||
id: check
|
||||
uses: immich-app/devtools/actions/pre-job@eed0f8b8165ffcb951f2ba854b2dd031935e1d73 # pre-job-action-v2.0.2
|
||||
uses: immich-app/devtools/actions/pre-job@08bac802a312fc89808e0dd589271ca0974087b5 # pre-job-action-v2.0.0
|
||||
with:
|
||||
github-token: ${{ steps.token.outputs.token }}
|
||||
filters: |
|
||||
@@ -63,13 +63,13 @@ jobs:
|
||||
working-directory: ./server
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
|
||||
with:
|
||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
token: ${{ steps.token.outputs.token }}
|
||||
@@ -77,7 +77,7 @@ jobs:
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
||||
with:
|
||||
node-version-file: './server/.nvmrc'
|
||||
cache: 'pnpm'
|
||||
@@ -108,20 +108,20 @@ jobs:
|
||||
working-directory: ./cli
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
|
||||
with:
|
||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
token: ${{ steps.token.outputs.token }}
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
||||
with:
|
||||
node-version-file: './cli/.nvmrc'
|
||||
cache: 'pnpm'
|
||||
@@ -155,20 +155,20 @@ jobs:
|
||||
working-directory: ./cli
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
|
||||
with:
|
||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
token: ${{ steps.token.outputs.token }}
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
||||
with:
|
||||
node-version-file: './cli/.nvmrc'
|
||||
cache: 'pnpm'
|
||||
@@ -197,20 +197,20 @@ jobs:
|
||||
working-directory: ./web
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
|
||||
with:
|
||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
token: ${{ steps.token.outputs.token }}
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
||||
with:
|
||||
node-version-file: './web/.nvmrc'
|
||||
cache: 'pnpm'
|
||||
@@ -241,20 +241,20 @@ jobs:
|
||||
working-directory: ./web
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
|
||||
with:
|
||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
token: ${{ steps.token.outputs.token }}
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
||||
with:
|
||||
node-version-file: './web/.nvmrc'
|
||||
cache: 'pnpm'
|
||||
@@ -279,20 +279,20 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
|
||||
with:
|
||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
token: ${{ steps.token.outputs.token }}
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
||||
with:
|
||||
node-version-file: './web/.nvmrc'
|
||||
cache: 'pnpm'
|
||||
@@ -327,20 +327,20 @@ jobs:
|
||||
working-directory: ./e2e
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
|
||||
with:
|
||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
token: ${{ steps.token.outputs.token }}
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
||||
with:
|
||||
node-version-file: './e2e/.nvmrc'
|
||||
cache: 'pnpm'
|
||||
@@ -373,13 +373,13 @@ jobs:
|
||||
working-directory: ./server
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
|
||||
with:
|
||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: 'recursive'
|
||||
@@ -387,7 +387,7 @@ jobs:
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
||||
with:
|
||||
node-version-file: './server/.nvmrc'
|
||||
cache: 'pnpm'
|
||||
@@ -412,13 +412,13 @@ jobs:
|
||||
runner: [ubuntu-latest, ubuntu-24.04-arm]
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
|
||||
with:
|
||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: 'recursive'
|
||||
@@ -426,7 +426,7 @@ jobs:
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
||||
with:
|
||||
node-version-file: './e2e/.nvmrc'
|
||||
cache: 'pnpm'
|
||||
@@ -446,29 +446,12 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
if: ${{ !cancelled() }}
|
||||
- name: Start Docker Compose
|
||||
run: docker compose up -d --build --renew-anon-volumes --force-recreate --remove-orphans --wait --wait-timeout 300
|
||||
- name: Docker build
|
||||
run: docker compose build
|
||||
if: ${{ !cancelled() }}
|
||||
- name: Run e2e tests (api & cli)
|
||||
env:
|
||||
VITEST_DISABLE_DOCKER_SETUP: true
|
||||
run: pnpm test
|
||||
if: ${{ !cancelled() }}
|
||||
- name: Run e2e tests (maintenance)
|
||||
env:
|
||||
VITEST_DISABLE_DOCKER_SETUP: true
|
||||
run: pnpm test:maintenance
|
||||
if: ${{ !cancelled() }}
|
||||
- name: Capture Docker logs
|
||||
if: always()
|
||||
run: docker compose logs --no-color > docker-compose-logs.txt
|
||||
working-directory: ./e2e
|
||||
- name: Archive Docker logs
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
if: always()
|
||||
with:
|
||||
name: e2e-server-docker-logs-${{ matrix.runner }}
|
||||
path: e2e/docker-compose-logs.txt
|
||||
e2e-tests-web:
|
||||
name: End-to-End Tests (Web)
|
||||
needs: pre-job
|
||||
@@ -484,13 +467,13 @@ jobs:
|
||||
runner: [ubuntu-latest, ubuntu-24.04-arm]
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
|
||||
with:
|
||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: 'recursive'
|
||||
@@ -498,7 +481,7 @@ jobs:
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
||||
with:
|
||||
node-version-file: './e2e/.nvmrc'
|
||||
cache: 'pnpm'
|
||||
@@ -511,15 +494,16 @@ jobs:
|
||||
run: pnpm install --frozen-lockfile
|
||||
if: ${{ !cancelled() }}
|
||||
- name: Install Playwright Browsers
|
||||
run: pnpm exec playwright install chromium --only-shell
|
||||
run: npx playwright install chromium --only-shell
|
||||
if: ${{ !cancelled() }}
|
||||
- name: Docker build
|
||||
run: docker compose up -d --build --renew-anon-volumes --force-recreate --remove-orphans --wait --wait-timeout 300
|
||||
if: ${{ !cancelled() }}
|
||||
- name: Run e2e tests (web)
|
||||
env:
|
||||
CI: true
|
||||
PLAYWRIGHT_DISABLE_WEBSERVER: true
|
||||
run: pnpm test:web
|
||||
run: npx playwright test --project=web
|
||||
if: ${{ !cancelled() }}
|
||||
- name: Archive e2e test (web) results
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
@@ -529,8 +513,9 @@ jobs:
|
||||
path: e2e/playwright-report/
|
||||
- name: Run ui tests (web)
|
||||
env:
|
||||
CI: true
|
||||
PLAYWRIGHT_DISABLE_WEBSERVER: true
|
||||
run: pnpm test:web:ui
|
||||
run: npx playwright test --project=ui
|
||||
if: ${{ !cancelled() }}
|
||||
- name: Archive ui test (web) results
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
@@ -540,8 +525,9 @@ jobs:
|
||||
path: e2e/playwright-report/
|
||||
- name: Run maintenance tests
|
||||
env:
|
||||
CI: true
|
||||
PLAYWRIGHT_DISABLE_WEBSERVER: true
|
||||
run: pnpm test:web:maintenance
|
||||
run: npx playwright test --project=maintenance
|
||||
if: ${{ !cancelled() }}
|
||||
- name: Archive maintenance tests (web) results
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
@@ -557,7 +543,7 @@ jobs:
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
if: always()
|
||||
with:
|
||||
name: e2e-web-docker-logs-${{ matrix.runner }}
|
||||
name: docker-compose-logs-${{ matrix.runner }}
|
||||
path: e2e/docker-compose-logs.txt
|
||||
success-check-e2e:
|
||||
name: End-to-End Tests Success
|
||||
@@ -578,12 +564,12 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
|
||||
with:
|
||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
token: ${{ steps.token.outputs.token }}
|
||||
@@ -610,17 +596,17 @@ jobs:
|
||||
working-directory: ./machine-learning
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
|
||||
with:
|
||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
token: ${{ steps.token.outputs.token }}
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@eac588ad8def6316056a12d4907a9d4d84ff7a3b # v7.3.0
|
||||
uses: astral-sh/setup-uv@681c641aba71e4a1c380be3ab5e12ad51f415867 # v7.1.6
|
||||
with:
|
||||
python-version: 3.11
|
||||
- name: Install dependencies
|
||||
@@ -650,20 +636,20 @@ jobs:
|
||||
working-directory: ./.github
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
|
||||
with:
|
||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
token: ${{ steps.token.outputs.token }}
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
||||
with:
|
||||
node-version-file: './.github/.nvmrc'
|
||||
cache: 'pnpm'
|
||||
@@ -680,12 +666,12 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
|
||||
with:
|
||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
token: ${{ steps.token.outputs.token }}
|
||||
@@ -701,20 +687,20 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
|
||||
with:
|
||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
token: ${{ steps.token.outputs.token }}
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
||||
with:
|
||||
node-version-file: './server/.nvmrc'
|
||||
cache: 'pnpm'
|
||||
@@ -763,20 +749,20 @@ jobs:
|
||||
working-directory: ./server
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
|
||||
with:
|
||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
token: ${{ steps.token.outputs.token }}
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
||||
with:
|
||||
node-version-file: './server/.nvmrc'
|
||||
cache: 'pnpm'
|
||||
|
||||
6
.github/workflows/weblate-lock.yml
vendored
6
.github/workflows/weblate-lock.yml
vendored
@@ -24,14 +24,14 @@ jobs:
|
||||
should_run: ${{ steps.check.outputs.should_run }}
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
|
||||
with:
|
||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
- name: Check what should run
|
||||
id: check
|
||||
uses: immich-app/devtools/actions/pre-job@eed0f8b8165ffcb951f2ba854b2dd031935e1d73 # pre-job-action-v2.0.2
|
||||
uses: immich-app/devtools/actions/pre-job@08bac802a312fc89808e0dd589271ca0974087b5 # pre-job-action-v2.0.0
|
||||
with:
|
||||
github-token: ${{ steps.token.outputs.token }}
|
||||
filters: |
|
||||
@@ -47,7 +47,7 @@ jobs:
|
||||
if: ${{ fromJSON(needs.pre-job.outputs.should_run).i18n == true }}
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
|
||||
with:
|
||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
@@ -4,18 +4,12 @@ module.exports = {
|
||||
if (!pkg.name) {
|
||||
return pkg;
|
||||
}
|
||||
// make exiftool-vendored.pl a regular dependency since Docker prod
|
||||
// images build with --no-optional to reduce image size
|
||||
if (pkg.name === "exiftool-vendored") {
|
||||
const binaryPackage =
|
||||
process.platform === "win32"
|
||||
? "exiftool-vendored.exe"
|
||||
: "exiftool-vendored.pl";
|
||||
|
||||
if (pkg.optionalDependencies[binaryPackage]) {
|
||||
pkg.dependencies[binaryPackage] =
|
||||
pkg.optionalDependencies[binaryPackage];
|
||||
delete pkg.optionalDependencies[binaryPackage];
|
||||
if (pkg.optionalDependencies["exiftool-vendored.pl"]) {
|
||||
// make exiftool-vendored.pl a regular dependency
|
||||
pkg.dependencies["exiftool-vendored.pl"] =
|
||||
pkg.optionalDependencies["exiftool-vendored.pl"];
|
||||
delete pkg.optionalDependencies["exiftool-vendored.pl"];
|
||||
}
|
||||
}
|
||||
return pkg;
|
||||
|
||||
@@ -17,7 +17,7 @@ If you are looking for something to work on, there are discussions and issues wi
|
||||
|
||||
## Use of generative AI
|
||||
|
||||
We ask you not to open PRs generated with an LLM. We find that code generated like this tends to need a large amount of back-and-forth, which is a very inefficient use of our time. If we want LLM-generated code, it's much faster for us to use an LLM ourselves than to go through an intermediary via a pull request.
|
||||
We generally discourage PRs entirely generated by an LLM. For any part generated by an LLM, please put extra effort into your self-review. By using generative AI without proper self-review, the time you save ends up being more work we need to put in for proper reviews and code cleanup. Please keep that in mind when submitting code by an LLM. Clearly state the use of LLMs/(generative) AI in your pull request as requested by the template.
|
||||
|
||||
## Feature freezes
|
||||
|
||||
|
||||
2
Makefile
2
Makefile
@@ -52,7 +52,7 @@ attach-server:
|
||||
docker exec -it docker_immich-server_1 sh
|
||||
|
||||
renovate:
|
||||
LOG_LEVEL=debug pnpm exec renovate --platform=local --repository-cache=reset
|
||||
LOG_LEVEL=debug npx renovate --platform=local --repository-cache=reset
|
||||
|
||||
# Directories that need to be created for volumes or build output
|
||||
VOLUME_DIRS = \
|
||||
|
||||
@@ -1 +1 @@
|
||||
24.13.1
|
||||
24.13.0
|
||||
|
||||
@@ -14,13 +14,13 @@
|
||||
],
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.8.0",
|
||||
"@immich/sdk": "workspace:*",
|
||||
"@immich/sdk": "file:../open-api/typescript-sdk",
|
||||
"@types/byte-size": "^8.1.0",
|
||||
"@types/cli-progress": "^3.11.0",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/micromatch": "^4.0.9",
|
||||
"@types/mock-fs": "^4.13.1",
|
||||
"@types/node": "^24.10.13",
|
||||
"@types/node": "^24.10.11",
|
||||
"@vitest/coverage-v8": "^3.0.0",
|
||||
"byte-size": "^9.0.0",
|
||||
"cli-progress": "^3.12.0",
|
||||
@@ -45,8 +45,8 @@
|
||||
"build": "vite build",
|
||||
"build:dev": "vite build --sourcemap true",
|
||||
"lint": "eslint \"src/**/*.ts\" --max-warnings 0",
|
||||
"lint:fix": "pnpm run lint --fix",
|
||||
"prepack": "pnpm run build",
|
||||
"lint:fix": "npm run lint -- --fix",
|
||||
"prepack": "npm run build",
|
||||
"test": "vitest",
|
||||
"test:cov": "vitest --coverage",
|
||||
"format": "prettier --check .",
|
||||
@@ -69,6 +69,6 @@
|
||||
"micromatch": "^4.0.8"
|
||||
},
|
||||
"volta": {
|
||||
"node": "24.13.1"
|
||||
"node": "24.13.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,15 +7,7 @@ import { describe, expect, it, MockedFunction, vi } from 'vitest';
|
||||
import { Action, checkBulkUpload, defaults, getSupportedMediaTypes, Reason } from '@immich/sdk';
|
||||
import createFetchMock from 'vitest-fetch-mock';
|
||||
|
||||
import {
|
||||
checkForDuplicates,
|
||||
deleteFiles,
|
||||
findSidecar,
|
||||
getAlbumName,
|
||||
startWatch,
|
||||
uploadFiles,
|
||||
UploadOptionsDto,
|
||||
} from 'src/commands/asset';
|
||||
import { checkForDuplicates, getAlbumName, startWatch, uploadFiles, UploadOptionsDto } from 'src/commands/asset';
|
||||
|
||||
vi.mock('@immich/sdk');
|
||||
|
||||
@@ -317,85 +309,3 @@ describe('startWatch', () => {
|
||||
await fs.promises.rm(testFolder, { recursive: true, force: true });
|
||||
});
|
||||
});
|
||||
|
||||
describe('findSidecar', () => {
|
||||
let testDir: string;
|
||||
let testFilePath: string;
|
||||
|
||||
beforeEach(() => {
|
||||
testDir = fs.mkdtempSync(path.join(os.tmpdir(), 'test-sidecar-'));
|
||||
testFilePath = path.join(testDir, 'test.jpg');
|
||||
fs.writeFileSync(testFilePath, 'test');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fs.rmSync(testDir, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
it('should find sidecar file with photo.xmp naming convention', () => {
|
||||
const sidecarPath = path.join(testDir, 'test.xmp');
|
||||
fs.writeFileSync(sidecarPath, 'xmp data');
|
||||
|
||||
const result = findSidecar(testFilePath);
|
||||
expect(result).toBe(sidecarPath);
|
||||
});
|
||||
|
||||
it('should find sidecar file with photo.ext.xmp naming convention', () => {
|
||||
const sidecarPath = path.join(testDir, 'test.jpg.xmp');
|
||||
fs.writeFileSync(sidecarPath, 'xmp data');
|
||||
|
||||
const result = findSidecar(testFilePath);
|
||||
expect(result).toBe(sidecarPath);
|
||||
});
|
||||
|
||||
it('should prefer photo.ext.xmp over photo.xmp when both exist', () => {
|
||||
const sidecarPath1 = path.join(testDir, 'test.xmp');
|
||||
const sidecarPath2 = path.join(testDir, 'test.jpg.xmp');
|
||||
fs.writeFileSync(sidecarPath1, 'xmp data 1');
|
||||
fs.writeFileSync(sidecarPath2, 'xmp data 2');
|
||||
|
||||
const result = findSidecar(testFilePath);
|
||||
// Should return the first one found (photo.xmp) based on the order in the code
|
||||
expect(result).toBe(sidecarPath1);
|
||||
});
|
||||
|
||||
it('should return undefined when no sidecar file exists', () => {
|
||||
const result = findSidecar(testFilePath);
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('deleteFiles', () => {
|
||||
let testDir: string;
|
||||
let testFilePath: string;
|
||||
|
||||
beforeEach(() => {
|
||||
testDir = fs.mkdtempSync(path.join(os.tmpdir(), 'test-delete-'));
|
||||
testFilePath = path.join(testDir, 'test.jpg');
|
||||
fs.writeFileSync(testFilePath, 'test');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fs.rmSync(testDir, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
it('should delete asset and sidecar file when main file is deleted', async () => {
|
||||
const sidecarPath = path.join(testDir, 'test.xmp');
|
||||
fs.writeFileSync(sidecarPath, 'xmp data');
|
||||
|
||||
await deleteFiles([{ id: 'test-id', filepath: testFilePath }], [], { delete: true, concurrency: 1 });
|
||||
|
||||
expect(fs.existsSync(testFilePath)).toBe(false);
|
||||
expect(fs.existsSync(sidecarPath)).toBe(false);
|
||||
});
|
||||
|
||||
it('should not delete sidecar file when delete option is false', async () => {
|
||||
const sidecarPath = path.join(testDir, 'test.xmp');
|
||||
fs.writeFileSync(sidecarPath, 'xmp data');
|
||||
|
||||
await deleteFiles([{ id: 'test-id', filepath: testFilePath }], [], { delete: false, concurrency: 1 });
|
||||
|
||||
expect(fs.existsSync(testFilePath)).toBe(true);
|
||||
expect(fs.existsSync(sidecarPath)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -17,7 +17,7 @@ import { Matcher, watch as watchFs } from 'chokidar';
|
||||
import { MultiBar, Presets, SingleBar } from 'cli-progress';
|
||||
import { chunk } from 'lodash-es';
|
||||
import micromatch from 'micromatch';
|
||||
import { Stats, createReadStream, existsSync } from 'node:fs';
|
||||
import { Stats, createReadStream } from 'node:fs';
|
||||
import { stat, unlink } from 'node:fs/promises';
|
||||
import path, { basename } from 'node:path';
|
||||
import { Queue } from 'src/queue';
|
||||
@@ -180,49 +180,18 @@ export const checkForDuplicates = async (files: string[], { concurrency, skipHas
|
||||
}
|
||||
|
||||
let multiBar: MultiBar | undefined;
|
||||
let totalSize = 0;
|
||||
const statsMap = new Map<string, Stats>();
|
||||
|
||||
// Calculate total size first
|
||||
for (const filepath of files) {
|
||||
const stats = await stat(filepath);
|
||||
statsMap.set(filepath, stats);
|
||||
totalSize += stats.size;
|
||||
}
|
||||
|
||||
if (progress) {
|
||||
multiBar = new MultiBar(
|
||||
{
|
||||
format: '{message} | {bar} | {percentage}% | ETA: {eta_formatted} | {value}/{total}',
|
||||
formatValue: (v: number, options, type) => {
|
||||
// Don't format percentage
|
||||
if (type === 'percentage') {
|
||||
return v.toString();
|
||||
}
|
||||
return byteSize(v).toString();
|
||||
},
|
||||
etaBuffer: 100, // Increase samples for ETA calculation
|
||||
},
|
||||
{ format: '{message} | {bar} | {percentage}% | ETA: {eta}s | {value}/{total} assets' },
|
||||
Presets.shades_classic,
|
||||
);
|
||||
|
||||
// Ensure we restore cursor on interrupt
|
||||
process.on('SIGINT', () => {
|
||||
if (multiBar) {
|
||||
multiBar.stop();
|
||||
}
|
||||
process.exit(0);
|
||||
});
|
||||
} else {
|
||||
console.log(`Received ${files.length} files (${byteSize(totalSize)}), hashing...`);
|
||||
console.log(`Received ${files.length} files, hashing...`);
|
||||
}
|
||||
|
||||
const hashProgressBar = multiBar?.create(totalSize, 0, {
|
||||
message: 'Hashing files ',
|
||||
});
|
||||
const checkProgressBar = multiBar?.create(totalSize, 0, {
|
||||
message: 'Checking for duplicates',
|
||||
});
|
||||
const hashProgressBar = multiBar?.create(files.length, 0, { message: 'Hashing files ' });
|
||||
const checkProgressBar = multiBar?.create(files.length, 0, { message: 'Checking for duplicates' });
|
||||
|
||||
const newFiles: string[] = [];
|
||||
const duplicates: Asset[] = [];
|
||||
@@ -242,13 +211,7 @@ export const checkForDuplicates = async (files: string[], { concurrency, skipHas
|
||||
}
|
||||
}
|
||||
|
||||
// Update progress based on total size of processed files
|
||||
let processedSize = 0;
|
||||
for (const asset of assets) {
|
||||
const stats = statsMap.get(asset.id);
|
||||
processedSize += stats?.size || 0;
|
||||
}
|
||||
checkProgressBar?.increment(processedSize);
|
||||
checkProgressBar?.increment(assets.length);
|
||||
},
|
||||
{ concurrency, retry: 3 },
|
||||
);
|
||||
@@ -258,10 +221,6 @@ export const checkForDuplicates = async (files: string[], { concurrency, skipHas
|
||||
|
||||
const queue = new Queue<string, AssetBulkUploadCheckItem[]>(
|
||||
async (filepath: string): Promise<AssetBulkUploadCheckItem[]> => {
|
||||
const stats = statsMap.get(filepath);
|
||||
if (!stats) {
|
||||
throw new Error(`Stats not found for ${filepath}`);
|
||||
}
|
||||
const dto = { id: filepath, checksum: await sha1(filepath) };
|
||||
|
||||
results.push(dto);
|
||||
@@ -272,7 +231,7 @@ export const checkForDuplicates = async (files: string[], { concurrency, skipHas
|
||||
void checkBulkUploadQueue.push(batch);
|
||||
}
|
||||
|
||||
hashProgressBar?.increment(stats.size);
|
||||
hashProgressBar?.increment();
|
||||
return results;
|
||||
},
|
||||
{ concurrency, retry: 3 },
|
||||
@@ -403,6 +362,23 @@ export const uploadFiles = async (
|
||||
const uploadFile = async (input: string, stats: Stats): Promise<AssetMediaResponseDto> => {
|
||||
const { baseUrl, headers } = defaults;
|
||||
|
||||
const assetPath = path.parse(input);
|
||||
const noExtension = path.join(assetPath.dir, assetPath.name);
|
||||
|
||||
const sidecarsFiles = await Promise.all(
|
||||
// XMP sidecars can come in two filename formats. For a photo named photo.ext, the filenames are photo.ext.xmp and photo.xmp
|
||||
[`${noExtension}.xmp`, `${input}.xmp`].map(async (sidecarPath) => {
|
||||
try {
|
||||
const stats = await stat(sidecarPath);
|
||||
return new UploadFile(sidecarPath, stats.size);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
const sidecarData = sidecarsFiles.find((file): file is UploadFile => file !== false);
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('deviceAssetId', `${basename(input)}-${stats.size}`.replaceAll(/\s+/g, ''));
|
||||
formData.append('deviceId', 'CLI');
|
||||
@@ -412,15 +388,8 @@ const uploadFile = async (input: string, stats: Stats): Promise<AssetMediaRespon
|
||||
formData.append('isFavorite', 'false');
|
||||
formData.append('assetData', new UploadFile(input, stats.size));
|
||||
|
||||
const sidecarPath = findSidecar(input);
|
||||
if (sidecarPath) {
|
||||
try {
|
||||
const stats = await stat(sidecarPath);
|
||||
const sidecarData = new UploadFile(sidecarPath, stats.size);
|
||||
formData.append('sidecarData', sidecarData);
|
||||
} catch {
|
||||
// noop
|
||||
}
|
||||
if (sidecarData) {
|
||||
formData.append('sidecarData', sidecarData);
|
||||
}
|
||||
|
||||
const response = await fetch(`${baseUrl}/assets`, {
|
||||
@@ -436,19 +405,7 @@ const uploadFile = async (input: string, stats: Stats): Promise<AssetMediaRespon
|
||||
return response.json();
|
||||
};
|
||||
|
||||
export const findSidecar = (filepath: string): string | undefined => {
|
||||
const assetPath = path.parse(filepath);
|
||||
const noExtension = path.join(assetPath.dir, assetPath.name);
|
||||
|
||||
// XMP sidecars can come in two filename formats. For a photo named photo.ext, the filenames are photo.ext.xmp and photo.xmp
|
||||
for (const sidecarPath of [`${noExtension}.xmp`, `${filepath}.xmp`]) {
|
||||
if (existsSync(sidecarPath)) {
|
||||
return sidecarPath;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const deleteFiles = async (uploaded: Asset[], duplicates: Asset[], options: UploadOptionsDto): Promise<void> => {
|
||||
const deleteFiles = async (uploaded: Asset[], duplicates: Asset[], options: UploadOptionsDto): Promise<void> => {
|
||||
let fileCount = 0;
|
||||
if (options.delete) {
|
||||
fileCount += uploaded.length;
|
||||
@@ -476,15 +433,7 @@ export const deleteFiles = async (uploaded: Asset[], duplicates: Asset[], option
|
||||
|
||||
const chunkDelete = async (files: Asset[]) => {
|
||||
for (const assetBatch of chunk(files, options.concurrency)) {
|
||||
await Promise.all(
|
||||
assetBatch.map(async (input: Asset) => {
|
||||
await unlink(input.filepath);
|
||||
const sidecarPath = findSidecar(input.filepath);
|
||||
if (sidecarPath) {
|
||||
await unlink(sidecarPath);
|
||||
}
|
||||
}),
|
||||
);
|
||||
await Promise.all(assetBatch.map((input: Asset) => unlink(input.filepath)));
|
||||
deletionProgress.update(assetBatch.length);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -14,65 +14,33 @@
|
||||
name: immich-dev
|
||||
|
||||
services:
|
||||
immich-app-base:
|
||||
profiles: ['_base']
|
||||
tmpfs:
|
||||
- /tmp
|
||||
volumes:
|
||||
- ..:/usr/src/app
|
||||
- pnpm_cache:/buildcache/pnpm_cache
|
||||
- server_node_modules:/usr/src/app/server/node_modules
|
||||
- web_node_modules:/usr/src/app/web/node_modules
|
||||
- github_node_modules:/usr/src/app/.github/node_modules
|
||||
- cli_node_modules:/usr/src/app/cli/node_modules
|
||||
- docs_node_modules:/usr/src/app/docs/node_modules
|
||||
- e2e_node_modules:/usr/src/app/e2e/node_modules
|
||||
- sdk_node_modules:/usr/src/app/open-api/typescript-sdk/node_modules
|
||||
- app_node_modules:/usr/src/app/node_modules
|
||||
- sveltekit:/usr/src/app/web/.svelte-kit
|
||||
- coverage:/usr/src/app/web/coverage
|
||||
|
||||
immich-init:
|
||||
extends:
|
||||
service: immich-app-base
|
||||
profiles: !reset []
|
||||
container_name: immich_init
|
||||
image: immich-server-dev:latest
|
||||
build:
|
||||
context: ../
|
||||
dockerfile: server/Dockerfile.dev
|
||||
target: dev
|
||||
command:
|
||||
- |
|
||||
pnpm install
|
||||
touch /tmp/init-complete
|
||||
exec tail -f /dev/null
|
||||
volumes:
|
||||
- pnpm_store_server:/buildcache/pnpm-store
|
||||
restart: 'no'
|
||||
healthcheck:
|
||||
test: ['CMD', 'test', '-f', '/tmp/init-complete']
|
||||
interval: 2s
|
||||
timeout: 3s
|
||||
retries: 300
|
||||
start_period: 300s
|
||||
|
||||
immich-server:
|
||||
extends:
|
||||
service: immich-app-base
|
||||
profiles: !reset []
|
||||
container_name: immich_server
|
||||
command: ['immich-dev']
|
||||
image: immich-server-dev:latest
|
||||
# extends:
|
||||
# file: hwaccel.transcoding.yml
|
||||
# service: cpu # set to one of [nvenc, quicksync, rkmpp, vaapi, vaapi-wsl] for accelerated transcoding
|
||||
build:
|
||||
context: ../
|
||||
dockerfile: server/Dockerfile.dev
|
||||
target: dev
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ..:/usr/src/app
|
||||
- ${UPLOAD_LOCATION}/photos:/data
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- pnpm_store_server:/buildcache/pnpm-store
|
||||
- pnpm-store:/usr/src/app/.pnpm-store
|
||||
- server-node_modules:/usr/src/app/server/node_modules
|
||||
- web-node_modules:/usr/src/app/web/node_modules
|
||||
- github-node_modules:/usr/src/app/.github/node_modules
|
||||
- cli-node_modules:/usr/src/app/cli/node_modules
|
||||
- docs-node_modules:/usr/src/app/docs/node_modules
|
||||
- e2e-node_modules:/usr/src/app/e2e/node_modules
|
||||
- sdk-node_modules:/usr/src/app/open-api/typescript-sdk/node_modules
|
||||
- app-node_modules:/usr/src/app/node_modules
|
||||
- sveltekit:/usr/src/app/web/.svelte-kit
|
||||
- coverage:/usr/src/app/web/coverage
|
||||
- ../plugins:/build/corePlugin
|
||||
env_file:
|
||||
- .env
|
||||
@@ -95,8 +63,6 @@ services:
|
||||
- 9231:9231
|
||||
- 2283:2283
|
||||
depends_on:
|
||||
immich-init:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_started
|
||||
database:
|
||||
@@ -105,9 +71,6 @@ services:
|
||||
disable: false
|
||||
|
||||
immich-web:
|
||||
extends:
|
||||
service: immich-app-base
|
||||
profiles: !reset []
|
||||
container_name: immich_web
|
||||
image: immich-web-dev:latest
|
||||
build:
|
||||
@@ -121,11 +84,20 @@ services:
|
||||
- 3000:3000
|
||||
- 24678:24678
|
||||
volumes:
|
||||
- pnpm_store_web:/buildcache/pnpm-store
|
||||
- ..:/usr/src/app
|
||||
- pnpm-store:/usr/src/app/.pnpm-store
|
||||
- server-node_modules:/usr/src/app/server/node_modules
|
||||
- web-node_modules:/usr/src/app/web/node_modules
|
||||
- github-node_modules:/usr/src/app/.github/node_modules
|
||||
- cli-node_modules:/usr/src/app/cli/node_modules
|
||||
- docs-node_modules:/usr/src/app/docs/node_modules
|
||||
- e2e-node_modules:/usr/src/app/e2e/node_modules
|
||||
- sdk-node_modules:/usr/src/app/open-api/typescript-sdk/node_modules
|
||||
- app-node_modules:/usr/src/app/node_modules
|
||||
- sveltekit:/usr/src/app/web/.svelte-kit
|
||||
- coverage:/usr/src/app/web/coverage
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
immich-init:
|
||||
condition: service_healthy
|
||||
immich-server:
|
||||
condition: service_started
|
||||
|
||||
@@ -144,7 +116,7 @@ services:
|
||||
- 3003:3003
|
||||
volumes:
|
||||
- ../machine-learning/immich_ml:/usr/src/immich_ml
|
||||
- model_cache:/cache
|
||||
- model-cache:/cache
|
||||
env_file:
|
||||
- .env
|
||||
depends_on:
|
||||
@@ -184,7 +156,7 @@ services:
|
||||
# image: prom/prometheus
|
||||
# volumes:
|
||||
# - ./prometheus.yml:/etc/prometheus/prometheus.yml
|
||||
# - prometheus_data:/prometheus
|
||||
# - prometheus-data:/prometheus
|
||||
|
||||
# first login uses admin/admin
|
||||
# add data source for http://immich-prometheus:9090 to get started
|
||||
@@ -195,22 +167,20 @@ services:
|
||||
# - 3000:3000
|
||||
# image: grafana/grafana:10.3.3-ubuntu
|
||||
# volumes:
|
||||
# - grafana_data:/var/lib/grafana
|
||||
# - grafana-data:/var/lib/grafana
|
||||
|
||||
volumes:
|
||||
model_cache:
|
||||
prometheus_data:
|
||||
grafana_data:
|
||||
pnpm_cache:
|
||||
pnpm_store_server:
|
||||
pnpm_store_web:
|
||||
server_node_modules:
|
||||
web_node_modules:
|
||||
github_node_modules:
|
||||
cli_node_modules:
|
||||
docs_node_modules:
|
||||
e2e_node_modules:
|
||||
sdk_node_modules:
|
||||
app_node_modules:
|
||||
model-cache:
|
||||
prometheus-data:
|
||||
grafana-data:
|
||||
pnpm-store:
|
||||
server-node_modules:
|
||||
web-node_modules:
|
||||
github-node_modules:
|
||||
cli-node_modules:
|
||||
docs-node_modules:
|
||||
e2e-node_modules:
|
||||
sdk-node_modules:
|
||||
app-node_modules:
|
||||
sveltekit:
|
||||
coverage:
|
||||
|
||||
@@ -1 +1 @@
|
||||
24.13.1
|
||||
24.13.0
|
||||
|
||||
@@ -44,7 +44,7 @@ While this guide focuses on VS Code, you have many options for Dev Container dev
|
||||
**Self-Hostable Options:**
|
||||
|
||||
- [Coder](https://coder.com) - Enterprise-focused, requires Terraform knowledge, self-managed
|
||||
- [DevPod](https://devpod.sh) - Client-only tool with excellent devcontainer.json support, works with any provider (local, cloud, or on-premise). Check [quick-start guide](#quick-start-guide-for-devpod-with-docker)
|
||||
- [DevPod](https://devpod.sh) - Client-only tool with excellent devcontainer.json support, works with any provider (local, cloud, or on-premise)
|
||||
:::
|
||||
|
||||
## Dev Container Services
|
||||
@@ -408,27 +408,7 @@ If you encounter issues:
|
||||
1. Check container logs: View → Output → Select "Dev Containers"
|
||||
2. Rebuild without cache: "Dev Containers: Rebuild Container Without Cache"
|
||||
3. Review [common Docker issues](https://docs.docker.com/desktop/troubleshoot/)
|
||||
4. Ask in [Discord](https://discord.immich.app) `#contributing` channel
|
||||
|
||||
### Quick-start guide for DevPod with docker
|
||||
|
||||
You will need DevPod CLI (check [DevPod CLI installation guide](https://devpod.sh/docs/getting-started/install)) and Docker Desktop.
|
||||
|
||||
```sh
|
||||
# Step 1: Clone the Repository
|
||||
git clone https://github.com/immich-app/immich.git
|
||||
cd immich
|
||||
|
||||
# Step 2: Prepare DevPod (if you haven't already)
|
||||
devpod provider add docker
|
||||
devpod provider use docker
|
||||
|
||||
# Step 3: Build 'immich-server-dev' docker image first manually
|
||||
docker build -f server/Dockerfile.dev -t immich-server-dev .
|
||||
|
||||
# Step 4: Now you can start devcontainer
|
||||
devpod up .
|
||||
```
|
||||
4. Ask in [Discord](https://discord.immich.app) `#help-desk-support` channel
|
||||
|
||||
## Mobile Development
|
||||
|
||||
|
||||
@@ -38,7 +38,6 @@ For the full list, refer to the [Immich source code](https://github.com/immich-a
|
||||
| `MP2T` | `.mts` `.m2ts` `.m2t` | :white_check_mark: | |
|
||||
| `MP4` | `.mp4` `.insv` | :white_check_mark: | |
|
||||
| `MPEG` | `.mpg` `.mpe` `.mpeg` | :white_check_mark: | |
|
||||
| `MXF` | `.mxf` | :white_check_mark: | |
|
||||
| `QUICKTIME` | `.mov` | :white_check_mark: | |
|
||||
| `WEBM` | `.webm` | :white_check_mark: | |
|
||||
| `WMV` | `.wmv` | :white_check_mark: | |
|
||||
|
||||
@@ -8,8 +8,7 @@ A config file can be provided as an alternative to the UI configuration.
|
||||
|
||||
### Step 1 - Create a new config file
|
||||
|
||||
In JSON format, create a new config file (e.g. `immich.json`) and put it in a location mounted in the container that can be accessed by Immich.
|
||||
YAML-formatted config files are also supported.
|
||||
In JSON format, create a new config file (e.g. `immich.json`) and put it in a location that can be accessed by Immich.
|
||||
The default configuration looks like this:
|
||||
|
||||
<details>
|
||||
@@ -252,15 +251,6 @@ So you can just grab it from there, paste it into a file and you're pretty much
|
||||
In your `.env` file, set the variable `IMMICH_CONFIG_FILE` to the path of your config.
|
||||
For more information, refer to the [Environment Variables](/install/environment-variables.md) section.
|
||||
|
||||
:::info Docker Compose
|
||||
In your `.env` file, the variables `UPLOAD_LOCATION` and `DB_DATA_LOCATION` concern the location on the host.
|
||||
However, the variable `IMMICH_CONFIG_FILE` concerns the location inside the container, and informs the `immich-server` container that a configuration file is present.
|
||||
|
||||
It is recommended to reuse this variable in your `docker-compose.yml`:
|
||||
|
||||
```yaml
|
||||
volumes:
|
||||
- ./configuration.yml:${IMMICH_CONFIG_FILE}
|
||||
```
|
||||
|
||||
::
|
||||
:::tip
|
||||
YAML-formatted config files are also supported.
|
||||
:::
|
||||
|
||||
@@ -8,6 +8,8 @@ sidebar_position: 85
|
||||
This is a community contribution and not officially supported by the Immich team, but included here for convenience.
|
||||
|
||||
Community support can be found in the dedicated channel on the [Discord Server](https://discord.immich.app/).
|
||||
|
||||
**Please report app issues to the corresponding [Github Repository](https://github.com/truenas/charts/tree/master/community/immich).**
|
||||
:::
|
||||
|
||||
Immich can easily be installed on a Synology NAS using Container Manager within DSM. If you have not installed Container Manager already, you can install it in the Packages Center. Refer to the [Container Manager docs](https://kb.synology.com/en-us/DSM/help/ContainerManager/docker_desc?version=7) for more information on using Container Manager.
|
||||
|
||||
@@ -23,9 +23,3 @@ run = "prettier --check ."
|
||||
[tasks."format-fix"]
|
||||
env._.path = "./node_modules/.bin"
|
||||
run = "prettier --write ."
|
||||
|
||||
[tasks.deploy]
|
||||
run = "wrangler pages deploy build --project-name=${PROJECT_NAME} --branch=${BRANCH_NAME}"
|
||||
|
||||
[tools]
|
||||
wrangler = "4.66.0"
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"format:fix": "prettier --write .",
|
||||
"start": "docusaurus start --port 3005",
|
||||
"copy:openapi": "jq -c < ../open-api/immich-openapi-specs.json > ./static/openapi.json || exit 0",
|
||||
"build": "pnpm run copy:openapi && docusaurus build",
|
||||
"build": "npm run copy:openapi && docusaurus build",
|
||||
"swizzle": "docusaurus swizzle",
|
||||
"deploy": "docusaurus deploy",
|
||||
"clear": "docusaurus clear",
|
||||
@@ -58,6 +58,6 @@
|
||||
"node": ">=20"
|
||||
},
|
||||
"volta": {
|
||||
"node": "24.13.1"
|
||||
"node": "24.13.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
24.13.1
|
||||
24.13.0
|
||||
|
||||
@@ -1,77 +1,86 @@
|
||||
name: immich-e2e
|
||||
|
||||
services:
|
||||
immich-app-base:
|
||||
extends:
|
||||
file: ../docker/docker-compose.dev.yml
|
||||
service: immich-app-base
|
||||
|
||||
immich-init:
|
||||
extends:
|
||||
file: ../docker/docker-compose.dev.yml
|
||||
service: immich-init
|
||||
container_name: immich-e2e-init
|
||||
|
||||
immich-server:
|
||||
extends:
|
||||
file: ../docker/docker-compose.dev.yml
|
||||
service: immich-server
|
||||
container_name: immich-e2e-server
|
||||
ports: !reset []
|
||||
env_file: !reset []
|
||||
command: ['immich-dev']
|
||||
image: immich-server-dev:latest
|
||||
build:
|
||||
context: ../
|
||||
dockerfile: server/Dockerfile.dev
|
||||
target: dev
|
||||
environment:
|
||||
DB_HOSTNAME: database
|
||||
DB_USERNAME: postgres
|
||||
DB_PASSWORD: postgres
|
||||
DB_DATABASE_NAME: immich
|
||||
IMMICH_MACHINE_LEARNING_ENABLED: 'false'
|
||||
IMMICH_TELEMETRY_INCLUDE: all
|
||||
IMMICH_ENV: testing
|
||||
IMMICH_PORT: '2285'
|
||||
IMMICH_IGNORE_MOUNT_CHECK_ERRORS: 'true'
|
||||
- DB_HOSTNAME=database
|
||||
- DB_USERNAME=postgres
|
||||
- DB_PASSWORD=postgres
|
||||
- DB_DATABASE_NAME=immich
|
||||
- IMMICH_MACHINE_LEARNING_ENABLED=false
|
||||
- IMMICH_TELEMETRY_INCLUDE=all
|
||||
- IMMICH_ENV=testing
|
||||
- IMMICH_PORT=2285
|
||||
- IMMICH_IGNORE_MOUNT_CHECK_ERRORS=true
|
||||
volumes:
|
||||
- ./test-assets:/test-assets
|
||||
- ..:/usr/src/app
|
||||
- ${UPLOAD_LOCATION}/photos:/data
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- pnpm-store:/usr/src/app/.pnpm-store
|
||||
- server-node_modules:/usr/src/app/server/node_modules
|
||||
- web-node_modules:/usr/src/app/web/node_modules
|
||||
- github-node_modules:/usr/src/app/.github/node_modules
|
||||
- cli-node_modules:/usr/src/app/cli/node_modules
|
||||
- docs-node_modules:/usr/src/app/docs/node_modules
|
||||
- e2e-node_modules:/usr/src/app/e2e/node_modules
|
||||
- sdk-node_modules:/usr/src/app/open-api/typescript-sdk/node_modules
|
||||
- app-node_modules:/usr/src/app/node_modules
|
||||
- sveltekit:/usr/src/app/web/.svelte-kit
|
||||
- coverage:/usr/src/app/web/coverage
|
||||
- ../plugins:/build/corePlugin
|
||||
depends_on:
|
||||
immich-init:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_started
|
||||
database:
|
||||
condition: service_healthy
|
||||
|
||||
immich-web:
|
||||
extends:
|
||||
file: ../docker/docker-compose.dev.yml
|
||||
service: immich-web
|
||||
container_name: immich-e2e-web
|
||||
ports: !override
|
||||
image: immich-web-dev:latest
|
||||
build:
|
||||
context: ../
|
||||
dockerfile: server/Dockerfile.dev
|
||||
target: dev
|
||||
command: ['immich-web']
|
||||
ports:
|
||||
- 2285:3000
|
||||
environment:
|
||||
IMMICH_SERVER_URL: http://immich-server:2285/
|
||||
depends_on:
|
||||
immich-init:
|
||||
condition: service_healthy
|
||||
- IMMICH_SERVER_URL=http://immich-server:2285/
|
||||
volumes:
|
||||
- ..:/usr/src/app
|
||||
- pnpm-store:/usr/src/app/.pnpm-store
|
||||
- server-node_modules:/usr/src/app/server/node_modules
|
||||
- web-node_modules:/usr/src/app/web/node_modules
|
||||
- github-node_modules:/usr/src/app/.github/node_modules
|
||||
- cli-node_modules:/usr/src/app/cli/node_modules
|
||||
- docs-node_modules:/usr/src/app/docs/node_modules
|
||||
- e2e-node_modules:/usr/src/app/e2e/node_modules
|
||||
- sdk-node_modules:/usr/src/app/open-api/typescript-sdk/node_modules
|
||||
- app-node_modules:/usr/src/app/node_modules
|
||||
- sveltekit:/usr/src/app/web/.svelte-kit
|
||||
- coverage:/usr/src/app/web/coverage
|
||||
restart: unless-stopped
|
||||
|
||||
redis:
|
||||
extends:
|
||||
file: ../docker/docker-compose.dev.yml
|
||||
service: redis
|
||||
container_name: immich-e2e-redis
|
||||
image: redis:6.2-alpine@sha256:46884be93652d02a96a176ccf173d1040bef365c5706aa7b6a1931caec8bfeef
|
||||
|
||||
database:
|
||||
extends:
|
||||
file: ../docker/docker-compose.dev.yml
|
||||
service: database
|
||||
container_name: immich-e2e-postgres
|
||||
image: ghcr.io/immich-app/postgres:14-vectorchord0.3.0@sha256:6f3e9d2c2177af16c2988ff71425d79d89ca630ec2f9c8db03209ab716542338
|
||||
command: -c fsync=off -c shared_preload_libraries=vchord.so -c config_file=/var/lib/postgresql/data/postgresql.conf
|
||||
env_file: !reset []
|
||||
ports: !override
|
||||
- 5435:5432
|
||||
environment:
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_DB: immich
|
||||
ports:
|
||||
- 5435:5432
|
||||
healthcheck:
|
||||
test: ['CMD-SHELL', 'pg_isready -U postgres -d immich']
|
||||
interval: 1s
|
||||
@@ -80,19 +89,17 @@ services:
|
||||
start_period: 10s
|
||||
|
||||
volumes:
|
||||
model_cache:
|
||||
prometheus_data:
|
||||
grafana_data:
|
||||
pnpm_cache:
|
||||
pnpm_store_server:
|
||||
pnpm_store_web:
|
||||
server_node_modules:
|
||||
web_node_modules:
|
||||
github_node_modules:
|
||||
cli_node_modules:
|
||||
docs_node_modules:
|
||||
e2e_node_modules:
|
||||
sdk_node_modules:
|
||||
app_node_modules:
|
||||
model-cache:
|
||||
prometheus-data:
|
||||
grafana-data:
|
||||
pnpm-store:
|
||||
server-node_modules:
|
||||
web-node_modules:
|
||||
github-node_modules:
|
||||
cli-node_modules:
|
||||
docs-node_modules:
|
||||
e2e-node_modules:
|
||||
sdk-node_modules:
|
||||
app-node_modules:
|
||||
sveltekit:
|
||||
coverage:
|
||||
|
||||
@@ -2,7 +2,6 @@ name: immich-e2e
|
||||
|
||||
services:
|
||||
e2e-auth-server:
|
||||
container_name: immich-e2e-auth-server
|
||||
build:
|
||||
context: ../e2e-auth-server
|
||||
ports:
|
||||
@@ -23,15 +22,15 @@ services:
|
||||
- BUILD_SOURCE_REF=e2e
|
||||
- BUILD_SOURCE_COMMIT=e2eeeeeeeeeeeeeeeeee
|
||||
environment:
|
||||
DB_HOSTNAME: database
|
||||
DB_USERNAME: postgres
|
||||
DB_PASSWORD: postgres
|
||||
DB_DATABASE_NAME: immich
|
||||
IMMICH_MACHINE_LEARNING_ENABLED: 'false'
|
||||
IMMICH_TELEMETRY_INCLUDE: all
|
||||
IMMICH_ENV: testing
|
||||
IMMICH_PORT: '2285'
|
||||
IMMICH_IGNORE_MOUNT_CHECK_ERRORS: 'true'
|
||||
- DB_HOSTNAME=database
|
||||
- DB_USERNAME=postgres
|
||||
- DB_PASSWORD=postgres
|
||||
- DB_DATABASE_NAME=immich
|
||||
- IMMICH_MACHINE_LEARNING_ENABLED=false
|
||||
- IMMICH_TELEMETRY_INCLUDE=all
|
||||
- IMMICH_ENV=testing
|
||||
- IMMICH_PORT=2285
|
||||
- IMMICH_IGNORE_MOUNT_CHECK_ERRORS=true
|
||||
volumes:
|
||||
- ./test-assets:/test-assets
|
||||
depends_on:
|
||||
@@ -43,14 +42,10 @@ services:
|
||||
- 2285:2285
|
||||
|
||||
redis:
|
||||
container_name: immich-e2e-redis
|
||||
image: docker.io/valkey/valkey:9@sha256:930b41430fb727f533c5982fe509b6f04233e26d0f7354e04de4b0d5c706e44e
|
||||
healthcheck:
|
||||
test: redis-cli ping || exit 1
|
||||
image: redis:6.2-alpine@sha256:46884be93652d02a96a176ccf173d1040bef365c5706aa7b6a1931caec8bfeef
|
||||
|
||||
database:
|
||||
container_name: immich-e2e-postgres
|
||||
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:bcf63357191b76a916ae5eb93464d65c07511da41e3bf7a8416db519b40b1c23
|
||||
image: ghcr.io/immich-app/postgres:14-vectorchord0.3.0@sha256:6f3e9d2c2177af16c2988ff71425d79d89ca630ec2f9c8db03209ab716542338
|
||||
command: -c fsync=off -c shared_preload_libraries=vchord.so -c config_file=/var/lib/postgresql/data/postgresql.conf
|
||||
environment:
|
||||
POSTGRES_PASSWORD: postgres
|
||||
@@ -58,7 +53,6 @@ services:
|
||||
POSTGRES_DB: immich
|
||||
ports:
|
||||
- 5435:5432
|
||||
shm_size: 128mb
|
||||
healthcheck:
|
||||
test: ['CMD-SHELL', 'pg_isready -U postgres -d immich']
|
||||
interval: 1s
|
||||
|
||||
@@ -7,17 +7,12 @@
|
||||
"scripts": {
|
||||
"test": "vitest --run",
|
||||
"test:watch": "vitest",
|
||||
"test:maintenance": "vitest --run --config vitest.maintenance.config.ts",
|
||||
"test:web": "pnpm exec playwright test --project=web",
|
||||
"test:web:maintenance": "pnpm exec playwright test --project=maintenance",
|
||||
"test:web:ui": "pnpm exec playwright test --project=ui",
|
||||
"start:web": "pnpm exec playwright test --ui --project=web",
|
||||
"start:web:maintenance": "pnpm exec playwright test --ui --project=maintenance",
|
||||
"start:web:ui": "pnpm exec playwright test --ui --project=ui",
|
||||
"test:web": "npx playwright test",
|
||||
"start:web": "npx playwright test --ui",
|
||||
"format": "prettier --check .",
|
||||
"format:fix": "prettier --write .",
|
||||
"lint": "eslint \"src/**/*.ts\" --max-warnings 0",
|
||||
"lint:fix": "pnpm run lint --fix",
|
||||
"lint:fix": "npm run lint -- --fix",
|
||||
"check": "tsc --noEmit"
|
||||
},
|
||||
"keywords": [],
|
||||
@@ -26,13 +21,13 @@
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.8.0",
|
||||
"@faker-js/faker": "^10.1.0",
|
||||
"@immich/cli": "workspace:*",
|
||||
"@immich/e2e-auth-server": "workspace:*",
|
||||
"@immich/sdk": "workspace:*",
|
||||
"@immich/cli": "file:../cli",
|
||||
"@immich/e2e-auth-server": "file:../e2e-auth-server",
|
||||
"@immich/sdk": "file:../open-api/typescript-sdk",
|
||||
"@playwright/test": "^1.44.1",
|
||||
"@socket.io/component-emitter": "^3.1.2",
|
||||
"@types/luxon": "^3.4.2",
|
||||
"@types/node": "^24.10.13",
|
||||
"@types/node": "^24.10.11",
|
||||
"@types/pg": "^8.15.1",
|
||||
"@types/pngjs": "^6.0.4",
|
||||
"@types/supertest": "^6.0.2",
|
||||
@@ -57,6 +52,6 @@
|
||||
"vitest": "^3.0.0"
|
||||
},
|
||||
"volta": {
|
||||
"node": "24.13.1"
|
||||
"node": "24.13.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import dotenv from 'dotenv';
|
||||
import { cpus } from 'node:os';
|
||||
import { resolve } from 'node:path';
|
||||
|
||||
dotenv.config({ quiet: true, path: resolve(import.meta.dirname, '.env') });
|
||||
dotenv.config({ path: resolve(import.meta.dirname, '.env') });
|
||||
|
||||
export const playwrightHost = process.env.PLAYWRIGHT_HOST ?? '127.0.0.1';
|
||||
export const playwrightDbHost = process.env.PLAYWRIGHT_DB_HOST ?? '127.0.0.1';
|
||||
@@ -48,7 +48,7 @@ const config: PlaywrightTestConfig = {
|
||||
{
|
||||
name: 'maintenance',
|
||||
use: { ...devices['Desktop Chrome'] },
|
||||
testDir: './src/specs/maintenance/web',
|
||||
testDir: './src/specs/maintenance',
|
||||
workers: 1,
|
||||
},
|
||||
],
|
||||
|
||||
@@ -253,8 +253,7 @@ describe('/asset', () => {
|
||||
|
||||
expect(status).toBe(200);
|
||||
expect(body.id).toEqual(facesAsset.id);
|
||||
const sortedPeople = body.people.toSorted((a: any, b: any) => a.name.localeCompare(b.name));
|
||||
expect(sortedPeople).toMatchObject(expectedFaces);
|
||||
expect(body.people).toMatchObject(expectedFaces);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ export const thumbnailUtils = {
|
||||
return page.locator(`[data-thumbnail-focus-container][data-asset="${assetId}"] button`);
|
||||
},
|
||||
selectedAsset(page: Page) {
|
||||
return page.locator('[data-thumbnail-focus-container][data-selected]');
|
||||
return page.locator('[data-thumbnail-focus-container]:has(button[aria-checked])');
|
||||
},
|
||||
async clickAssetId(page: Page, assetId: string) {
|
||||
await thumbnailUtils.withAssetId(page, assetId).click();
|
||||
@@ -103,8 +103,11 @@ export const thumbnailUtils = {
|
||||
await expect(thumbnailUtils.withAssetId(page, assetId).locator('[data-icon-archive]')).toHaveCount(0);
|
||||
},
|
||||
async expectSelectedReadonly(page: Page, assetId: string) {
|
||||
// todo - need a data attribute for selected
|
||||
await expect(
|
||||
page.locator(`[data-thumbnail-focus-container][data-asset="${assetId}"][data-selected]`),
|
||||
page.locator(
|
||||
`[data-thumbnail-focus-container][data-asset="${assetId}"] > .group.cursor-not-allowed > .rounded-xl`,
|
||||
),
|
||||
).toBeVisible();
|
||||
},
|
||||
async expectTimelineHasOnScreenAssets(page: Page) {
|
||||
|
||||
@@ -1,20 +1,15 @@
|
||||
import { defineConfig } from 'vitest/config';
|
||||
|
||||
const skipDockerSetup = process.env.VITEST_DISABLE_DOCKER_SETUP === 'true';
|
||||
|
||||
// skip `docker compose up` if `make e2e` was already run or if VITEST_DISABLE_DOCKER_SETUP is set
|
||||
// skip `docker compose up` if `make e2e` was already run
|
||||
const globalSetup: string[] = [];
|
||||
if (!skipDockerSetup) {
|
||||
try {
|
||||
await fetch('http://127.0.0.1:2285/api/server/ping');
|
||||
} catch {
|
||||
globalSetup.push('src/docker-compose.ts');
|
||||
}
|
||||
try {
|
||||
await fetch('http://127.0.0.1:2285/api/server/ping');
|
||||
} catch {
|
||||
globalSetup.push('src/docker-compose.ts');
|
||||
}
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
retry: process.env.CI ? 4 : 0,
|
||||
include: ['src/specs/server/**/*.e2e-spec.ts'],
|
||||
globalSetup,
|
||||
testTimeout: 15_000,
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
import { defineConfig } from 'vitest/config';
|
||||
|
||||
const skipDockerSetup = process.env.VITEST_DISABLE_DOCKER_SETUP === 'true';
|
||||
|
||||
// skip `docker compose up` if `make e2e` was already run or if VITEST_DISABLE_DOCKER_SETUP is set
|
||||
const globalSetup: string[] = [];
|
||||
if (!skipDockerSetup) {
|
||||
try {
|
||||
await fetch('http://127.0.0.1:2285/api/server/ping');
|
||||
} catch {
|
||||
globalSetup.push('src/docker-compose.ts');
|
||||
}
|
||||
}
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
retry: process.env.CI ? 4 : 0,
|
||||
include: ['src/specs/maintenance/server/**/*.e2e-spec.ts'],
|
||||
globalSetup,
|
||||
testTimeout: 15_000,
|
||||
pool: 'threads',
|
||||
poolOptions: {
|
||||
threads: {
|
||||
singleThread: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
12
i18n/ca.json
12
i18n/ca.json
@@ -311,7 +311,7 @@
|
||||
"search_jobs": "Cercar treballs…",
|
||||
"send_welcome_email": "Enviar correu electrònic de benvinguda",
|
||||
"server_external_domain_settings": "Domini extern",
|
||||
"server_external_domain_settings_description": "Domini utilitzat per a enllaços externs",
|
||||
"server_external_domain_settings_description": "Domini per enllaços públics compartits, incloent http(s)://",
|
||||
"server_public_users": "Usuaris públics",
|
||||
"server_public_users_description": "Tots els usuaris (nom i correu electrònic) apareixen a la llista a l'afegir un usuari als àlbums compartits. Si es desactiva, la llista només serà disponible pels usuaris administradors.",
|
||||
"server_settings": "Configuració del servidor",
|
||||
@@ -794,11 +794,6 @@
|
||||
"color": "Color",
|
||||
"color_theme": "Tema de color",
|
||||
"command": "Ordre",
|
||||
"command_palette_prompt": "Trobar ràpidament pàgines, accions o comandes",
|
||||
"command_palette_to_close": "per a tancar",
|
||||
"command_palette_to_navigate": "per a introduir",
|
||||
"command_palette_to_select": "per a seleccionar",
|
||||
"command_palette_to_show_all": "per a mostrar-ho tot",
|
||||
"comment_deleted": "Comentari esborrat",
|
||||
"comment_options": "Opcions de comentari",
|
||||
"comments_and_likes": "Comentaris i agradaments",
|
||||
@@ -1173,7 +1168,6 @@
|
||||
"exif_bottom_sheet_people": "PERSONES",
|
||||
"exif_bottom_sheet_person_add_person": "Afegir nom",
|
||||
"exit_slideshow": "Surt de la presentació de diapositives",
|
||||
"expand": "Ampliar-ho",
|
||||
"expand_all": "Ampliar-ho tot",
|
||||
"experimental_settings_new_asset_list_subtitle": "Treball en curs",
|
||||
"experimental_settings_new_asset_list_title": "Habilita la graella de fotos experimental",
|
||||
@@ -1538,7 +1532,7 @@
|
||||
"mobile_app_download_onboarding_note": "Descarregar la App de mòbil fent servir les seguents opcions",
|
||||
"model": "Model",
|
||||
"month": "Mes",
|
||||
"monthly_title_text_date_format": "MMMM a",
|
||||
"monthly_title_text_date_format": "MMMM y",
|
||||
"more": "Més",
|
||||
"move": "Moure",
|
||||
"move_down": "Moure cap avall",
|
||||
@@ -1648,7 +1642,6 @@
|
||||
"online": "En línia",
|
||||
"only_favorites": "Només preferits",
|
||||
"open": "Obrir",
|
||||
"open_calendar": "Obrir el calendari",
|
||||
"open_in_map_view": "Obrir a la vista del mapa",
|
||||
"open_in_openstreetmap": "Obre a OpenStreetMap",
|
||||
"open_the_search_filters": "Obriu els filtres de cerca",
|
||||
@@ -2190,7 +2183,6 @@
|
||||
"support": "Suport",
|
||||
"support_and_feedback": "Suport i comentaris",
|
||||
"support_third_party_description": "La vostra instal·lació immich la va empaquetar un tercer. Els problemes que experimenteu poden ser causats per aquest paquet així que, si us plau, plantegeu els poblemes amb ells en primer lloc mitjançant els enllaços següents.",
|
||||
"supporter": "Contribuïdor",
|
||||
"swap_merge_direction": "Canvia la direcció d'unió",
|
||||
"sync": "Sincronitza",
|
||||
"sync_albums": "Sincronitzar àlbums",
|
||||
|
||||
10
i18n/cs.json
10
i18n/cs.json
@@ -311,7 +311,7 @@
|
||||
"search_jobs": "Hledat úlohy…",
|
||||
"send_welcome_email": "Odeslat uvítací e-mail",
|
||||
"server_external_domain_settings": "Externí doména",
|
||||
"server_external_domain_settings_description": "Doména používaná pro externí odkazy",
|
||||
"server_external_domain_settings_description": "Doména pro veřejně sdílené odkazy, včetně http(s)://",
|
||||
"server_public_users": "Veřejní uživatelé",
|
||||
"server_public_users_description": "Všichni uživatelé (jméno a e-mail) jsou uvedeni při přidávání uživatele do sdílených alb. Pokud je tato funkce vypnuta, bude seznam uživatelů dostupný pouze uživatelům z řad správců.",
|
||||
"server_settings": "Server",
|
||||
@@ -794,11 +794,6 @@
|
||||
"color": "Barva",
|
||||
"color_theme": "Barevný motiv",
|
||||
"command": "Příkaz",
|
||||
"command_palette_prompt": "Rychlé vyhledávání stránek, akcí nebo příkazů",
|
||||
"command_palette_to_close": "zavřít",
|
||||
"command_palette_to_navigate": "vstoupit",
|
||||
"command_palette_to_select": "vybrat",
|
||||
"command_palette_to_show_all": "zobrazit vše",
|
||||
"comment_deleted": "Komentář odstraněn",
|
||||
"comment_options": "Možnosti komentáře",
|
||||
"comments_and_likes": "Komentáře a lajky",
|
||||
@@ -1173,7 +1168,6 @@
|
||||
"exif_bottom_sheet_people": "LIDÉ",
|
||||
"exif_bottom_sheet_person_add_person": "Přidat jméno",
|
||||
"exit_slideshow": "Ukončit prezentaci",
|
||||
"expand": "Rozbalit",
|
||||
"expand_all": "Rozbalit vše",
|
||||
"experimental_settings_new_asset_list_subtitle": "Zpracovávám",
|
||||
"experimental_settings_new_asset_list_title": "Povolení experimentální mřížky fotografií",
|
||||
@@ -1648,7 +1642,6 @@
|
||||
"online": "Online",
|
||||
"only_favorites": "Pouze oblíbené",
|
||||
"open": "Otevřít",
|
||||
"open_calendar": "Otevřít kalendář",
|
||||
"open_in_map_view": "Otevřít v zobrazení mapy",
|
||||
"open_in_openstreetmap": "Otevřít v OpenStreetMap",
|
||||
"open_the_search_filters": "Otevřít vyhledávací filtry",
|
||||
@@ -2190,7 +2183,6 @@
|
||||
"support": "Podpora",
|
||||
"support_and_feedback": "Podpora a zpětná vazba",
|
||||
"support_third_party_description": "Vaše Immich instalace byla připravena třetí stranou. Problémy, které se u vás vyskytly, mohou být způsobeny tímto balíčkem, proto se na ně obraťte v první řadě pomocí níže uvedených odkazů.",
|
||||
"supporter": "Podporovatel",
|
||||
"swap_merge_direction": "Obrátit směr sloučení",
|
||||
"sync": "Synchronizovat",
|
||||
"sync_albums": "Synchronizovat alba",
|
||||
|
||||
@@ -1218,7 +1218,6 @@
|
||||
"filter_description": "Conditions to filter the target assets",
|
||||
"filter_people": "Filter people",
|
||||
"filter_places": "Filter places",
|
||||
"filter_tags": "Filter tags",
|
||||
"filters": "Filters",
|
||||
"find_them_fast": "Find them fast by name with search",
|
||||
"first": "First",
|
||||
@@ -1946,7 +1945,6 @@
|
||||
"search_filter_ocr": "Search by OCR",
|
||||
"search_filter_people_title": "Select people",
|
||||
"search_filter_star_rating": "Star Rating",
|
||||
"search_filter_tags_title": "Select tags",
|
||||
"search_for": "Search for",
|
||||
"search_for_existing_person": "Search for existing person",
|
||||
"search_no_more_result": "No more results",
|
||||
@@ -2026,9 +2024,6 @@
|
||||
"set_profile_picture": "Set profile picture",
|
||||
"set_slideshow_to_fullscreen": "Set Slideshow to fullscreen",
|
||||
"set_stack_primary_asset": "Set as primary asset",
|
||||
"setting_image_navigation_enable_subtitle": "If enabled, you can navigate to the previous/next image by tapping the leftmost/rightmost quarter of the screen.",
|
||||
"setting_image_navigation_enable_title": "Tap to Navigate",
|
||||
"setting_image_navigation_title": "Image Navigation",
|
||||
"setting_image_viewer_help": "The detail viewer loads the small thumbnail first, then loads the medium-size preview (if enabled), finally loads the original (if enabled).",
|
||||
"setting_image_viewer_original_subtitle": "Enable to load the original full-resolution image (large!). Disable to reduce data usage (both network and on device cache).",
|
||||
"setting_image_viewer_original_title": "Load original image",
|
||||
|
||||
16
i18n/es.json
16
i18n/es.json
@@ -311,7 +311,7 @@
|
||||
"search_jobs": "Buscar trabajos…",
|
||||
"send_welcome_email": "Enviar correo de bienvenida",
|
||||
"server_external_domain_settings": "Dominio externo",
|
||||
"server_external_domain_settings_description": "Dominio usado para enlaces externos",
|
||||
"server_external_domain_settings_description": "Dominio para enlaces públicos compartidos, incluidos http(s)://",
|
||||
"server_public_users": "Usuarios públicos",
|
||||
"server_public_users_description": "Cuando se añade un usuario a los álbumes compartidos, todos los usuarios aparecen en una lista con su nombre y su correo electrónico. Si deshabilita esta opción, solo los administradores podrán ver la lista de usuarios.",
|
||||
"server_settings": "Configuración del servidor",
|
||||
@@ -794,11 +794,6 @@
|
||||
"color": "Color",
|
||||
"color_theme": "Color del tema",
|
||||
"command": "Comando",
|
||||
"command_palette_prompt": "Encuentra rápidamente páginas, acciones o comandos",
|
||||
"command_palette_to_close": "para cerrar",
|
||||
"command_palette_to_navigate": "para entrar",
|
||||
"command_palette_to_select": "para seleccionar",
|
||||
"command_palette_to_show_all": "para mostrar todo",
|
||||
"comment_deleted": "Comentario borrado",
|
||||
"comment_options": "Opciones de comentarios",
|
||||
"comments_and_likes": "Comentarios y me gusta",
|
||||
@@ -1173,7 +1168,6 @@
|
||||
"exif_bottom_sheet_people": "PERSONAS",
|
||||
"exif_bottom_sheet_person_add_person": "Añadir nombre",
|
||||
"exit_slideshow": "Salir de la presentación",
|
||||
"expand": "Expandir",
|
||||
"expand_all": "Expandir todo",
|
||||
"experimental_settings_new_asset_list_subtitle": "Trabajo en progreso",
|
||||
"experimental_settings_new_asset_list_title": "Habilitar cuadrícula fotográfica experimental",
|
||||
@@ -1648,7 +1642,6 @@
|
||||
"online": "En línea",
|
||||
"only_favorites": "Solo favoritos",
|
||||
"open": "Abierto",
|
||||
"open_calendar": "Abrir calendario",
|
||||
"open_in_map_view": "Abrir en la vista del mapa",
|
||||
"open_in_openstreetmap": "Abrir en OpenStreetMap",
|
||||
"open_the_search_filters": "Abre los filtros de búsqueda",
|
||||
@@ -1771,7 +1764,7 @@
|
||||
"profile_picture_set": "Conjunto de imágenes de perfil.",
|
||||
"public_album": "Álbum público",
|
||||
"public_share": "Compartir públicamente",
|
||||
"purchase_account_info": "Colaborador",
|
||||
"purchase_account_info": "Seguidor",
|
||||
"purchase_activated_subtitle": "Gracias por apoyar a Immich y al software de código abierto",
|
||||
"purchase_activated_time": "Activado el {date}",
|
||||
"purchase_activated_title": "Su clave ha sido activada correctamente",
|
||||
@@ -1784,7 +1777,7 @@
|
||||
"purchase_button_select": "Seleccionar",
|
||||
"purchase_failed_activation": "¡Error al activar! ¡Por favor, revisa tu correo electrónico para obtener la clave del producto correcta!",
|
||||
"purchase_individual_description_1": "Para un usuario",
|
||||
"purchase_individual_description_2": "Estatus de colaborador",
|
||||
"purchase_individual_description_2": "Estado de soporte",
|
||||
"purchase_individual_title": "Individual",
|
||||
"purchase_input_suggestion": "¿Tiene una clave de producto? Introdúzcala a continuación",
|
||||
"purchase_license_subtitle": "Compre Immich para apoyar el desarrollo continuo del servicio",
|
||||
@@ -1800,7 +1793,7 @@
|
||||
"purchase_remove_server_product_key": "Eliminar la clave de producto del servidor",
|
||||
"purchase_remove_server_product_key_prompt": "¿Está seguro de que desea eliminar la clave de producto del servidor?",
|
||||
"purchase_server_description_1": "Para todo el servidor",
|
||||
"purchase_server_description_2": "Estatus de colaborador",
|
||||
"purchase_server_description_2": "Estado del soporte",
|
||||
"purchase_server_title": "Servidor",
|
||||
"purchase_settings_server_activated": "La clave del producto del servidor la administra el administrador",
|
||||
"query_asset_id": "Consultar ID de recurso",
|
||||
@@ -2190,7 +2183,6 @@
|
||||
"support": "Soporte",
|
||||
"support_and_feedback": "Soporte y comentarios",
|
||||
"support_third_party_description": "Esta instalación de Immich fue empaquetada por un tercero. Los problemas actuales pueden ser ocasionados por ese paquete; por favor, discuta sus inconvenientes con el empaquetador antes de usar los enlaces de abajo.",
|
||||
"supporter": "Colaborador",
|
||||
"swap_merge_direction": "Alternar dirección de mezcla",
|
||||
"sync": "Sincronizar",
|
||||
"sync_albums": "Sincronizar álbumes",
|
||||
|
||||
10
i18n/et.json
10
i18n/et.json
@@ -311,7 +311,7 @@
|
||||
"search_jobs": "Otsi töödet…",
|
||||
"send_welcome_email": "Saada tervituskiri",
|
||||
"server_external_domain_settings": "Väline domeen",
|
||||
"server_external_domain_settings_description": "Domeen väliste linkide jaoks",
|
||||
"server_external_domain_settings_description": "Domeen avalikult jagatud linkide jaoks, k.a. http(s)://",
|
||||
"server_public_users": "Avalikud kasutajad",
|
||||
"server_public_users_description": "Kasutaja jagatud albumisse lisamisel kuvatakse kõiki kasutajaid (nime ja e-posti aadressiga). Kui keelatud, kuvatakse kasutajate nimekirja ainult administraatoritele.",
|
||||
"server_settings": "Serveri seaded",
|
||||
@@ -794,11 +794,6 @@
|
||||
"color": "Värv",
|
||||
"color_theme": "Värviteema",
|
||||
"command": "Käsk",
|
||||
"command_palette_prompt": "Leia kiirelt lehti, tegevusi või käske",
|
||||
"command_palette_to_close": "sulge",
|
||||
"command_palette_to_navigate": "sisene",
|
||||
"command_palette_to_select": "vali",
|
||||
"command_palette_to_show_all": "näita kõiki",
|
||||
"comment_deleted": "Kommentaar kustutatud",
|
||||
"comment_options": "Kommentaari valikud",
|
||||
"comments_and_likes": "Kommentaarid ja meeldimised",
|
||||
@@ -1173,7 +1168,6 @@
|
||||
"exif_bottom_sheet_people": "ISIKUD",
|
||||
"exif_bottom_sheet_person_add_person": "Lisa nimi",
|
||||
"exit_slideshow": "Sulge slaidiesitlus",
|
||||
"expand": "Laienda",
|
||||
"expand_all": "Näita kõik",
|
||||
"experimental_settings_new_asset_list_subtitle": "Töös",
|
||||
"experimental_settings_new_asset_list_title": "Luba eksperimentaalne fotoruudistik",
|
||||
@@ -1648,7 +1642,6 @@
|
||||
"online": "Ühendatud",
|
||||
"only_favorites": "Ainult lemmikud",
|
||||
"open": "Ava",
|
||||
"open_calendar": "Ava kalender",
|
||||
"open_in_map_view": "Ava kaardi vaates",
|
||||
"open_in_openstreetmap": "Ava OpenStreetMap",
|
||||
"open_the_search_filters": "Ava otsingufiltrid",
|
||||
@@ -2190,7 +2183,6 @@
|
||||
"support": "Tugi",
|
||||
"support_and_feedback": "Tugi ja tagasiside",
|
||||
"support_third_party_description": "Sinu Immich'i install on kolmanda osapoole pakendatud. Probleemid, mida täheldad, võivad olla põhjustatud selle pakendamise poolt, seega võta esmajärjekorras nendega ühendust, kasutades allolevaid linke.",
|
||||
"supporter": "Toetaja",
|
||||
"swap_merge_direction": "Muuda ühendamise suunda",
|
||||
"sync": "Sünkrooni",
|
||||
"sync_albums": "Sünkrooni albumid",
|
||||
|
||||
10
i18n/fr.json
10
i18n/fr.json
@@ -311,7 +311,7 @@
|
||||
"search_jobs": "Recherche des tâches…",
|
||||
"send_welcome_email": "Envoyer un courriel de bienvenue",
|
||||
"server_external_domain_settings": "Domaine externe",
|
||||
"server_external_domain_settings_description": "Nom de domaine utilisé pour les liens externes",
|
||||
"server_external_domain_settings_description": "Nom de domaine pour les liens partagés publics, y compris http(s)://",
|
||||
"server_public_users": "Utilisateurs publics",
|
||||
"server_public_users_description": "Tous les utilisateurs (nom et courriel) sont listés lors de l'ajout d'un utilisateur à des albums partagés. Quand cela est désactivé, la liste des utilisateurs est uniquement disponible pour les comptes administrateurs.",
|
||||
"server_settings": "Paramètres du serveur",
|
||||
@@ -794,11 +794,6 @@
|
||||
"color": "Couleur",
|
||||
"color_theme": "Thème de couleur",
|
||||
"command": "Commande",
|
||||
"command_palette_prompt": "Trouver rapidement des pages, actions ou commandes",
|
||||
"command_palette_to_close": "pour fermer",
|
||||
"command_palette_to_navigate": "pour entrer",
|
||||
"command_palette_to_select": "pour sélectionner",
|
||||
"command_palette_to_show_all": "pour tout afficher",
|
||||
"comment_deleted": "Commentaire supprimé",
|
||||
"comment_options": "Options des commentaires",
|
||||
"comments_and_likes": "Commentaires et \"J'aime\"",
|
||||
@@ -1173,7 +1168,6 @@
|
||||
"exif_bottom_sheet_people": "PERSONNES",
|
||||
"exif_bottom_sheet_person_add_person": "Ajouter un nom",
|
||||
"exit_slideshow": "Quitter le diaporama",
|
||||
"expand": "Développer",
|
||||
"expand_all": "Tout développer",
|
||||
"experimental_settings_new_asset_list_subtitle": "En cours de développement",
|
||||
"experimental_settings_new_asset_list_title": "Activer la grille de photos expérimentale",
|
||||
@@ -1648,7 +1642,6 @@
|
||||
"online": "En ligne",
|
||||
"only_favorites": "Uniquement les favoris",
|
||||
"open": "Ouvrir",
|
||||
"open_calendar": "Ouvrir le calendrier",
|
||||
"open_in_map_view": "Montrer sur la carte",
|
||||
"open_in_openstreetmap": "Ouvrir dans OpenStreetMap",
|
||||
"open_the_search_filters": "Ouvrir les filtres de recherche",
|
||||
@@ -2190,7 +2183,6 @@
|
||||
"support": "Soutenir",
|
||||
"support_and_feedback": "Support & Retours",
|
||||
"support_third_party_description": "Votre installation d'Immich est packagée via une application tierce. Si vous rencontrez des anomalies, elles peuvent venir de ce packaging tiers, merci de créer les anomalies avec ces tiers en premier lieu en utilisant les liens ci-dessous.",
|
||||
"supporter": "Contributeur",
|
||||
"swap_merge_direction": "Inverser la direction de fusion",
|
||||
"sync": "Synchroniser",
|
||||
"sync_albums": "Synchroniser dans des albums",
|
||||
|
||||
10
i18n/ga.json
10
i18n/ga.json
@@ -311,7 +311,7 @@
|
||||
"search_jobs": "Cuardaigh poist…",
|
||||
"send_welcome_email": "Seol ríomhphost fáilte",
|
||||
"server_external_domain_settings": "Fearann seachtrach",
|
||||
"server_external_domain_settings_description": "Fearann a úsáidtear le haghaidh naisc sheachtracha",
|
||||
"server_external_domain_settings_description": "Fearann le haghaidh naisc chomhroinnte poiblí, lena n-áirítear http(s)://",
|
||||
"server_public_users": "Úsáideoirí Poiblí",
|
||||
"server_public_users_description": "Liostaítear gach úsáideoir (ainm agus ríomhphost) nuair a chuirtear úsáideoir le halbaim chomhroinnte. Nuair a bhíonn sé díchumasaithe, ní bheidh an liosta úsáideoirí ar fáil ach d’úsáideoirí riarthóra.",
|
||||
"server_settings": "Socruithe Freastalaí",
|
||||
@@ -794,11 +794,6 @@
|
||||
"color": "Dath",
|
||||
"color_theme": "Téama datha",
|
||||
"command": "Ordú",
|
||||
"command_palette_prompt": "Aimsigh leathanaigh, gníomhartha nó orduithe go tapa",
|
||||
"command_palette_to_close": "a dhúnadh",
|
||||
"command_palette_to_navigate": "dul isteach",
|
||||
"command_palette_to_select": "a roghnú",
|
||||
"command_palette_to_show_all": "chun gach rud a thaispeáint",
|
||||
"comment_deleted": "Trácht scriosta",
|
||||
"comment_options": "Roghanna tráchta",
|
||||
"comments_and_likes": "Tráchtanna & Is maith liom",
|
||||
@@ -1173,7 +1168,6 @@
|
||||
"exif_bottom_sheet_people": "DAOINE",
|
||||
"exif_bottom_sheet_person_add_person": "Cuir ainm leis",
|
||||
"exit_slideshow": "Scoir an Taispeántais Sleamhnán",
|
||||
"expand": "Leathnaigh",
|
||||
"expand_all": "Leathnaigh gach rud",
|
||||
"experimental_settings_new_asset_list_subtitle": "Obair ar siúl",
|
||||
"experimental_settings_new_asset_list_title": "Cumasaigh eangach grianghraf turgnamhach",
|
||||
@@ -1648,7 +1642,6 @@
|
||||
"online": "Ar líne",
|
||||
"only_favorites": "Is fearr leat amháin",
|
||||
"open": "Oscail",
|
||||
"open_calendar": "Oscail an féilire",
|
||||
"open_in_map_view": "Oscail i radharc léarscáile",
|
||||
"open_in_openstreetmap": "Oscail in OpenStreetMap",
|
||||
"open_the_search_filters": "Oscail na scagairí cuardaigh",
|
||||
@@ -2190,7 +2183,6 @@
|
||||
"support": "Tacaíocht",
|
||||
"support_and_feedback": "Tacaíocht & Aiseolas",
|
||||
"support_third_party_description": "Rinne tríú páirtí pacáiste de do shuiteáil Immich. D’fhéadfadh sé gur an pacáiste sin ba chúis le fadhbanna a bhíonn agat, mar sin tabhair ceisteanna dóibh ar dtús trí na naisc thíos a úsáid.",
|
||||
"supporter": "Tacaíochtaí",
|
||||
"swap_merge_direction": "Malartaigh treo an chumaisc",
|
||||
"sync": "Sioncrónaigh",
|
||||
"sync_albums": "Sioncrónaigh albaim",
|
||||
|
||||
10
i18n/it.json
10
i18n/it.json
@@ -311,7 +311,7 @@
|
||||
"search_jobs": "Cerca Attività…",
|
||||
"send_welcome_email": "Invia email di benvenuto",
|
||||
"server_external_domain_settings": "Dominio esterno",
|
||||
"server_external_domain_settings_description": "Dominio utilizzato per i link esterni",
|
||||
"server_external_domain_settings_description": "Dominio per link condivisi pubblicamente, incluso http(s)://",
|
||||
"server_public_users": "Utenti Pubblici",
|
||||
"server_public_users_description": "Tutti gli utenti (nome ed e-mail) sono elencati quando si aggiunge un utente agli album condivisi. Quando disabilitato, l'elenco degli utenti sarà disponibile solo per gli utenti amministratori.",
|
||||
"server_settings": "Impostazioni Server",
|
||||
@@ -794,11 +794,6 @@
|
||||
"color": "Colore",
|
||||
"color_theme": "Colore Tema",
|
||||
"command": "Comando",
|
||||
"command_palette_prompt": "Trova rapidamente pagine, azioni o comandi",
|
||||
"command_palette_to_close": "per chiudere",
|
||||
"command_palette_to_navigate": "per entrare",
|
||||
"command_palette_to_select": "per selezionare",
|
||||
"command_palette_to_show_all": "per mostrare tutto",
|
||||
"comment_deleted": "Commento eliminato",
|
||||
"comment_options": "Opzioni per i commenti",
|
||||
"comments_and_likes": "Commenti & mi piace",
|
||||
@@ -1173,7 +1168,6 @@
|
||||
"exif_bottom_sheet_people": "PERSONE",
|
||||
"exif_bottom_sheet_person_add_person": "Aggiungi nome",
|
||||
"exit_slideshow": "Esci dalla presentazione",
|
||||
"expand": "Espandi",
|
||||
"expand_all": "Espandi tutto",
|
||||
"experimental_settings_new_asset_list_subtitle": "Lavori in corso",
|
||||
"experimental_settings_new_asset_list_title": "Attiva griglia foto sperimentale",
|
||||
@@ -1648,7 +1642,6 @@
|
||||
"online": "Online",
|
||||
"only_favorites": "Solo preferiti",
|
||||
"open": "Apri",
|
||||
"open_calendar": "Apri il calendario",
|
||||
"open_in_map_view": "Apri nella visualizzazione mappa",
|
||||
"open_in_openstreetmap": "Apri su OpenStreetMap",
|
||||
"open_the_search_filters": "Apri filtri di ricerca",
|
||||
@@ -2190,7 +2183,6 @@
|
||||
"support": "Supporto",
|
||||
"support_and_feedback": "Supporto & Feedback",
|
||||
"support_third_party_description": "La tua installazione di Immich è stata costruita da terze parti. I problemi che riscontri potrebbero essere causati da altri pacchetti, quindi ti preghiamo di sollevare il problema in prima istanza utilizzando i link sottostanti.",
|
||||
"supporter": "Sostenitore",
|
||||
"swap_merge_direction": "Scambia direzione di unione",
|
||||
"sync": "Sincronizza",
|
||||
"sync_albums": "Sincronizza album",
|
||||
|
||||
14
i18n/nl.json
14
i18n/nl.json
@@ -311,7 +311,7 @@
|
||||
"search_jobs": "Taak zoeken…",
|
||||
"send_welcome_email": "Stuur een welkomstmail",
|
||||
"server_external_domain_settings": "Extern domein",
|
||||
"server_external_domain_settings_description": "Domein voor externe links",
|
||||
"server_external_domain_settings_description": "Domein voor openbaar gedeelde links, inclusief http(s)://",
|
||||
"server_public_users": "Openbare gebruikerslijst",
|
||||
"server_public_users_description": "Alle gebruikers (met naam en e-mailadres) worden weergegeven wanneer een gebruiker wordt toegevoegd aan gedeelde albums. Wanneer uitgeschakeld, is de gebruikerslijst alleen beschikbaar voor beheerders.",
|
||||
"server_settings": "Serverinstellingen",
|
||||
@@ -793,12 +793,7 @@
|
||||
"collapse_all": "Alles inklappen",
|
||||
"color": "Kleur",
|
||||
"color_theme": "Kleurenthema",
|
||||
"command": "Commando",
|
||||
"command_palette_prompt": "Vind snel pagina's, acties of commando's",
|
||||
"command_palette_to_close": "om te sluiten",
|
||||
"command_palette_to_navigate": "om te navigeren",
|
||||
"command_palette_to_select": "om te selecteren",
|
||||
"command_palette_to_show_all": "om alles te tonen",
|
||||
"command": "Opdracht",
|
||||
"comment_deleted": "Opmerking verwijderd",
|
||||
"comment_options": "Opties voor opmerkingen",
|
||||
"comments_and_likes": "Opmerkingen & likes",
|
||||
@@ -1173,7 +1168,6 @@
|
||||
"exif_bottom_sheet_people": "MENSEN",
|
||||
"exif_bottom_sheet_person_add_person": "Naam toevoegen",
|
||||
"exit_slideshow": "Diavoorstelling sluiten",
|
||||
"expand": "Uitklappen",
|
||||
"expand_all": "Alles uitvouwen",
|
||||
"experimental_settings_new_asset_list_subtitle": "Werk in uitvoering",
|
||||
"experimental_settings_new_asset_list_title": "Experimenteel fotoraster inschakelen",
|
||||
@@ -1648,7 +1642,6 @@
|
||||
"online": "Online",
|
||||
"only_favorites": "Alleen favorieten",
|
||||
"open": "Openen",
|
||||
"open_calendar": "Open kalender",
|
||||
"open_in_map_view": "Openen in kaartweergave",
|
||||
"open_in_openstreetmap": "Openen in OpenStreetMap",
|
||||
"open_the_search_filters": "Open de zoekfilters",
|
||||
@@ -2190,7 +2183,6 @@
|
||||
"support": "Ondersteuning",
|
||||
"support_and_feedback": "Ondersteuning & feedback",
|
||||
"support_third_party_description": "Je Immich installatie is door een derde partij samengesteld. Problemen die je ervaart, kunnen door dat pakket veroorzaakt zijn. Meld problemen in eerste instantie bij hen via de onderstaande links.",
|
||||
"supporter": "Supporter",
|
||||
"swap_merge_direction": "Wissel richting voor samenvoegen om",
|
||||
"sync": "Synchroniseren",
|
||||
"sync_albums": "Albums synchroniseren",
|
||||
@@ -2302,7 +2294,7 @@
|
||||
"unstack_action_prompt": "{count} item(s) ontstapeld",
|
||||
"unstacked_assets_count": "{count, plural, one {# item} other {# items}} ontstapeld",
|
||||
"unsupported_field_type": "Veldtype niet ondersteund",
|
||||
"untagged": "Zonder tags",
|
||||
"untagged": "Ongemarkeerd",
|
||||
"untitled_workflow": "Naamloze werkstroom",
|
||||
"up_next": "Volgende",
|
||||
"update_location_action_prompt": "Werk de locatie bij van {count} geselecteerde items met:",
|
||||
|
||||
@@ -2064,7 +2064,7 @@
|
||||
"shared_by_you": "Udostępnione przez ciebie",
|
||||
"shared_from_partner": "Zdjęcia od {partner}",
|
||||
"shared_intent_upload_button_progress_text": "{current} / {total} Przesłano",
|
||||
"shared_link_app_bar_title": "Udostępnione",
|
||||
"shared_link_app_bar_title": "Udostępnione linki",
|
||||
"shared_link_clipboard_copied_massage": "Skopiowane do schowka",
|
||||
"shared_link_clipboard_text": "Link: {link}\nHasło: {password}",
|
||||
"shared_link_create_error": "Błąd podczas tworzenia linka do udostępnienia",
|
||||
|
||||
12
i18n/ru.json
12
i18n/ru.json
@@ -311,7 +311,7 @@
|
||||
"search_jobs": "Поиск задач…",
|
||||
"send_welcome_email": "Отправить приветственное письмо",
|
||||
"server_external_domain_settings": "Внешний домен",
|
||||
"server_external_domain_settings_description": "Домен для публичных ссылок",
|
||||
"server_external_domain_settings_description": "Домен для публичных ссылок, включая http(s)://",
|
||||
"server_public_users": "Публичные пользователи",
|
||||
"server_public_users_description": "Выводить список пользователей (имена и email) в общих альбомах. Когда отключено, список доступен только администраторам, пользователи смогут делиться только ссылкой.",
|
||||
"server_settings": "Настройки сервера",
|
||||
@@ -794,11 +794,6 @@
|
||||
"color": "Цвет",
|
||||
"color_theme": "Цветовая тема",
|
||||
"command": "Команда",
|
||||
"command_palette_prompt": "Быстрый поиск страниц, действий или команд",
|
||||
"command_palette_to_close": "закрыть",
|
||||
"command_palette_to_navigate": "навигация",
|
||||
"command_palette_to_select": "выбрать",
|
||||
"command_palette_to_show_all": "показать все",
|
||||
"comment_deleted": "Комментарий удалён",
|
||||
"comment_options": "Действия с комментарием",
|
||||
"comments_and_likes": "Комментарии и отметки \"нравится\"",
|
||||
@@ -1173,7 +1168,6 @@
|
||||
"exif_bottom_sheet_people": "ЛЮДИ",
|
||||
"exif_bottom_sheet_person_add_person": "Добавить имя",
|
||||
"exit_slideshow": "Выйти из слайд-шоу",
|
||||
"expand": "Развернуть",
|
||||
"expand_all": "Развернуть всё",
|
||||
"experimental_settings_new_asset_list_subtitle": "В разработке",
|
||||
"experimental_settings_new_asset_list_title": "Включить экспериментальную сетку фотографий",
|
||||
@@ -1648,7 +1642,6 @@
|
||||
"online": "Доступен",
|
||||
"only_favorites": "Только избранное",
|
||||
"open": "Открыть",
|
||||
"open_calendar": "Открыть календарь",
|
||||
"open_in_map_view": "Открыть в режиме просмотра карты",
|
||||
"open_in_openstreetmap": "Открыть в OpenStreetMap",
|
||||
"open_the_search_filters": "Открыть фильтры поиска",
|
||||
@@ -2135,7 +2128,7 @@
|
||||
"show_search_options": "Показать параметры поиска",
|
||||
"show_shared_links": "Показать публичные ссылки",
|
||||
"show_slideshow_transition": "Плавный переход",
|
||||
"show_supporter_badge": "Значок спонсорства",
|
||||
"show_supporter_badge": "Значок поддержки",
|
||||
"show_supporter_badge_description": "Показать значок поддержки",
|
||||
"show_text_recognition": "Показать распознанный текст",
|
||||
"show_text_search_menu": "Показать меню текстового поиска",
|
||||
@@ -2190,7 +2183,6 @@
|
||||
"support": "Поддержка",
|
||||
"support_and_feedback": "Поддержка и обратная связь",
|
||||
"support_third_party_description": "Ваша установка immich была упакована сторонним разработчиком. Проблемы, с которыми вы столкнулись, могут быть вызваны этим пакетом, поэтому, пожалуйста, в первую очередь обращайтесь к ним, используя ссылки ниже.",
|
||||
"supporter": "Спонсор Immich",
|
||||
"swap_merge_direction": "Изменить направление слияния",
|
||||
"sync": "Синхр.",
|
||||
"sync_albums": "Синхронизировать альбомы",
|
||||
|
||||
10
i18n/sk.json
10
i18n/sk.json
@@ -311,7 +311,7 @@
|
||||
"search_jobs": "Vyhľadať úlohy…",
|
||||
"send_welcome_email": "Odoslať uvítací e-mail",
|
||||
"server_external_domain_settings": "Externá doména",
|
||||
"server_external_domain_settings_description": "Doména používaná pre externé odkazy",
|
||||
"server_external_domain_settings_description": "Verejná doména pre zdieľané odkazy, vrátane http(s)://",
|
||||
"server_public_users": "Verejní používatelia",
|
||||
"server_public_users_description": "Všetci používatelia (meno a email) sú uvedení pri pridávaní používateľa do zdieľaných albumov. Ak je táto funkcia vypnutá, zoznam používateľov bude dostupný iba správcom.",
|
||||
"server_settings": "Server",
|
||||
@@ -794,11 +794,6 @@
|
||||
"color": "Farba",
|
||||
"color_theme": "Farba témy",
|
||||
"command": "Príkaz",
|
||||
"command_palette_prompt": "Rýchlo vyhľadajte stránky, akcie alebo príkazy ako",
|
||||
"command_palette_to_close": "zatvoriť",
|
||||
"command_palette_to_navigate": "vložiť",
|
||||
"command_palette_to_select": "vybrať",
|
||||
"command_palette_to_show_all": "zobraziť všetko",
|
||||
"comment_deleted": "Komentár bol odstránený",
|
||||
"comment_options": "Možnosti komentára",
|
||||
"comments_and_likes": "Komentáre a páči sa mi to",
|
||||
@@ -1173,7 +1168,6 @@
|
||||
"exif_bottom_sheet_people": "ĽUDIA",
|
||||
"exif_bottom_sheet_person_add_person": "Pridať meno",
|
||||
"exit_slideshow": "Opustiť prezentáciu",
|
||||
"expand": "Rozbaliť",
|
||||
"expand_all": "Rozbaliť všetko",
|
||||
"experimental_settings_new_asset_list_subtitle": "Prebiehajúca práca",
|
||||
"experimental_settings_new_asset_list_title": "Povolenie experimentálnej mriežky fotografií",
|
||||
@@ -1648,7 +1642,6 @@
|
||||
"online": "Online",
|
||||
"only_favorites": "Len obľúbené",
|
||||
"open": "Otvoriť",
|
||||
"open_calendar": "Otvoriť kalendár",
|
||||
"open_in_map_view": "Otvoriť v mape",
|
||||
"open_in_openstreetmap": "Otvoriť v OpenStreetMap",
|
||||
"open_the_search_filters": "Otvoriť vyhľadávacie filtre",
|
||||
@@ -2190,7 +2183,6 @@
|
||||
"support": "Podpora",
|
||||
"support_and_feedback": "Podpora a spätná väzba",
|
||||
"support_third_party_description": "Vaša inštalácia Immich bola pripravená treťou stranou. Problémy, ktoré sa vyskytli, môžu byť spôsobené týmto balíčkom, preto sa na nich obráťte v prvom rade cez nasledujúce odkazy.",
|
||||
"supporter": "Podporovateľ",
|
||||
"swap_merge_direction": "Vymeniť smer zlúčenia",
|
||||
"sync": "Synchronizovať",
|
||||
"sync_albums": "Synchronizovať albumy",
|
||||
|
||||
10
i18n/sl.json
10
i18n/sl.json
@@ -311,7 +311,7 @@
|
||||
"search_jobs": "Išči opravila…",
|
||||
"send_welcome_email": "Pošlji pozdravno e-pošto",
|
||||
"server_external_domain_settings": "Zunanja domena",
|
||||
"server_external_domain_settings_description": "Domena, uporabljena za zunanje povezave",
|
||||
"server_external_domain_settings_description": "Domena za javne skupne povezave, vključno s http(s)://",
|
||||
"server_public_users": "Javni uporabniki",
|
||||
"server_public_users_description": "Vsi uporabniki (ime in e-pošta) so navedeni pri dodajanju uporabnika v albume v skupni rabi. Ko je onemogočen, bo seznam uporabnikov na voljo samo skrbniškim uporabnikom.",
|
||||
"server_settings": "Nastavitve strežnika",
|
||||
@@ -794,11 +794,6 @@
|
||||
"color": "Barva",
|
||||
"color_theme": "Barva teme",
|
||||
"command": "Ukaz",
|
||||
"command_palette_prompt": "Hitro iskanje strani, dejanj ali ukazov",
|
||||
"command_palette_to_close": "zapreti",
|
||||
"command_palette_to_navigate": "vstopiti",
|
||||
"command_palette_to_select": "izbrati",
|
||||
"command_palette_to_show_all": "prikazati vse",
|
||||
"comment_deleted": "Komentar izbrisan",
|
||||
"comment_options": "Možnosti komentiranja",
|
||||
"comments_and_likes": "Komentarji in všečki",
|
||||
@@ -1173,7 +1168,6 @@
|
||||
"exif_bottom_sheet_people": "OSEBE",
|
||||
"exif_bottom_sheet_person_add_person": "Dodaj ime",
|
||||
"exit_slideshow": "Zapustite diaprojekcijo",
|
||||
"expand": "Razširi",
|
||||
"expand_all": "Razširi vse",
|
||||
"experimental_settings_new_asset_list_subtitle": "Delo v teku",
|
||||
"experimental_settings_new_asset_list_title": "Omogoči eksperimentalno mrežo fotografij",
|
||||
@@ -1648,7 +1642,6 @@
|
||||
"online": "Povezano",
|
||||
"only_favorites": "Samo priljubljene",
|
||||
"open": "Odpri",
|
||||
"open_calendar": "Odpri koledar",
|
||||
"open_in_map_view": "Odpri v pogledu zemljevida",
|
||||
"open_in_openstreetmap": "Odpri v OpenStreetMap",
|
||||
"open_the_search_filters": "Odpri iskalne filtre",
|
||||
@@ -2190,7 +2183,6 @@
|
||||
"support": "Podpora",
|
||||
"support_and_feedback": "Podpora in povratne informacije",
|
||||
"support_third_party_description": "Vašo namestitev Immich je pakirala tretja oseba. Težave, ki jih imate, lahko povzroči ta paket, zato prosimo, da težave najprej izpostavite njim, tako da uporabite spodnje povezave.",
|
||||
"supporter": "Podpornik",
|
||||
"swap_merge_direction": "Zamenjaj smer združevanja",
|
||||
"sync": "Sinhronizacija",
|
||||
"sync_albums": "Sinhronizacija albumov",
|
||||
|
||||
24
i18n/th.json
24
i18n/th.json
@@ -6,8 +6,8 @@
|
||||
"action": "ดำเนินการ",
|
||||
"action_common_update": "อัปเดต",
|
||||
"actions": "การดำเนินการ",
|
||||
"active": "กำลังทำงาน",
|
||||
"active_count": "กำลังทำงาน: {count}",
|
||||
"active": "ใช้งานอยู่",
|
||||
"active_count": "ใช้งานอยู่: {count}",
|
||||
"activity": "กิจกรรม",
|
||||
"activity_changed": "กิจกรรม{enabled, select, true {เปิด} other {ปิด}}อยู่",
|
||||
"add": "เพิ่ม",
|
||||
@@ -27,7 +27,7 @@
|
||||
"add_path": "เพิ่มพาทที่ตั้ง",
|
||||
"add_photos": "เพิ่มรูปภาพ",
|
||||
"add_tag": "เพิ่มแท็ก",
|
||||
"add_to": "เพิ่มไปยัง…",
|
||||
"add_to": "เพิ่มไปยัง …",
|
||||
"add_to_album": "เพิ่มไปยังอัลบั้ม",
|
||||
"add_to_album_bottom_sheet_added": "เพิ่มไปยัง {album} แล้ว",
|
||||
"add_to_album_bottom_sheet_already_exists": "อยู่ใน {album} อยู่แล้ว",
|
||||
@@ -200,7 +200,7 @@
|
||||
"metadata_settings": "การตั้งค่า Metadata",
|
||||
"metadata_settings_description": "จัดการการตั้งค่า Metadata",
|
||||
"migration_job": "การโยกย้าย",
|
||||
"migration_job_description": "ย้ายภาพตัวอย่างสำหรับสื่อและใบหน้าไปยังโครงสร้างโฟลเดอร์ล่าสุด",
|
||||
"migration_job_description": "ย้ายภาพตัวอย่างสื่อและใบหน้าไปยังโครงสร้างโฟลเดอร์ล่าสุด",
|
||||
"nightly_tasks_cluster_new_faces_setting": "คลัสเตอร์ใบหน้าใหม่",
|
||||
"nightly_tasks_generate_memories_setting": "สร้างความทรงจำ",
|
||||
"nightly_tasks_generate_memories_setting_description": "สร้างความทรงจำใหม่จากสื่อ",
|
||||
@@ -526,20 +526,20 @@
|
||||
"asset_viewer_settings_subtitle": "ตั้งค่าการแสดงแกลเลอรี",
|
||||
"asset_viewer_settings_title": "ตัวดูทรัพยากร",
|
||||
"assets": "สื่อ",
|
||||
"assets_added_count": "เพิ่มสื่อ {count, plural, one{# รายการ} other {# รายการ}}แล้ว",
|
||||
"assets_added_count": "เพิ่ม {count, plural, one{# สื่อ} other {# สื่อ}} แล้ว",
|
||||
"assets_added_to_album_count": "เพิ่ม {count, plural, one {# asset} other {# assets}} ไปยังอัลบั้ม",
|
||||
"assets_added_to_albums_count": "เพิ่มสื่อ {assetTotal, plural, one {# รายการ} other {# รายการ}} ไปยังอัลบั้ม {albumTotal, plural, one {# รายการ} other {# รายการ}}แล้ว",
|
||||
"assets_cannot_be_added_to_album_count": "ไม่สามารถเพิ่ม {count, plural, one {สื่อ} other {สื่อ}} ไปยังอัลบั้ม",
|
||||
"assets_cannot_be_added_to_albums": "ไม่สามารถเพิ่ม{count, plural, one {สื่อ} other {สื่อ}}ไปยังอัลบั้มใด ๆ ได้",
|
||||
"assets_count": "สื่อ {count, plural, one {# รายการ} other {# รายการ}}",
|
||||
"assets_count": "{count, plural, one { สื่อ} other { สื่อ}}",
|
||||
"assets_deleted_permanently": "{count} สื่อถูกลบอย่างถาวร",
|
||||
"assets_deleted_permanently_from_server": "ลบสื่อ {count} รายการออกจากเซิร์ฟเวอร์ Immich อย่างถาวรแล้ว",
|
||||
"assets_deleted_permanently_from_server": "ลบ {count} สื่อออกจาก Immich อย่างถาวร",
|
||||
"assets_downloaded_failed": "ดาวน์โหลด {count, plural, one {ไฟล์} other {ไฟล์}} ไม่สำเร็จ - {error}",
|
||||
"assets_downloaded_successfully": "ดาวน์โหลด {count, plural, one {ไฟล์} other {ไฟล์}} สำเร็จ",
|
||||
"assets_moved_to_trash_count": "ย้าย {count, plural, one {# asset} other {# assets}} ไปยังถังขยะแล้ว",
|
||||
"assets_permanently_deleted_count": "ลบ {count, plural, one {# asset} other {# assets}} ทิ้งถาวร",
|
||||
"assets_removed_count": "{count, plural, one {# asset} other {# assets}} ถูกลบแล้ว",
|
||||
"assets_removed_permanently_from_device": "ลบสื่อ {count} รายการออกจากอุปกรณ์ของคุณอย่างถาวรแล้ว",
|
||||
"assets_removed_permanently_from_device": "นำ {count} สื่อออกจากอุปกรณ์อย่างถาวร",
|
||||
"assets_restore_confirmation": "คุณแน่ใจหรือไม่ว่าต้องการกู้คืนสื่อที่ทิ้งทั้งหมด? คุณไม่สามารถย้อนกลับการดำเนินการนี้ได้! โปรดทราบว่าสื่อออฟไลน์ใดๆ ไม่สามารถกู้คืนได้ด้วยวิธีนี้",
|
||||
"assets_restored_count": "{count, plural, one {# asset} other {# assets}} คืนค่า",
|
||||
"assets_restored_successfully": "กู้คืน {count} สื่อสำเร็จ",
|
||||
@@ -699,8 +699,6 @@
|
||||
"cleanup_found_assets": "พบสื่อ {count} รายการที่สำรองข้อมูลแล้ว",
|
||||
"cleanup_found_assets_with_size": "พบสื่อ {count} รายการที่สำรองข้อมูลแล้ว ({size})",
|
||||
"cleanup_icloud_shared_albums_excluded": "อัลบั้มที่แชร์บน iCloud ไม่นับรวมในการค้นหา",
|
||||
"cleanup_no_assets_found": "ไม่พบสื่อที่ตรงตามเงื่อนไขด้านบน \"เพิ่มพื้นที่ว่าง\" สามารถลบได้เฉพาะสื่อที่สำรองข้อมูลบนเซิร์ฟเวอร์เรียบร้อยแล้วเท่านั้น",
|
||||
"cleanup_preview_title": "สื่อที่จะลบ ({count})",
|
||||
"clear": "ล้าง",
|
||||
"clear_all": "ล้างทั้งหมด",
|
||||
"clear_all_recent_searches": "ล้างประวัติการค้นหา",
|
||||
@@ -1064,7 +1062,7 @@
|
||||
"export_as_json": "ส่งออกเป็นไฟล์ JSON",
|
||||
"extension": "ส่วนต่อขยาย",
|
||||
"external": "ภายนอก",
|
||||
"external_libraries": "คลังภาพภายนอก",
|
||||
"external_libraries": "ภายนอกคลังภาพ",
|
||||
"external_network": "การเชื่อมต่อภายนอก",
|
||||
"external_network_sheet_info": "เมื่อไม่ได้เชื่อมต่อ Wi-Fi ที่เลือกไว้ แอพจะเชื่อมต่อเซิร์ฟเวอร์ผ่าน URL ด้านล่างตามลำดับ",
|
||||
"face_unassigned": "ไม่กำหนดมอบหมาย",
|
||||
@@ -1660,8 +1658,8 @@
|
||||
"server_endpoint": "ปลายทางเซิร์ฟเวอร์",
|
||||
"server_info_box_app_version": "เวอร์ชันแอพ",
|
||||
"server_info_box_server_url": "URL เซิร์ฟเวอร์",
|
||||
"server_offline": "เซิร์ฟเวอร์ออฟไลน์",
|
||||
"server_online": "เซิร์ฟเวอร์ออนไลน์",
|
||||
"server_offline": "Server ออฟไลน์",
|
||||
"server_online": "Server ออนไลน์",
|
||||
"server_privacy": "ความเป็นส่วนตัวเซิร์ฟเวอร์",
|
||||
"server_stats": "สถิติเซิร์ฟเวอร์",
|
||||
"server_version": "เวอร์ชันของเซิร์ฟเวอร์",
|
||||
|
||||
@@ -41,7 +41,6 @@
|
||||
"add_to_bottom_bar": "加至",
|
||||
"add_to_shared_album": "加至共享相簿",
|
||||
"add_url": "加網址",
|
||||
"add_workflow_step": "增加工作步驟",
|
||||
"added_to_favorites": "已加至最愛",
|
||||
"added_to_favorites_count": "已加{count, number} 個項目至最愛",
|
||||
"admin": {
|
||||
|
||||
@@ -311,7 +311,7 @@
|
||||
"search_jobs": "搜尋任務…",
|
||||
"send_welcome_email": "傳送歡迎電子郵件",
|
||||
"server_external_domain_settings": "外部網域",
|
||||
"server_external_domain_settings_description": "公開分享連結的網域",
|
||||
"server_external_domain_settings_description": "公開分享連結的網域,包含 http(s)://",
|
||||
"server_public_users": "公開使用者",
|
||||
"server_public_users_description": "將使用者新增至共享相簿時,會列出所有使用者(姓名與電子郵件)。若停用,使用者清單將僅供管理員查看。",
|
||||
"server_settings": "伺服器設定",
|
||||
@@ -794,11 +794,6 @@
|
||||
"color": "顏色",
|
||||
"color_theme": "色彩主題",
|
||||
"command": "命令",
|
||||
"command_palette_prompt": "快速尋找頁面,動作或者指令",
|
||||
"command_palette_to_close": "關閉",
|
||||
"command_palette_to_navigate": "輸入",
|
||||
"command_palette_to_select": "選擇",
|
||||
"command_palette_to_show_all": "顯示全部",
|
||||
"comment_deleted": "留言已刪除",
|
||||
"comment_options": "留言選項",
|
||||
"comments_and_likes": "留言與喜歡",
|
||||
@@ -1173,7 +1168,6 @@
|
||||
"exif_bottom_sheet_people": "人物",
|
||||
"exif_bottom_sheet_person_add_person": "新增姓名",
|
||||
"exit_slideshow": "結束幻燈片",
|
||||
"expand": "展開",
|
||||
"expand_all": "展開全部",
|
||||
"experimental_settings_new_asset_list_subtitle": "正在處理",
|
||||
"experimental_settings_new_asset_list_title": "啟用實驗性相片格狀版面",
|
||||
@@ -1648,7 +1642,6 @@
|
||||
"online": "線上",
|
||||
"only_favorites": "僅顯示己收藏",
|
||||
"open": "開啟",
|
||||
"open_calendar": "打開日曆",
|
||||
"open_in_map_view": "開啟地圖檢視",
|
||||
"open_in_openstreetmap": "用 OpenStreetMap 開啟",
|
||||
"open_the_search_filters": "開啟搜尋篩選器",
|
||||
@@ -1923,7 +1916,7 @@
|
||||
"search_by_description_example": "在沙壩的健行之日",
|
||||
"search_by_filename": "依檔名或副檔名搜尋",
|
||||
"search_by_filename_example": "如 IMG_1234.JPG 或 PNG",
|
||||
"search_by_ocr": "透過OCR搜尋",
|
||||
"search_by_ocr": "通過OCR蒐索",
|
||||
"search_by_ocr_example": "拿鐵",
|
||||
"search_camera_lens_model": "蒐索鏡頭型號...",
|
||||
"search_camera_make": "搜尋相機製造商…",
|
||||
@@ -1942,7 +1935,7 @@
|
||||
"search_filter_location_title": "選擇位置",
|
||||
"search_filter_media_type": "媒體類型",
|
||||
"search_filter_media_type_title": "選擇媒體類型",
|
||||
"search_filter_ocr": "透過OCR搜尋",
|
||||
"search_filter_ocr": "通過OCR蒐索",
|
||||
"search_filter_people_title": "選擇人物",
|
||||
"search_filter_star_rating": "評分",
|
||||
"search_for": "搜尋",
|
||||
@@ -2190,7 +2183,6 @@
|
||||
"support": "支援",
|
||||
"support_and_feedback": "支援與回饋",
|
||||
"support_third_party_description": "您安裝的 Immich 是由第三方打包的。您遇到的問題可能是該套件造成的,所以請先使用下面的連結向他們提出問題。",
|
||||
"supporter": "支持者",
|
||||
"swap_merge_direction": "交換合併方向",
|
||||
"sync": "同步",
|
||||
"sync_albums": "同步相簿",
|
||||
|
||||
@@ -8,6 +8,7 @@ readme = "README.md"
|
||||
dependencies = [
|
||||
"aiocache>=0.12.1,<1.0",
|
||||
"fastapi>=0.95.2,<1.0",
|
||||
"ftfy>=6.1.1",
|
||||
"gunicorn>=21.1.0",
|
||||
"huggingface-hub>=0.20.1,<1.0",
|
||||
"insightface>=0.7.3,<1.0",
|
||||
|
||||
11
mise.toml
11
mise.toml
@@ -14,15 +14,15 @@ config_roots = [
|
||||
]
|
||||
|
||||
[tools]
|
||||
node = "24.13.1"
|
||||
node = "24.13.0"
|
||||
flutter = "3.35.7"
|
||||
pnpm = "10.29.3"
|
||||
pnpm = "10.28.2"
|
||||
terragrunt = "0.98.0"
|
||||
opentofu = "1.11.4"
|
||||
java = "21.0.2"
|
||||
|
||||
[tools."github:CQLabs/homebrew-dcm"]
|
||||
version = "1.35.1"
|
||||
version = "1.30.0"
|
||||
bin = "dcm"
|
||||
postinstall = "chmod +x $MISE_TOOL_INSTALL_PATH/dcm"
|
||||
|
||||
@@ -37,12 +37,13 @@ run = "pnpm install --filter @immich/sdk --frozen-lockfile"
|
||||
|
||||
[tasks."sdk:build"]
|
||||
dir = "open-api/typescript-sdk"
|
||||
run = "pnpm run build"
|
||||
env._.path = "./node_modules/.bin"
|
||||
run = "tsc"
|
||||
|
||||
# i18n tasks
|
||||
[tasks."i18n:format"]
|
||||
dir = "i18n"
|
||||
run = "pnpm run format"
|
||||
run = { task = ":i18n:format-fix" }
|
||||
|
||||
[tasks."i18n:format-fix"]
|
||||
dir = "i18n"
|
||||
|
||||
@@ -117,7 +117,7 @@
|
||||
android:pathPrefix="/albums/" />
|
||||
<data
|
||||
android:host="my.immich.app"
|
||||
android:pathPrefix="/people/" />
|
||||
android:pathPrefix="/memories/" />
|
||||
<data
|
||||
android:host="my.immich.app"
|
||||
android:path="/memory" />
|
||||
|
||||
@@ -48,6 +48,7 @@ fun Bitmap.toNativeBuffer(): Map<String, Long> {
|
||||
try {
|
||||
val buffer = NativeBuffer.wrap(pointer, size)
|
||||
copyPixelsToBuffer(buffer)
|
||||
recycle()
|
||||
return mapOf(
|
||||
"pointer" to pointer,
|
||||
"width" to width.toLong(),
|
||||
@@ -56,9 +57,8 @@ fun Bitmap.toNativeBuffer(): Map<String, Long> {
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
NativeBuffer.free(pointer)
|
||||
throw e
|
||||
} finally {
|
||||
recycle()
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
version: '>=1.29.0 <=1.36.0'
|
||||
version: '>=1.29.0 <=1.30.0'
|
||||
|
||||
@@ -16,8 +16,9 @@ class ScrollToDateEvent extends Event {
|
||||
}
|
||||
|
||||
// Asset Viewer Events
|
||||
class ViewerShowDetailsEvent extends Event {
|
||||
const ViewerShowDetailsEvent();
|
||||
class ViewerOpenBottomSheetEvent extends Event {
|
||||
final bool activitiesMode;
|
||||
const ViewerOpenBottomSheetEvent({this.activitiesMode = false});
|
||||
}
|
||||
|
||||
class ViewerReloadAssetEvent extends Event {
|
||||
|
||||
@@ -73,9 +73,6 @@ enum StoreKey<T> {
|
||||
autoPlayVideo<bool>._(139),
|
||||
albumGridView<bool>._(140),
|
||||
|
||||
// Image viewer navigation settings
|
||||
tapToNavigate<bool>._(141),
|
||||
|
||||
// Experimental stuff
|
||||
photoManagerCustomFilter<bool>._(1000),
|
||||
betaPromptShown<bool>._(1001),
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
class Tag {
|
||||
final String id;
|
||||
final String value;
|
||||
|
||||
const Tag({required this.id, required this.value});
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'Tag(id: $id, value: $value)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(covariant Tag other) {
|
||||
if (identical(this, other)) return true;
|
||||
|
||||
return other.id == id && other.value == value;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return id.hashCode ^ value.hashCode;
|
||||
}
|
||||
|
||||
static Tag fromDto(TagResponseDto dto) {
|
||||
return Tag(id: dto.id, value: dto.value);
|
||||
}
|
||||
}
|
||||
@@ -10,10 +10,6 @@ class DriftPeopleService {
|
||||
|
||||
const DriftPeopleService(this._repository, this._personApiRepository);
|
||||
|
||||
Future<DriftPerson?> get(String personId) {
|
||||
return _repository.get(personId);
|
||||
}
|
||||
|
||||
Future<List<DriftPerson>> getAssetPeople(String assetId) {
|
||||
return _repository.getAssetPeople(assetId);
|
||||
}
|
||||
|
||||
@@ -183,8 +183,8 @@ class TimelineService {
|
||||
return _buffer.slice(start, start + count);
|
||||
}
|
||||
|
||||
// Preload assets around the given index for asset viewer
|
||||
Future<void> preloadAssets(int index) => _mutex.run(() => _loadAssets(index, math.min(5, _totalAssets - index)));
|
||||
// Pre-cache assets around the given index for asset viewer
|
||||
Future<void> preCacheAssets(int index) => _mutex.run(() => _loadAssets(index, math.min(5, _totalAssets - index)));
|
||||
|
||||
BaseAsset getRandomAsset() => _buffer.elementAt(math.Random().nextInt(_buffer.length));
|
||||
|
||||
|
||||
@@ -32,125 +32,3 @@ class FastClampingScrollPhysics extends ClampingScrollPhysics {
|
||||
damping: 80,
|
||||
);
|
||||
}
|
||||
|
||||
class SnapScrollPhysics extends ScrollPhysics {
|
||||
static const _minFlingVelocity = 700.0;
|
||||
static const minSnapDistance = 30.0;
|
||||
|
||||
static final _spring = SpringDescription.withDampingRatio(mass: .5, stiffness: 300);
|
||||
|
||||
const SnapScrollPhysics({super.parent});
|
||||
|
||||
@override
|
||||
SnapScrollPhysics applyTo(ScrollPhysics? ancestor) {
|
||||
return SnapScrollPhysics(parent: buildParent(ancestor));
|
||||
}
|
||||
|
||||
@override
|
||||
Simulation? createBallisticSimulation(ScrollMetrics position, double velocity) {
|
||||
assert(
|
||||
position is SnapScrollPosition,
|
||||
'SnapScrollPhysics can only be used with Scrollables that use a '
|
||||
'controller whose createScrollPosition returns a SnapScrollPosition',
|
||||
);
|
||||
|
||||
final snapOffset = (position as SnapScrollPosition).snapOffset;
|
||||
if (snapOffset <= 0) {
|
||||
return super.createBallisticSimulation(position, velocity);
|
||||
}
|
||||
|
||||
if (position.pixels >= snapOffset) {
|
||||
final simulation = super.createBallisticSimulation(position, velocity);
|
||||
if (simulation == null || simulation.x(double.infinity) >= snapOffset) {
|
||||
return simulation;
|
||||
}
|
||||
}
|
||||
|
||||
return ScrollSpringSimulation(
|
||||
_spring,
|
||||
position.pixels,
|
||||
target(position, velocity, snapOffset),
|
||||
velocity,
|
||||
tolerance: toleranceFor(position),
|
||||
);
|
||||
}
|
||||
|
||||
static double target(ScrollMetrics position, double velocity, double snapOffset) {
|
||||
if (velocity > _minFlingVelocity) return snapOffset;
|
||||
if (velocity < -_minFlingVelocity) return position.pixels < snapOffset ? 0.0 : snapOffset;
|
||||
return position.pixels < minSnapDistance ? 0.0 : snapOffset;
|
||||
}
|
||||
}
|
||||
|
||||
class SnapScrollPosition extends ScrollPositionWithSingleContext {
|
||||
double snapOffset;
|
||||
|
||||
SnapScrollPosition({this.snapOffset = 0.0, required super.physics, required super.context, super.oldPosition});
|
||||
}
|
||||
|
||||
class ProxyScrollController extends ScrollController {
|
||||
final ScrollController scrollController;
|
||||
|
||||
ProxyScrollController({required this.scrollController});
|
||||
|
||||
SnapScrollPosition get snapPosition => position as SnapScrollPosition;
|
||||
|
||||
@override
|
||||
ScrollPosition createScrollPosition(ScrollPhysics physics, ScrollContext context, ScrollPosition? oldPosition) {
|
||||
return ProxyScrollPosition(
|
||||
scrollController: scrollController,
|
||||
physics: physics,
|
||||
context: context,
|
||||
oldPosition: oldPosition,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
scrollController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
class ProxyScrollPosition extends SnapScrollPosition {
|
||||
final ScrollController scrollController;
|
||||
|
||||
ProxyScrollPosition({
|
||||
required this.scrollController,
|
||||
required super.physics,
|
||||
required super.context,
|
||||
super.oldPosition,
|
||||
});
|
||||
|
||||
@override
|
||||
double setPixels(double newPixels) {
|
||||
final overscroll = super.setPixels(newPixels);
|
||||
if (scrollController.hasClients && scrollController.position.pixels != pixels) {
|
||||
scrollController.position.forcePixels(pixels);
|
||||
}
|
||||
return overscroll;
|
||||
}
|
||||
|
||||
@override
|
||||
void forcePixels(double value) {
|
||||
super.forcePixels(value);
|
||||
if (scrollController.hasClients && scrollController.position.pixels != pixels) {
|
||||
scrollController.position.forcePixels(pixels);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
double get maxScrollExtent => scrollController.hasClients && scrollController.position.hasContentDimensions
|
||||
? scrollController.position.maxScrollExtent
|
||||
: super.maxScrollExtent;
|
||||
|
||||
@override
|
||||
double get minScrollExtent => scrollController.hasClients && scrollController.position.hasContentDimensions
|
||||
? scrollController.position.minScrollExtent
|
||||
: super.minScrollExtent;
|
||||
|
||||
@override
|
||||
double get viewportDimension => scrollController.hasClients && scrollController.position.hasViewportDimension
|
||||
? scrollController.position.viewportDimension
|
||||
: super.viewportDimension;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||
import 'package:immich_mobile/domain/models/person.model.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/person.entity.drift.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||
@@ -8,13 +7,6 @@ class DriftPeopleRepository extends DriftDatabaseRepository {
|
||||
final Drift _db;
|
||||
const DriftPeopleRepository(this._db) : super(_db);
|
||||
|
||||
Future<DriftPerson?> get(String personId) async {
|
||||
final query = _db.select(_db.personEntity)..where((row) => row.id.equals(personId));
|
||||
|
||||
final result = await query.getSingleOrNull();
|
||||
return result?.toDto();
|
||||
}
|
||||
|
||||
Future<List<DriftPerson>> getAssetPeople(String assetId) async {
|
||||
final query = _db.select(_db.assetFaceEntity).join([
|
||||
innerJoin(_db.personEntity, _db.personEntity.id.equalsExp(_db.assetFaceEntity.personId)),
|
||||
@@ -27,28 +19,19 @@ class DriftPeopleRepository extends DriftDatabaseRepository {
|
||||
}
|
||||
|
||||
Future<List<DriftPerson>> getAllPeople() async {
|
||||
final people = _db.personEntity;
|
||||
final faces = _db.assetFaceEntity;
|
||||
final assets = _db.remoteAssetEntity;
|
||||
|
||||
final query =
|
||||
_db.select(people).join([
|
||||
innerJoin(faces, faces.personId.equalsExp(people.id)),
|
||||
innerJoin(assets, assets.id.equalsExp(faces.assetId)),
|
||||
_db.select(_db.personEntity).join([
|
||||
leftOuterJoin(_db.assetFaceEntity, _db.assetFaceEntity.personId.equalsExp(_db.personEntity.id)),
|
||||
])
|
||||
..where(
|
||||
people.isHidden.equals(false) &
|
||||
assets.deletedAt.isNull() &
|
||||
assets.visibility.equalsValue(AssetVisibility.timeline),
|
||||
)
|
||||
..groupBy([people.id], having: faces.id.count().isBiggerOrEqualValue(3) | people.name.equals('').not())
|
||||
..where(_db.personEntity.isHidden.equals(false))
|
||||
..groupBy([_db.personEntity.id], having: _db.assetFaceEntity.id.count().isBiggerOrEqualValue(3))
|
||||
..orderBy([
|
||||
OrderingTerm(expression: people.name.equals('').not(), mode: OrderingMode.desc),
|
||||
OrderingTerm(expression: faces.id.count(), mode: OrderingMode.desc),
|
||||
OrderingTerm(expression: _db.personEntity.name.equals('').not(), mode: OrderingMode.desc),
|
||||
OrderingTerm(expression: _db.assetFaceEntity.id.count(), mode: OrderingMode.desc),
|
||||
]);
|
||||
|
||||
return query.map((row) {
|
||||
final person = row.readTable(people);
|
||||
final person = row.readTable(_db.personEntity);
|
||||
return person.toDto();
|
||||
}).get();
|
||||
}
|
||||
|
||||
@@ -35,7 +35,6 @@ class SearchApiRepository extends ApiRepository {
|
||||
isFavorite: filter.display.isFavorite ? true : null,
|
||||
isNotInAlbum: filter.display.isNotInAlbum ? true : null,
|
||||
personIds: filter.people.map((e) => e.id).toList(),
|
||||
tagIds: filter.tagIds,
|
||||
type: type,
|
||||
page: page,
|
||||
size: 100,
|
||||
@@ -60,7 +59,6 @@ class SearchApiRepository extends ApiRepository {
|
||||
isFavorite: filter.display.isFavorite ? true : null,
|
||||
isNotInAlbum: filter.display.isNotInAlbum ? true : null,
|
||||
personIds: filter.people.map((e) => e.id).toList(),
|
||||
tagIds: filter.tagIds,
|
||||
type: type,
|
||||
page: page,
|
||||
size: 1000,
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/api.repository.dart';
|
||||
import 'package:immich_mobile/providers/api.provider.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
final tagsApiRepositoryProvider = Provider<TagsApiRepository>(
|
||||
(ref) => TagsApiRepository(ref.read(apiServiceProvider).tagsApi),
|
||||
);
|
||||
|
||||
class TagsApiRepository extends ApiRepository {
|
||||
final TagsApi _api;
|
||||
const TagsApiRepository(this._api);
|
||||
|
||||
Future<List<TagResponseDto>?> getAllTags() async {
|
||||
return await _api.getAllTags();
|
||||
}
|
||||
}
|
||||
@@ -214,7 +214,6 @@ class SearchFilter {
|
||||
String? ocr;
|
||||
String? language;
|
||||
String? assetId;
|
||||
List<String>? tagIds;
|
||||
Set<PersonDto> people;
|
||||
SearchLocationFilter location;
|
||||
SearchCameraFilter camera;
|
||||
@@ -232,7 +231,6 @@ class SearchFilter {
|
||||
this.ocr,
|
||||
this.language,
|
||||
this.assetId,
|
||||
this.tagIds,
|
||||
required this.people,
|
||||
required this.location,
|
||||
required this.camera,
|
||||
@@ -248,7 +246,6 @@ class SearchFilter {
|
||||
(description == null || (description!.isEmpty)) &&
|
||||
(assetId == null || (assetId!.isEmpty)) &&
|
||||
(ocr == null || (ocr!.isEmpty)) &&
|
||||
(tagIds ?? []).isEmpty &&
|
||||
people.isEmpty &&
|
||||
location.country == null &&
|
||||
location.state == null &&
|
||||
@@ -272,7 +269,6 @@ class SearchFilter {
|
||||
String? ocr,
|
||||
String? assetId,
|
||||
Set<PersonDto>? people,
|
||||
List<String>? tagIds,
|
||||
SearchLocationFilter? location,
|
||||
SearchCameraFilter? camera,
|
||||
SearchDateFilter? date,
|
||||
@@ -294,13 +290,12 @@ class SearchFilter {
|
||||
display: display ?? this.display,
|
||||
rating: rating ?? this.rating,
|
||||
mediaType: mediaType ?? this.mediaType,
|
||||
tagIds: tagIds ?? this.tagIds,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'SearchFilter(context: $context, filename: $filename, description: $description, language: $language, ocr: $ocr, people: $people, location: $location, tagIds: $tagIds, camera: $camera, date: $date, display: $display, rating: $rating, mediaType: $mediaType, assetId: $assetId)';
|
||||
return 'SearchFilter(context: $context, filename: $filename, description: $description, language: $language, ocr: $ocr, people: $people, location: $location, camera: $camera, date: $date, display: $display, rating: $rating, mediaType: $mediaType, assetId: $assetId)';
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -314,7 +309,6 @@ class SearchFilter {
|
||||
other.ocr == ocr &&
|
||||
other.assetId == assetId &&
|
||||
other.people == people &&
|
||||
other.tagIds == tagIds &&
|
||||
other.location == location &&
|
||||
other.camera == camera &&
|
||||
other.date == date &&
|
||||
@@ -332,7 +326,6 @@ class SearchFilter {
|
||||
ocr.hashCode ^
|
||||
assetId.hashCode ^
|
||||
people.hashCode ^
|
||||
tagIds.hashCode ^
|
||||
location.hashCode ^
|
||||
camera.hashCode ^
|
||||
date.hashCode ^
|
||||
|
||||
@@ -6,7 +6,6 @@ class ServerFeatures {
|
||||
final bool oauthEnabled;
|
||||
final bool passwordLogin;
|
||||
final bool ocr;
|
||||
final bool smartSearch;
|
||||
|
||||
const ServerFeatures({
|
||||
required this.trash,
|
||||
@@ -14,30 +13,21 @@ class ServerFeatures {
|
||||
required this.oauthEnabled,
|
||||
required this.passwordLogin,
|
||||
this.ocr = false,
|
||||
this.smartSearch = false,
|
||||
});
|
||||
|
||||
ServerFeatures copyWith({
|
||||
bool? trash,
|
||||
bool? map,
|
||||
bool? oauthEnabled,
|
||||
bool? passwordLogin,
|
||||
bool? ocr,
|
||||
bool? smartSearch,
|
||||
}) {
|
||||
ServerFeatures copyWith({bool? trash, bool? map, bool? oauthEnabled, bool? passwordLogin, bool? ocr}) {
|
||||
return ServerFeatures(
|
||||
trash: trash ?? this.trash,
|
||||
map: map ?? this.map,
|
||||
oauthEnabled: oauthEnabled ?? this.oauthEnabled,
|
||||
passwordLogin: passwordLogin ?? this.passwordLogin,
|
||||
ocr: ocr ?? this.ocr,
|
||||
smartSearch: smartSearch ?? this.smartSearch,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ServerFeatures(trash: $trash, map: $map, oauthEnabled: $oauthEnabled, passwordLogin: $passwordLogin, ocr: $ocr, smartSearch: $smartSearch)';
|
||||
return 'ServerFeatures(trash: $trash, map: $map, oauthEnabled: $oauthEnabled, passwordLogin: $passwordLogin, ocr: $ocr)';
|
||||
}
|
||||
|
||||
ServerFeatures.fromDto(ServerFeaturesDto dto)
|
||||
@@ -45,8 +35,7 @@ class ServerFeatures {
|
||||
map = dto.map,
|
||||
oauthEnabled = dto.oauth,
|
||||
passwordLogin = dto.passwordLogin,
|
||||
ocr = dto.ocr,
|
||||
smartSearch = dto.smartSearch;
|
||||
ocr = dto.ocr;
|
||||
|
||||
@override
|
||||
bool operator ==(covariant ServerFeatures other) {
|
||||
@@ -56,17 +45,11 @@ class ServerFeatures {
|
||||
other.map == map &&
|
||||
other.oauthEnabled == oauthEnabled &&
|
||||
other.passwordLogin == passwordLogin &&
|
||||
other.ocr == ocr &&
|
||||
other.smartSearch == smartSearch;
|
||||
other.ocr == ocr;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return trash.hashCode ^
|
||||
map.hashCode ^
|
||||
oauthEnabled.hashCode ^
|
||||
passwordLogin.hashCode ^
|
||||
ocr.hashCode ^
|
||||
smartSearch.hashCode;
|
||||
return trash.hashCode ^ map.hashCode ^ oauthEnabled.hashCode ^ passwordLogin.hashCode ^ ocr.hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ class ServerInfo {
|
||||
|
||||
const ServerInfo({
|
||||
required this.serverVersion,
|
||||
this.latestVersion,
|
||||
required this.latestVersion,
|
||||
required this.serverFeatures,
|
||||
required this.serverConfig,
|
||||
required this.serverDiskInfo,
|
||||
|
||||
@@ -134,7 +134,7 @@ class AlbumOptionsPage extends HookConsumerWidget {
|
||||
itemBuilder: (context, index) {
|
||||
final user = sharedUsers.value[index];
|
||||
return ListTile(
|
||||
leading: UserCircleAvatar(user: user),
|
||||
leading: UserCircleAvatar(user: user, radius: 22),
|
||||
title: Text(user.name, style: const TextStyle(fontWeight: FontWeight.w500)),
|
||||
subtitle: Text(user.email, style: TextStyle(color: context.colorScheme.onSurfaceSecondary)),
|
||||
trailing: userId == user.id || isOwner ? const Icon(Icons.more_horiz_rounded) : const SizedBox(),
|
||||
|
||||
@@ -41,7 +41,7 @@ class AlbumSharedUserIcons extends HookConsumerWidget {
|
||||
itemBuilder: ((context, index) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(right: 8.0),
|
||||
child: UserCircleAvatar(user: sharedUsers.value[index], size: 36),
|
||||
child: UserCircleAvatar(user: sharedUsers.value[index], radius: 18, size: 36),
|
||||
);
|
||||
}),
|
||||
itemCount: sharedUsers.value.length,
|
||||
|
||||
@@ -221,37 +221,8 @@ class GalleryViewerPage extends HookConsumerWidget {
|
||||
onDragUpdate: (_, details, __) {
|
||||
handleSwipeUpDown(details);
|
||||
},
|
||||
onTapDown: (ctx, tapDownDetails, _) {
|
||||
final tapToNavigate = ref.read(appSettingsServiceProvider).getSetting<bool>(AppSettingsEnum.tapToNavigate);
|
||||
if (!tapToNavigate) {
|
||||
ref.read(showControlsProvider.notifier).toggle();
|
||||
return;
|
||||
}
|
||||
|
||||
double tapX = tapDownDetails.globalPosition.dx;
|
||||
double screenWidth = ctx.width;
|
||||
|
||||
// We want to change images if the user taps in the leftmost or
|
||||
// rightmost quarter of the screen
|
||||
bool tappedLeftSide = tapX < screenWidth / 4;
|
||||
bool tappedRightSide = tapX > screenWidth * (3 / 4);
|
||||
|
||||
int? currentPage = controller.page?.toInt();
|
||||
int maxPage = renderList.totalAssets - 1;
|
||||
|
||||
if (tappedLeftSide && currentPage != null) {
|
||||
// Nested if because we don't want to fallback to show/hide controls
|
||||
if (currentPage != 0) {
|
||||
controller.jumpToPage(currentPage - 1);
|
||||
}
|
||||
} else if (tappedRightSide && currentPage != null) {
|
||||
// Nested if because we don't want to fallback to show/hide controls
|
||||
if (currentPage != maxPage) {
|
||||
controller.jumpToPage(currentPage + 1);
|
||||
}
|
||||
} else {
|
||||
ref.read(showControlsProvider.notifier).toggle();
|
||||
}
|
||||
onTapDown: (_, __, ___) {
|
||||
ref.read(showControlsProvider.notifier).toggle();
|
||||
},
|
||||
onLongPressStart: asset.isMotionPhoto
|
||||
? (_, __, ___) {
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
@@ -10,7 +12,6 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/providers/album/album.provider.dart';
|
||||
import 'package:immich_mobile/repositories/file_media.repository.dart';
|
||||
import 'package:immich_mobile/routing/router.dart';
|
||||
import 'package:immich_mobile/utils/image_converter.dart';
|
||||
import 'package:immich_mobile/widgets/common/immich_toast.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
|
||||
@@ -29,10 +30,27 @@ class EditImagePage extends ConsumerWidget {
|
||||
final bool isEdited;
|
||||
|
||||
const EditImagePage({super.key, required this.asset, required this.image, required this.isEdited});
|
||||
Future<Uint8List> _imageToUint8List(Image image) async {
|
||||
final Completer<Uint8List> completer = Completer();
|
||||
image.image
|
||||
.resolve(const ImageConfiguration())
|
||||
.addListener(
|
||||
ImageStreamListener((ImageInfo info, bool _) {
|
||||
info.image.toByteData(format: ImageByteFormat.png).then((byteData) {
|
||||
if (byteData != null) {
|
||||
completer.complete(byteData.buffer.asUint8List());
|
||||
} else {
|
||||
completer.completeError('Failed to convert image to bytes');
|
||||
}
|
||||
});
|
||||
}, onError: (exception, stackTrace) => completer.completeError(exception)),
|
||||
);
|
||||
return completer.future;
|
||||
}
|
||||
|
||||
Future<void> _saveEditedImage(BuildContext context, Asset asset, Image image, WidgetRef ref) async {
|
||||
try {
|
||||
final Uint8List imageData = await imageToUint8List(image);
|
||||
final Uint8List imageData = await _imageToUint8List(image);
|
||||
await ref
|
||||
.read(fileMediaRepositoryProvider)
|
||||
.saveImage(imageData, title: "${p.withoutExtension(asset.fileName)}_edited.jpg");
|
||||
|
||||
@@ -118,7 +118,7 @@ class MapPage extends HookConsumerWidget {
|
||||
}
|
||||
|
||||
// finds the nearest asset marker from the tap point and store it as the selectedMarker
|
||||
Future<void> onMarkerClicked(Point<double> point, LatLng _) async {
|
||||
Future<void> onMarkerClicked(Point<double> point, LatLng coords) async {
|
||||
// Guard map not created
|
||||
if (mapController.value == null) {
|
||||
return;
|
||||
|
||||
@@ -28,7 +28,7 @@ class MapLocationPickerPage extends HookConsumerWidget {
|
||||
marker.value = await controller.value?.addMarkerAtLatLng(initialLatLng);
|
||||
}
|
||||
|
||||
Future<void> onMapClick(Point<num> _, LatLng centre) async {
|
||||
Future<void> onMapClick(Point<num> point, LatLng centre) async {
|
||||
selectedLatLng.value = centre;
|
||||
await controller.value?.animateCamera(CameraUpdate.newLatLng(centre));
|
||||
if (marker.value != null) {
|
||||
|
||||
100
mobile/lib/presentation/pages/dev/ui_showcase.page.dart
Normal file
100
mobile/lib/presentation/pages/dev/ui_showcase.page.dart
Normal file
@@ -0,0 +1,100 @@
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_ui/immich_ui.dart';
|
||||
|
||||
List<Widget> _showcaseBuilder(Function(ImmichVariant variant, ImmichColor color) builder) {
|
||||
final children = <Widget>[];
|
||||
|
||||
final items = [
|
||||
(variant: ImmichVariant.filled, title: "Filled Variant"),
|
||||
(variant: ImmichVariant.ghost, title: "Ghost Variant"),
|
||||
];
|
||||
|
||||
for (final (:variant, :title) in items) {
|
||||
children.add(Text(title));
|
||||
children.add(Row(spacing: 10, children: [for (var color in ImmichColor.values) builder(variant, color)]));
|
||||
}
|
||||
|
||||
return children;
|
||||
}
|
||||
|
||||
class _ComponentTitle extends StatelessWidget {
|
||||
final String title;
|
||||
|
||||
const _ComponentTitle(this.title);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Text(title, style: context.textTheme.titleLarge);
|
||||
}
|
||||
}
|
||||
|
||||
@RoutePage()
|
||||
class ImmichUIShowcasePage extends StatelessWidget {
|
||||
const ImmichUIShowcasePage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('Immich UI Showcase')),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
spacing: 10,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const _ComponentTitle("IconButton"),
|
||||
..._showcaseBuilder(
|
||||
(variant, color) =>
|
||||
ImmichIconButton(icon: Icons.favorite, color: color, variant: variant, onPressed: () {}),
|
||||
),
|
||||
const _ComponentTitle("CloseButton"),
|
||||
..._showcaseBuilder(
|
||||
(variant, color) => ImmichCloseButton(color: color, variant: variant, onPressed: () {}),
|
||||
),
|
||||
const _ComponentTitle("TextButton"),
|
||||
|
||||
ImmichTextButton(
|
||||
labelText: "Text Button",
|
||||
onPressed: () {},
|
||||
variant: ImmichVariant.filled,
|
||||
color: ImmichColor.primary,
|
||||
),
|
||||
ImmichTextButton(
|
||||
labelText: "Text Button",
|
||||
onPressed: () {},
|
||||
variant: ImmichVariant.filled,
|
||||
color: ImmichColor.primary,
|
||||
loading: true,
|
||||
),
|
||||
ImmichTextButton(
|
||||
labelText: "Text Button",
|
||||
onPressed: () {},
|
||||
variant: ImmichVariant.ghost,
|
||||
color: ImmichColor.primary,
|
||||
),
|
||||
ImmichTextButton(
|
||||
labelText: "Text Button",
|
||||
onPressed: () {},
|
||||
variant: ImmichVariant.ghost,
|
||||
color: ImmichColor.primary,
|
||||
loading: true,
|
||||
),
|
||||
const _ComponentTitle("Form"),
|
||||
ImmichForm(
|
||||
onSubmit: () {},
|
||||
child: const Column(
|
||||
spacing: 10,
|
||||
children: [ImmichTextInput(label: "Title", hintText: "Enter a title")],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -14,15 +14,13 @@ import 'package:immich_mobile/providers/infrastructure/current_album.provider.da
|
||||
@RoutePage()
|
||||
class DriftActivitiesPage extends HookConsumerWidget {
|
||||
final RemoteAlbum album;
|
||||
final String? assetId;
|
||||
final String? assetName;
|
||||
|
||||
const DriftActivitiesPage({super.key, required this.album, this.assetId, this.assetName});
|
||||
const DriftActivitiesPage({super.key, required this.album});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final activityNotifier = ref.read(albumActivityProvider(album.id, assetId).notifier);
|
||||
final activities = ref.watch(albumActivityProvider(album.id, assetId));
|
||||
final activityNotifier = ref.read(albumActivityProvider(album.id).notifier);
|
||||
final activities = ref.watch(albumActivityProvider(album.id));
|
||||
final listViewScrollController = useScrollController();
|
||||
|
||||
void scrollToBottom() {
|
||||
@@ -38,13 +36,7 @@ class DriftActivitiesPage extends HookConsumerWidget {
|
||||
overrides: [currentRemoteAlbumScopedProvider.overrideWithValue(album)],
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(album.name),
|
||||
if (assetName != null) Text(assetName!, style: context.textTheme.bodySmall),
|
||||
],
|
||||
),
|
||||
title: Text(album.name),
|
||||
actions: [const LikeActivityActionButton(iconOnly: true)],
|
||||
actionsPadding: const EdgeInsets.only(right: 8),
|
||||
),
|
||||
@@ -55,7 +47,7 @@ class DriftActivitiesPage extends HookConsumerWidget {
|
||||
activityWidgets.add(
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
|
||||
child: CommentBubble(activity: activity, isAssetActivity: assetId != null),
|
||||
child: CommentBubble(activity: activity),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user