mirror of
https://github.com/immich-app/immich.git
synced 2026-05-14 11:32:15 -04:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 865df448ef | |||
| b031548791 | |||
| fcea617313 | |||
| 024f20ea26 | |||
| 0a4ed6fd71 | |||
| b6e2ce1f35 | |||
| e323e778cd | |||
| 6a87797649 | |||
| f4a4649bbc |
@@ -17,6 +17,15 @@ on:
|
||||
description: 'Bump mobile build number'
|
||||
required: false
|
||||
type: boolean
|
||||
rc:
|
||||
description: 'Release candidate mode'
|
||||
required: false
|
||||
default: 'false'
|
||||
type: choice
|
||||
options:
|
||||
- 'false'
|
||||
- 'true'
|
||||
- 'finalize'
|
||||
skipTranslations:
|
||||
description: 'Skip translations'
|
||||
required: false
|
||||
@@ -74,7 +83,8 @@ jobs:
|
||||
env:
|
||||
SERVER_BUMP: ${{ inputs.serverBump }}
|
||||
MOBILE_BUMP: ${{ inputs.mobileBump }}
|
||||
run: misc/release/pump-version.sh -s "${SERVER_BUMP}" -m "${MOBILE_BUMP}"
|
||||
RC: ${{ inputs.rc }}
|
||||
run: misc/release/pump-version.sh -s "${SERVER_BUMP}" -m "${MOBILE_BUMP}" -r "${RC}"
|
||||
|
||||
- id: output
|
||||
run: echo "version=$IMMICH_VERSION" >> $GITHUB_OUTPUT
|
||||
@@ -108,7 +118,7 @@ jobs:
|
||||
|
||||
with:
|
||||
ref: ${{ needs.bump_version.outputs.ref }}
|
||||
environment: production
|
||||
environment: ${{ inputs.rc != 'false' && 'rc' || 'production' }}
|
||||
|
||||
prepare_release:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -140,6 +150,7 @@ jobs:
|
||||
uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2.6.2
|
||||
with:
|
||||
draft: true
|
||||
prerelease: ${{ contains(needs.bump_version.outputs.version, '-rc.') }}
|
||||
tag_name: ${{ needs.bump_version.outputs.version }}
|
||||
token: ${{ steps.generate-token.outputs.token }}
|
||||
generate_release_notes: true
|
||||
|
||||
+30
-30
@@ -2,37 +2,37 @@
|
||||
# Manual edits may be lost in future updates.
|
||||
|
||||
provider "registry.opentofu.org/cloudflare/cloudflare" {
|
||||
version = "4.52.5"
|
||||
constraints = "4.52.5"
|
||||
version = "4.52.7"
|
||||
constraints = "4.52.7"
|
||||
hashes = [
|
||||
"h1:+rfzF+16ZcWZWnTyW/p1HHTzYbPKX8Zt2nIFtR/+f+E=",
|
||||
"h1:18bXaaOSq8MWKuMxo/4y7EB7/i7G90y5QsKHZRmkoDo=",
|
||||
"h1:4vZVOpKeEQZsF2VrARRZFeL37Ed/gD4rRMtfnvWQres=",
|
||||
"h1:BZOsTF83QPKXTAaYqxPKzdl1KRjk/L2qbPpFjM0w28A=",
|
||||
"h1:CDuC+HXLvc1z6wkCRsSDcc/+QENIHEtssYshiWg3opA=",
|
||||
"h1:DE+YFzLnqSe79pI2R4idRGx5QzLdrA7RXvngTkGfZ30=",
|
||||
"h1:DfaJwH3Ml4yrRbdAY4AcDVy0QTQk5T3A622TXzS/u2E=",
|
||||
"h1:EIDXP0W3kgIv2pecrFmqtK/DnlqkyckzBzhxKaXU+4A=",
|
||||
"h1:EV4kYyaOnwGA0bh/3hU6Ezqnt1PFDxopH7i85e48IzY=",
|
||||
"h1:M0iXabfzamU+MPDi0G9XACpbacFKMakmM+Z9HZ8HrsM=",
|
||||
"h1:YWmCbGF/KbsrUzcYVBLscwLizidbp95TDQa0N2qpmVo=",
|
||||
"h1:cxPcCB5gbrpUO1+IXkQYs1YTY50/0IlApCzGea0cwuQ=",
|
||||
"h1:g6DldikTV2HXUu9uoeNY5FuLufgaYWF4ufgZg7wq62s=",
|
||||
"h1:oi/Hrx9pwoQ+Z52CBC+rrowVH387EIj0qvnxQgDeI+0=",
|
||||
"zh:1a3400cb38863b2585968d1876706bcfc67a148e1318a1d325c6c7704adc999b",
|
||||
"zh:4c5062cb9e9da1676f06ae92b8370186d98976cc4c7030d3cd76df12af54282a",
|
||||
"zh:52110f493b5f0587ef77a1cfd1a67001fd4c617b14c6502d732ab47352bdc2f7",
|
||||
"zh:5aa536f9eaeb43823aaf2aa80e7d39b25ef2b383405ed034aa16a28b446a9238",
|
||||
"zh:5cc39459a1c6be8a918f17054e4fbba573825ed5597dcada588fe99614d98a5b",
|
||||
"zh:629ae6a7ba298815131da826474d199312d21cec53a4d5ded4fa56a692e6f072",
|
||||
"zh:719cc7c75dc1d3eb30c22ff5102a017996d9788b948078c7e1c5b3446aeca661",
|
||||
"zh:8698635a3ca04383c1e93b21d6963346bdae54d27177a48e4b1435b7f731731c",
|
||||
"h1:+O72J3QYiZtYmYYZM/Eh0f4NNfl1BvjX1eju43qTQsQ=",
|
||||
"h1:0oqjYIPXcXh7XiDiKI085cHDYQQ5mh8kDl9dmBtvtog=",
|
||||
"h1:4b4ESb87MGv5bnadgYe7sK5rEkKMZhbkQcwPubQTsR4=",
|
||||
"h1:6mTr3eA1Ddb348lLmJuyvn98z4KF+ejqaUEJ76D1rzQ=",
|
||||
"h1:9/3YH+9k9HqsvFtbmBf7SO2+xqZeZrXNKzLkjNuhUEA=",
|
||||
"h1:Jcq4tBWgyH4/2JsojNBSRaN0mcItVMchO+lynonrlqc=",
|
||||
"h1:Y4Vv/2RdP0Q+uxqhOxzOdKxuuEMjXPDcU0vPc5bCQzI=",
|
||||
"h1:a0gW8FBKsbP9Fi0HEDoy49WIbEWVHk9+BR4/iwuBdDQ=",
|
||||
"h1:gElv6iqJtg8OKN77gbw+MjrkrQmJHPkkMEi1J+0xkpU=",
|
||||
"h1:oslXUugD/NQ+duJgT4BhKQyfGbuFOANknMvR73fiOeM=",
|
||||
"h1:pPItIWii5oymR+geZB219ROSPuSODPLTlM4S/u8xLvM=",
|
||||
"h1:u67GWw8GwD9NDlDzp9Y5VRnSQGcCrE8rSpkGPaBpDl0=",
|
||||
"h1:uUUa9dY0XQOycI8pxg16PFFtL0WCTi9uEJz8trTQ7pU=",
|
||||
"h1:y3rV8KF2q6GEMANNlf5EkKJurlfbKlIKpjGcdxoy7pQ=",
|
||||
"zh:0c904ce31a4c6c4a5b3bf7ff1560e77c0cc7e2450c8553ded8e8c90398e1418b",
|
||||
"zh:36183d310c36373fe4cb936b83c595c6fd3b0a94bc7827f28e5789ccbf59752e",
|
||||
"zh:556a568a6f0235e8f41647de9e4d3a1e7b1d6502df8b19b54ec441f1c653ea10",
|
||||
"zh:633ebbd5b0245e75e500ef9be4d9e62288f97e8da3baaa51323892a786d90285",
|
||||
"zh:6acfe60cf52a65ba8f044f748548d2119e7f4fd7f8ebcb14698960d87c68f529",
|
||||
"zh:890df766e9b839623b1f0437355032a3c006226a6c200cd911e15ee1a9014e9f",
|
||||
"zh:8a9993f1dcadf1dd6ca43b23348abe374605d29945a2fafc07fb3457644e6a54",
|
||||
"zh:b1b9a1e6bcc24d5863a664a411d2dc906373ae7a2399d2d65548ce7377057852",
|
||||
"zh:b270184cdeec277218e84b94cb136fead753da717f9b9dc378e51907f3f00bb0",
|
||||
"zh:dff2bc10071210181726ce270f954995fe42c696e61e2e8f874021fed02521e5",
|
||||
"zh:e8e87b40b6a87dc097b0fdc20d3f725cec0d82abc9cc3755c1f89f8f6e8b0036",
|
||||
"zh:ee964a6573d399a5dd22ce328fb38ca1207797a02248f14b2e4913ee390e7803",
|
||||
"zh:904acc31ebb9d6ef68c792074b30532ee61bf515f19e0a3c75b46f126cca1f13",
|
||||
"zh:a1d0a81246afc8750286d3f6fe7a8fbe6460dd2662407b28dbfbabb612e5fa9d",
|
||||
"zh:a41a36fe253fc365fe2b7ffc749624688b2693b4634862fda161179ab100029f",
|
||||
"zh:a7ef269e77ffa8715c8945a2c14322c7ff159ea44c15f62505f3cbb2cae3b32d",
|
||||
"zh:b01aa3bed30610633b762df64332b26f8844a68c3960cebcb30f04918efc67fe",
|
||||
"zh:b069cc2cd18cae10757df3ae030508eac8d55de7e49eda7a5e3e11f2f7fe6455",
|
||||
"zh:b2d2c6313729ebb7465dceece374049e2d08bda34473901be9ff46a8836d42b2",
|
||||
"zh:db0e114edaf4bc2f3d4769958807c83022bfbc619a00bdf4c4bd17faa4ab2d8b",
|
||||
"zh:ecc0aa8b9044f664fd2aaf8fa992d976578f78478980555b4b8f6148e8d1a5fe",
|
||||
]
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ terraform {
|
||||
required_providers {
|
||||
cloudflare = {
|
||||
source = "cloudflare/cloudflare"
|
||||
version = "4.52.5"
|
||||
version = "4.52.7"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+30
-30
@@ -2,37 +2,37 @@
|
||||
# Manual edits may be lost in future updates.
|
||||
|
||||
provider "registry.opentofu.org/cloudflare/cloudflare" {
|
||||
version = "4.52.5"
|
||||
constraints = "4.52.5"
|
||||
version = "4.52.7"
|
||||
constraints = "4.52.7"
|
||||
hashes = [
|
||||
"h1:+rfzF+16ZcWZWnTyW/p1HHTzYbPKX8Zt2nIFtR/+f+E=",
|
||||
"h1:18bXaaOSq8MWKuMxo/4y7EB7/i7G90y5QsKHZRmkoDo=",
|
||||
"h1:4vZVOpKeEQZsF2VrARRZFeL37Ed/gD4rRMtfnvWQres=",
|
||||
"h1:BZOsTF83QPKXTAaYqxPKzdl1KRjk/L2qbPpFjM0w28A=",
|
||||
"h1:CDuC+HXLvc1z6wkCRsSDcc/+QENIHEtssYshiWg3opA=",
|
||||
"h1:DE+YFzLnqSe79pI2R4idRGx5QzLdrA7RXvngTkGfZ30=",
|
||||
"h1:DfaJwH3Ml4yrRbdAY4AcDVy0QTQk5T3A622TXzS/u2E=",
|
||||
"h1:EIDXP0W3kgIv2pecrFmqtK/DnlqkyckzBzhxKaXU+4A=",
|
||||
"h1:EV4kYyaOnwGA0bh/3hU6Ezqnt1PFDxopH7i85e48IzY=",
|
||||
"h1:M0iXabfzamU+MPDi0G9XACpbacFKMakmM+Z9HZ8HrsM=",
|
||||
"h1:YWmCbGF/KbsrUzcYVBLscwLizidbp95TDQa0N2qpmVo=",
|
||||
"h1:cxPcCB5gbrpUO1+IXkQYs1YTY50/0IlApCzGea0cwuQ=",
|
||||
"h1:g6DldikTV2HXUu9uoeNY5FuLufgaYWF4ufgZg7wq62s=",
|
||||
"h1:oi/Hrx9pwoQ+Z52CBC+rrowVH387EIj0qvnxQgDeI+0=",
|
||||
"zh:1a3400cb38863b2585968d1876706bcfc67a148e1318a1d325c6c7704adc999b",
|
||||
"zh:4c5062cb9e9da1676f06ae92b8370186d98976cc4c7030d3cd76df12af54282a",
|
||||
"zh:52110f493b5f0587ef77a1cfd1a67001fd4c617b14c6502d732ab47352bdc2f7",
|
||||
"zh:5aa536f9eaeb43823aaf2aa80e7d39b25ef2b383405ed034aa16a28b446a9238",
|
||||
"zh:5cc39459a1c6be8a918f17054e4fbba573825ed5597dcada588fe99614d98a5b",
|
||||
"zh:629ae6a7ba298815131da826474d199312d21cec53a4d5ded4fa56a692e6f072",
|
||||
"zh:719cc7c75dc1d3eb30c22ff5102a017996d9788b948078c7e1c5b3446aeca661",
|
||||
"zh:8698635a3ca04383c1e93b21d6963346bdae54d27177a48e4b1435b7f731731c",
|
||||
"h1:+O72J3QYiZtYmYYZM/Eh0f4NNfl1BvjX1eju43qTQsQ=",
|
||||
"h1:0oqjYIPXcXh7XiDiKI085cHDYQQ5mh8kDl9dmBtvtog=",
|
||||
"h1:4b4ESb87MGv5bnadgYe7sK5rEkKMZhbkQcwPubQTsR4=",
|
||||
"h1:6mTr3eA1Ddb348lLmJuyvn98z4KF+ejqaUEJ76D1rzQ=",
|
||||
"h1:9/3YH+9k9HqsvFtbmBf7SO2+xqZeZrXNKzLkjNuhUEA=",
|
||||
"h1:Jcq4tBWgyH4/2JsojNBSRaN0mcItVMchO+lynonrlqc=",
|
||||
"h1:Y4Vv/2RdP0Q+uxqhOxzOdKxuuEMjXPDcU0vPc5bCQzI=",
|
||||
"h1:a0gW8FBKsbP9Fi0HEDoy49WIbEWVHk9+BR4/iwuBdDQ=",
|
||||
"h1:gElv6iqJtg8OKN77gbw+MjrkrQmJHPkkMEi1J+0xkpU=",
|
||||
"h1:oslXUugD/NQ+duJgT4BhKQyfGbuFOANknMvR73fiOeM=",
|
||||
"h1:pPItIWii5oymR+geZB219ROSPuSODPLTlM4S/u8xLvM=",
|
||||
"h1:u67GWw8GwD9NDlDzp9Y5VRnSQGcCrE8rSpkGPaBpDl0=",
|
||||
"h1:uUUa9dY0XQOycI8pxg16PFFtL0WCTi9uEJz8trTQ7pU=",
|
||||
"h1:y3rV8KF2q6GEMANNlf5EkKJurlfbKlIKpjGcdxoy7pQ=",
|
||||
"zh:0c904ce31a4c6c4a5b3bf7ff1560e77c0cc7e2450c8553ded8e8c90398e1418b",
|
||||
"zh:36183d310c36373fe4cb936b83c595c6fd3b0a94bc7827f28e5789ccbf59752e",
|
||||
"zh:556a568a6f0235e8f41647de9e4d3a1e7b1d6502df8b19b54ec441f1c653ea10",
|
||||
"zh:633ebbd5b0245e75e500ef9be4d9e62288f97e8da3baaa51323892a786d90285",
|
||||
"zh:6acfe60cf52a65ba8f044f748548d2119e7f4fd7f8ebcb14698960d87c68f529",
|
||||
"zh:890df766e9b839623b1f0437355032a3c006226a6c200cd911e15ee1a9014e9f",
|
||||
"zh:8a9993f1dcadf1dd6ca43b23348abe374605d29945a2fafc07fb3457644e6a54",
|
||||
"zh:b1b9a1e6bcc24d5863a664a411d2dc906373ae7a2399d2d65548ce7377057852",
|
||||
"zh:b270184cdeec277218e84b94cb136fead753da717f9b9dc378e51907f3f00bb0",
|
||||
"zh:dff2bc10071210181726ce270f954995fe42c696e61e2e8f874021fed02521e5",
|
||||
"zh:e8e87b40b6a87dc097b0fdc20d3f725cec0d82abc9cc3755c1f89f8f6e8b0036",
|
||||
"zh:ee964a6573d399a5dd22ce328fb38ca1207797a02248f14b2e4913ee390e7803",
|
||||
"zh:904acc31ebb9d6ef68c792074b30532ee61bf515f19e0a3c75b46f126cca1f13",
|
||||
"zh:a1d0a81246afc8750286d3f6fe7a8fbe6460dd2662407b28dbfbabb612e5fa9d",
|
||||
"zh:a41a36fe253fc365fe2b7ffc749624688b2693b4634862fda161179ab100029f",
|
||||
"zh:a7ef269e77ffa8715c8945a2c14322c7ff159ea44c15f62505f3cbb2cae3b32d",
|
||||
"zh:b01aa3bed30610633b762df64332b26f8844a68c3960cebcb30f04918efc67fe",
|
||||
"zh:b069cc2cd18cae10757df3ae030508eac8d55de7e49eda7a5e3e11f2f7fe6455",
|
||||
"zh:b2d2c6313729ebb7465dceece374049e2d08bda34473901be9ff46a8836d42b2",
|
||||
"zh:db0e114edaf4bc2f3d4769958807c83022bfbc619a00bdf4c4bd17faa4ab2d8b",
|
||||
"zh:ecc0aa8b9044f664fd2aaf8fa992d976578f78478980555b4b8f6148e8d1a5fe",
|
||||
]
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ terraform {
|
||||
required_providers {
|
||||
cloudflare = {
|
||||
source = "cloudflare/cloudflare"
|
||||
version = "4.52.5"
|
||||
version = "4.52.7"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,8 +13,11 @@ The `immich-server` docker image comes preinstalled with an administrative CLI (
|
||||
| `enable-oauth-login` | Enable OAuth login |
|
||||
| `disable-oauth-login` | Disable OAuth login |
|
||||
| `list-users` | List Immich users |
|
||||
| `grant-admin` | Grant admin privileges to a user (by email) |
|
||||
| `revoke-admin` | Revoke admin privileges from a user (by email) |
|
||||
| `version` | Print Immich version |
|
||||
| `change-media-location` | Change database file paths to align with a new media location |
|
||||
| `schema-check` | Verify database migrations and check for schema drift |
|
||||
|
||||
## How to run a command
|
||||
|
||||
@@ -102,6 +105,22 @@ immich-admin list-users
|
||||
]
|
||||
```
|
||||
|
||||
Grant Admin
|
||||
|
||||
```
|
||||
immich-admin grant-admin
|
||||
? Please enter the user email: user@example.com
|
||||
Admin access has been granted to user@example.com
|
||||
```
|
||||
|
||||
Revoke Admin
|
||||
|
||||
```
|
||||
immich-admin revoke-admin
|
||||
? Please enter the user email: user@example.com
|
||||
Admin access has been revoked from user@example.com
|
||||
```
|
||||
|
||||
Print Immich Version
|
||||
|
||||
```
|
||||
@@ -126,3 +145,12 @@ immich-admin change-media-location
|
||||
Database file paths updated successfully! 🎉
|
||||
...
|
||||
```
|
||||
|
||||
Schema Check
|
||||
|
||||
```
|
||||
immich-admin schema-check
|
||||
Migrations are up to date
|
||||
|
||||
No schema drift detected
|
||||
```
|
||||
|
||||
+1
-4
@@ -885,15 +885,13 @@
|
||||
"cutoff_date_description": "Keep photos from the last…",
|
||||
"cutoff_day": "{count, plural, one {day} other {days}}",
|
||||
"cutoff_year": "{count, plural, one {year} other {years}}",
|
||||
"daily_title_text_date": "E, MMM dd",
|
||||
"daily_title_text_date_year": "E, MMM dd, yyyy",
|
||||
"dark": "Dark",
|
||||
"dark_theme": "Switch to dark theme",
|
||||
"date": "Date",
|
||||
"date_after": "Date after",
|
||||
"date_and_time": "Date and Time",
|
||||
"date_before": "Date before",
|
||||
"date_format": "E, LLL d, y • h:mm a",
|
||||
"date_of_birth": "Date of birth",
|
||||
"date_of_birth_saved": "Date of birth saved successfully",
|
||||
"date_range": "Date range",
|
||||
"day": "Day",
|
||||
@@ -1583,7 +1581,6 @@
|
||||
"mobile_app_download_onboarding_note": "Download the companion mobile app using the following options",
|
||||
"model": "Model",
|
||||
"month": "Month",
|
||||
"monthly_title_text_date_format": "MMMM y",
|
||||
"more": "More",
|
||||
"motion": "Motion",
|
||||
"move": "Move",
|
||||
|
||||
@@ -3,21 +3,26 @@
|
||||
#
|
||||
# Pump one or both of the server/mobile versions in appropriate files
|
||||
#
|
||||
# usage: './scripts/pump-version.sh -s <major|minor|patch> <-m> <true|false>
|
||||
# usage: './scripts/pump-version.sh -s <major|minor|patch> <-m> <true|false> <-r> <true|false|finalize>
|
||||
#
|
||||
# examples:
|
||||
# ./scripts/pump-version.sh -s major # 1.0.0+50 => 2.0.0+50
|
||||
# ./scripts/pump-version.sh -s minor -m true # 1.0.0+50 => 1.1.0+51
|
||||
# ./scripts/pump-version.sh -m true # 1.0.0+50 => 1.0.0+51
|
||||
# ./scripts/pump-version.sh -s minor -m true -r true # 3.0.0 => 3.1.0-rc.1 (start RC)
|
||||
# ./scripts/pump-version.sh -m true -r true # 3.1.0-rc.1 => 3.1.0-rc.2 (iterate RC)
|
||||
# ./scripts/pump-version.sh -m true -r finalize # 3.1.0-rc.2 => 3.1.0 (finalize RC)
|
||||
#
|
||||
|
||||
SERVER_PUMP="false"
|
||||
MOBILE_PUMP="false"
|
||||
RC="false"
|
||||
|
||||
while getopts 's:m:' flag; do
|
||||
while getopts 's:m:r:' flag; do
|
||||
case "${flag}" in
|
||||
s) SERVER_PUMP=${OPTARG} ;;
|
||||
m) MOBILE_PUMP=${OPTARG} ;;
|
||||
r) RC=${OPTARG} ;;
|
||||
*)
|
||||
echo "Invalid args"
|
||||
exit 1
|
||||
@@ -25,10 +30,51 @@ while getopts 's:m:' flag; do
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ "$RC" != "true" && "$RC" != "false" && "$RC" != "finalize" ]]; then
|
||||
echo "Expected <true|false|finalize> for the -r argument"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
CURRENT_SERVER=$(jq -r '.version' server/package.json)
|
||||
MAJOR=$(echo "$CURRENT_SERVER" | cut -d '.' -f1)
|
||||
MINOR=$(echo "$CURRENT_SERVER" | cut -d '.' -f2)
|
||||
PATCH=$(echo "$CURRENT_SERVER" | cut -d '.' -f3)
|
||||
|
||||
if [[ "$CURRENT_SERVER" == *-rc.* ]]; then
|
||||
CURRENT_BASE="${CURRENT_SERVER%-rc.*}"
|
||||
CURRENT_RC_NUM="${CURRENT_SERVER##*-rc.}"
|
||||
else
|
||||
CURRENT_BASE="$CURRENT_SERVER"
|
||||
CURRENT_RC_NUM=""
|
||||
fi
|
||||
|
||||
# Validate RC/server-bump combinations against current version state
|
||||
if [[ -n "$CURRENT_RC_NUM" ]]; then
|
||||
# Currently on an RC
|
||||
if [[ "$RC" == "false" ]]; then
|
||||
echo "Current version $CURRENT_SERVER is a release candidate. Pass -r true to iterate the RC or -r finalize to finalize the release."
|
||||
exit 1
|
||||
fi
|
||||
if [[ "$RC" == "true" && "$SERVER_PUMP" != "false" ]]; then
|
||||
echo "Cannot start a new RC while still on an RC; finalize first."
|
||||
exit 1
|
||||
fi
|
||||
if [[ "$RC" == "finalize" && "$SERVER_PUMP" != "false" ]]; then
|
||||
echo "Finalize takes no server bump."
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
# Not currently on an RC
|
||||
if [[ "$RC" == "true" && "$SERVER_PUMP" == "false" ]]; then
|
||||
echo "Starting an RC requires a server bump."
|
||||
exit 1
|
||||
fi
|
||||
if [[ "$RC" == "finalize" ]]; then
|
||||
echo "Nothing to finalize."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
MAJOR=$(echo "$CURRENT_BASE" | cut -d '.' -f1)
|
||||
MINOR=$(echo "$CURRENT_BASE" | cut -d '.' -f2)
|
||||
PATCH=$(echo "$CURRENT_BASE" | cut -d '.' -f3)
|
||||
|
||||
if [[ $SERVER_PUMP == "major" ]]; then
|
||||
MAJOR=$((MAJOR + 1))
|
||||
@@ -46,7 +92,22 @@ else
|
||||
exit 1
|
||||
fi
|
||||
|
||||
NEXT_SERVER=$MAJOR.$MINOR.$PATCH
|
||||
NEXT_BASE=$MAJOR.$MINOR.$PATCH
|
||||
|
||||
if [[ "$RC" == "true" ]]; then
|
||||
if [[ -n "$CURRENT_RC_NUM" ]]; then
|
||||
# Iterate existing RC
|
||||
NEXT_RC_NUM=$((CURRENT_RC_NUM + 1))
|
||||
NEXT_SERVER="${NEXT_BASE}-rc.${NEXT_RC_NUM}"
|
||||
else
|
||||
# Start new RC after server bump
|
||||
NEXT_SERVER="${NEXT_BASE}-rc.1"
|
||||
fi
|
||||
elif [[ "$RC" == "finalize" ]]; then
|
||||
NEXT_SERVER="$NEXT_BASE"
|
||||
else
|
||||
NEXT_SERVER="$NEXT_BASE"
|
||||
fi
|
||||
|
||||
CURRENT_MOBILE=$(grep "^version: .*+[0-9]\+$" mobile/pubspec.yaml | cut -d "+" -f2)
|
||||
NEXT_MOBILE=$CURRENT_MOBILE
|
||||
@@ -72,9 +133,12 @@ if [ "$CURRENT_SERVER" != "$NEXT_SERVER" ]; then
|
||||
# copy version to open-api spec
|
||||
mise run //:open-api
|
||||
|
||||
uv version --directory machine-learning "$NEXT_SERVER"
|
||||
NEXT_PY="${NEXT_SERVER//-rc./rc}"
|
||||
uv version --directory machine-learning "$NEXT_PY"
|
||||
|
||||
./misc/release/archive-version.js "$NEXT_SERVER"
|
||||
if [[ "$NEXT_SERVER" != *-rc.* ]]; then
|
||||
./misc/release/archive-version.js "$NEXT_SERVER"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$CURRENT_MOBILE" != "$NEXT_MOBILE" ]; then
|
||||
@@ -84,7 +148,9 @@ fi
|
||||
sed -i "s/\"android\.injected\.version\.name\" => \"$CURRENT_SERVER\",/\"android\.injected\.version\.name\" => \"$NEXT_SERVER\",/" mobile/android/fastlane/Fastfile
|
||||
sed -i "s/\"android\.injected\.version\.code\" => $CURRENT_MOBILE,/\"android\.injected\.version\.code\" => $NEXT_MOBILE,/" mobile/android/fastlane/Fastfile
|
||||
sed -i "s/^version: $CURRENT_SERVER+$CURRENT_MOBILE$/version: $NEXT_SERVER+$NEXT_MOBILE/" mobile/pubspec.yaml
|
||||
perl -i -p0e "s/(<key>CFBundleShortVersionString<\/key>\s*<string>)$CURRENT_SERVER(<\/string>)/\${1}$NEXT_SERVER\${2}/s" mobile/ios/Runner/Info.plist
|
||||
# iOS marketing version cannot contain a pre-release suffix; strip -rc.N before writing the plist.
|
||||
IOS_NEXT="${NEXT_SERVER%-rc.*}"
|
||||
perl -i -p0e "s/(<key>CFBundleShortVersionString<\/key>\s*<string>)$CURRENT_SERVER(<\/string>)/\${1}$IOS_NEXT\${2}/s" mobile/ios/Runner/Info.plist
|
||||
|
||||
|
||||
echo "IMMICH_VERSION=v$NEXT_SERVER" >>"$GITHUB_ENV"
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:immich_mobile/domain/models/config/image_config.dart';
|
||||
import 'package:immich_mobile/domain/models/config/map_config.dart';
|
||||
import 'package:immich_mobile/domain/models/config/theme_config.dart';
|
||||
import 'package:immich_mobile/domain/models/config/timeline_config.dart';
|
||||
import 'package:immich_mobile/domain/models/config/viewer_config.dart';
|
||||
|
||||
class AppConfig {
|
||||
final ThemeConfig theme;
|
||||
@@ -10,6 +11,7 @@ class AppConfig {
|
||||
final MapConfig map;
|
||||
final TimelineConfig timeline;
|
||||
final ImageConfig image;
|
||||
final ViewerConfig viewer;
|
||||
|
||||
const AppConfig({
|
||||
this.theme = const .new(),
|
||||
@@ -17,6 +19,7 @@ class AppConfig {
|
||||
this.map = const .new(),
|
||||
this.timeline = const .new(),
|
||||
this.image = const .new(),
|
||||
this.viewer = const .new(),
|
||||
});
|
||||
|
||||
AppConfig copyWith({
|
||||
@@ -25,12 +28,14 @@ class AppConfig {
|
||||
MapConfig? map,
|
||||
TimelineConfig? timeline,
|
||||
ImageConfig? image,
|
||||
ViewerConfig? viewer,
|
||||
}) => .new(
|
||||
theme: theme ?? this.theme,
|
||||
cleanup: cleanup ?? this.cleanup,
|
||||
map: map ?? this.map,
|
||||
timeline: timeline ?? this.timeline,
|
||||
image: image ?? this.image,
|
||||
viewer: viewer ?? this.viewer,
|
||||
);
|
||||
|
||||
@override
|
||||
@@ -41,11 +46,13 @@ class AppConfig {
|
||||
other.cleanup == cleanup &&
|
||||
other.map == map &&
|
||||
other.timeline == timeline &&
|
||||
other.image == image);
|
||||
other.image == image &&
|
||||
other.viewer == viewer);
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(theme, cleanup, map, timeline, image);
|
||||
int get hashCode => Object.hash(theme, cleanup, map, timeline, image, viewer);
|
||||
|
||||
@override
|
||||
String toString() => 'AppConfig(theme: $theme, cleanup: $cleanup, map: $map, timeline: $timeline, image: $image)';
|
||||
String toString() =>
|
||||
'AppConfig(theme: $theme, cleanup: $cleanup, map: $map, timeline: $timeline, image: $image, viewer: $viewer)';
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
class ViewerConfig {
|
||||
final bool loopVideo;
|
||||
final bool loadOriginalVideo;
|
||||
final bool autoPlayVideo;
|
||||
final bool tapToNavigate;
|
||||
|
||||
const ViewerConfig({
|
||||
this.loopVideo = true,
|
||||
this.loadOriginalVideo = false,
|
||||
this.autoPlayVideo = true,
|
||||
this.tapToNavigate = false,
|
||||
});
|
||||
|
||||
ViewerConfig copyWith({bool? loopVideo, bool? loadOriginalVideo, bool? autoPlayVideo, bool? tapToNavigate}) =>
|
||||
ViewerConfig(
|
||||
loopVideo: loopVideo ?? this.loopVideo,
|
||||
loadOriginalVideo: loadOriginalVideo ?? this.loadOriginalVideo,
|
||||
autoPlayVideo: autoPlayVideo ?? this.autoPlayVideo,
|
||||
tapToNavigate: tapToNavigate ?? this.tapToNavigate,
|
||||
);
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
(other is ViewerConfig &&
|
||||
other.loopVideo == loopVideo &&
|
||||
other.loadOriginalVideo == loadOriginalVideo &&
|
||||
other.autoPlayVideo == autoPlayVideo &&
|
||||
other.tapToNavigate == tapToNavigate);
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(loopVideo, loadOriginalVideo, autoPlayVideo, tapToNavigate);
|
||||
|
||||
@override
|
||||
String toString() =>
|
||||
'ViewerConfig(loopVideo: $loopVideo, loadOriginalVideo: $loadOriginalVideo, autoPlayVideo: $autoPlayVideo, tapToNavigate: $tapToNavigate)';
|
||||
}
|
||||
@@ -28,6 +28,12 @@ enum MetadataKey<T extends Object> {
|
||||
imagePreferRemote<bool>(.appConfig, 'image.preferRemote', false),
|
||||
imageLoadOriginal<bool>(.appConfig, 'image.loadOriginal', false),
|
||||
|
||||
// Viewer
|
||||
viewerLoopVideo<bool>(.appConfig, 'viewer.loopVideo', true),
|
||||
viewerLoadOriginalVideo<bool>(.appConfig, 'viewer.loadOriginalVideo', false),
|
||||
viewerAutoPlayVideo<bool>(.appConfig, 'viewer.autoPlayVideo', true),
|
||||
viewerTapToNavigate<bool>(.appConfig, 'viewer.tapToNavigate', false),
|
||||
|
||||
// Timeline
|
||||
timelineTilesPerRow<int>(.appConfig, 'timeline.tilesPerRow', 4),
|
||||
timelineGroupAssetsBy<GroupAssetsBy>(
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||
|
||||
enum Setting<T> {
|
||||
loadOriginalVideo<bool>(StoreKey.loadOriginalVideo, false),
|
||||
autoPlayVideo<bool>(StoreKey.autoPlayVideo, true),
|
||||
advancedTroubleshooting<bool>(StoreKey.advancedTroubleshooting, false),
|
||||
enableBackup<bool>(StoreKey.enableBackup, false);
|
||||
|
||||
|
||||
@@ -40,12 +40,6 @@ enum StoreKey<T> {
|
||||
albumGridView<bool>._(140),
|
||||
loadOriginal<bool>._(101),
|
||||
|
||||
// Image viewer navigation settings
|
||||
loopVideo<bool>._(117),
|
||||
loadOriginalVideo<bool>._(136),
|
||||
autoPlayVideo<bool>._(139),
|
||||
tapToNavigate<bool>._(141),
|
||||
|
||||
// Experimental stuff
|
||||
enableBackup<bool>._(1003),
|
||||
useWifiForUploadVideos<bool>._(1004),
|
||||
@@ -53,6 +47,10 @@ enum StoreKey<T> {
|
||||
syncMigrationStatus<String>._(1013),
|
||||
|
||||
// Legacy keys that have been migrated to the new metadata store
|
||||
legacyLoopVideo<bool>._(117),
|
||||
legacyLoadOriginalVideo<bool>._(136),
|
||||
legacyAutoPlayVideo<bool>._(139),
|
||||
legacyTapToNavigate<bool>._(141),
|
||||
legacyPreferRemoteImage<bool>._(116),
|
||||
legacyLoadOriginal<bool>._(101),
|
||||
legacyPrimaryColor<String>._(128),
|
||||
|
||||
@@ -133,6 +133,12 @@ extension<T extends Object> on MetadataDomain<T> {
|
||||
storageIndicator: repo._read(.timelineStorageIndicator),
|
||||
),
|
||||
image: .new(preferRemote: repo._read(.imagePreferRemote), loadOriginal: repo._read(.imageLoadOriginal)),
|
||||
viewer: .new(
|
||||
loopVideo: repo._read(.viewerLoopVideo),
|
||||
loadOriginalVideo: repo._read(.viewerLoadOriginalVideo),
|
||||
autoPlayVideo: repo._read(.viewerAutoPlayVideo),
|
||||
tapToNavigate: repo._read(.viewerTapToNavigate),
|
||||
),
|
||||
);
|
||||
case .systemConfig:
|
||||
repo._systemConfig = .new(logLevel: repo._read(.logLevel));
|
||||
|
||||
@@ -17,11 +17,10 @@ import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_stack.widg
|
||||
import 'package:immich_mobile/presentation/widgets/asset_viewer/video_viewer.widget.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/images/image_provider.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart';
|
||||
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
||||
import 'package:immich_mobile/providers/asset_viewer/asset_viewer.provider.dart';
|
||||
import 'package:immich_mobile/providers/asset_viewer/is_motion_video_playing.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
|
||||
import 'package:immich_mobile/services/app_settings.service.dart';
|
||||
import 'package:immich_mobile/widgets/common/immich_loading_indicator.dart';
|
||||
import 'package:immich_mobile/widgets/photo_view/photo_view.dart';
|
||||
|
||||
@@ -231,7 +230,7 @@ class _AssetPageState extends ConsumerState<AssetPage> {
|
||||
return;
|
||||
}
|
||||
|
||||
final tapToNavigate = ref.read(appSettingsServiceProvider).getSetting<bool>(AppSettingsEnum.tapToNavigate);
|
||||
final tapToNavigate = ref.read(metadataProvider).appConfig.viewer.tapToNavigate;
|
||||
if (!tapToNavigate) {
|
||||
_viewer.toggleControls();
|
||||
return;
|
||||
|
||||
@@ -3,21 +3,17 @@ import 'dart:async';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||
import 'package:immich_mobile/domain/models/setting.model.dart';
|
||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||
import 'package:immich_mobile/domain/services/setting.service.dart';
|
||||
import 'package:immich_mobile/entities/store.entity.dart';
|
||||
import 'package:immich_mobile/extensions/platform_extensions.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart';
|
||||
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
||||
import 'package:immich_mobile/providers/asset_viewer/asset_viewer.provider.dart';
|
||||
import 'package:immich_mobile/providers/asset_viewer/is_motion_video_playing.provider.dart';
|
||||
import 'package:immich_mobile/providers/asset_viewer/video_player_provider.dart';
|
||||
import 'package:immich_mobile/providers/cast.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/asset.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/setting.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
|
||||
import 'package:immich_mobile/services/api.service.dart';
|
||||
import 'package:immich_mobile/services/app_settings.service.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:native_video_player/native_video_player.dart';
|
||||
|
||||
@@ -132,7 +128,7 @@ class _NativeVideoViewerState extends ConsumerState<NativeVideoViewer> with Widg
|
||||
final remoteId = (videoAsset as RemoteAsset).id;
|
||||
|
||||
final serverEndpoint = Store.get(StoreKey.serverEndpoint);
|
||||
final isOriginalVideo = ref.read(settingsProvider).get<bool>(Setting.loadOriginalVideo);
|
||||
final isOriginalVideo = ref.read(metadataProvider).appConfig.viewer.loadOriginalVideo;
|
||||
final String postfixUrl = isOriginalVideo ? 'original' : 'video/playback';
|
||||
final String videoUrl = videoAsset.livePhotoVideoId != null
|
||||
? '$serverEndpoint/assets/${videoAsset.livePhotoVideoId}/$postfixUrl'
|
||||
@@ -165,7 +161,7 @@ class _NativeVideoViewerState extends ConsumerState<NativeVideoViewer> with Widg
|
||||
return;
|
||||
}
|
||||
|
||||
final autoPlayVideo = AppSetting.get(Setting.autoPlayVideo);
|
||||
final autoPlayVideo = ref.read(metadataProvider).appConfig.viewer.autoPlayVideo;
|
||||
if (autoPlayVideo || widget.asset.isMotionPhoto) {
|
||||
await _notifier.play();
|
||||
}
|
||||
@@ -216,7 +212,7 @@ class _NativeVideoViewerState extends ConsumerState<NativeVideoViewer> with Widg
|
||||
}
|
||||
|
||||
await _notifier.load(source);
|
||||
final loopVideo = ref.read(appSettingsServiceProvider).getSetting<bool>(AppSettingsEnum.loopVideo);
|
||||
final loopVideo = ref.read(metadataProvider).appConfig.viewer.loopVideo;
|
||||
await _notifier.setLoop(!widget.asset.isMotionPhoto && loopVideo);
|
||||
await _notifier.setVolume(1);
|
||||
}
|
||||
|
||||
@@ -15,37 +15,62 @@ class AuthGuard extends AutoRouteGuard {
|
||||
final ApiService _apiService;
|
||||
final AuthService _authService;
|
||||
final _log = Logger("AuthGuard");
|
||||
bool _validateInFlight = false;
|
||||
AuthGuard(this._apiService, this._authService);
|
||||
@override
|
||||
void onNavigation(NavigationResolver resolver, StackRouter router) async {
|
||||
resolver.next(true);
|
||||
|
||||
void onNavigation(NavigationResolver resolver, StackRouter router) {
|
||||
// Synchronously check for the access token. auto_route awaits async
|
||||
// guards, so we keep this function fully sync and validate the token in
|
||||
// the background — otherwise a slow validateAccessToken() request would
|
||||
// block the route transition for as long as the OS-level HTTP timeout.
|
||||
try {
|
||||
// Look in the store for an access token
|
||||
Store.get(StoreKey.accessToken);
|
||||
|
||||
// Validate the access token with the server
|
||||
final res = await _apiService.authenticationApi.validateAccessToken();
|
||||
if (res == null || res.authStatus != true) {
|
||||
// If the access token is invalid, take user back to login
|
||||
_log.fine('User token is invalid. Redirecting to login');
|
||||
unawaited(router.replaceAll([const LoginRoute()]).then((_) => _authService.clearLocalData()));
|
||||
}
|
||||
} on StoreKeyNotFoundException catch (_) {
|
||||
// If there is no access token, take us to the login page
|
||||
_log.warning('No access token in the store.');
|
||||
resolver.next(false);
|
||||
unawaited(router.replaceAll([const LoginRoute()]));
|
||||
return;
|
||||
}
|
||||
|
||||
resolver.next(true);
|
||||
unawaited(_validateAccessTokenInBackground(router));
|
||||
}
|
||||
|
||||
Future<void> _validateAccessTokenInBackground(StackRouter router) async {
|
||||
if (_validateInFlight) {
|
||||
return;
|
||||
}
|
||||
final token = Store.tryGet(StoreKey.accessToken);
|
||||
if (token == null) {
|
||||
return;
|
||||
}
|
||||
_validateInFlight = true;
|
||||
try {
|
||||
final res = await _apiService.authenticationApi.validateAccessToken();
|
||||
if (res == null || res.authStatus != true) {
|
||||
// Token may have changed during validation (user logged out + logged in
|
||||
// again); only act if it still applies to the current session.
|
||||
if (Store.tryGet(StoreKey.accessToken) != token) {
|
||||
return;
|
||||
}
|
||||
_log.fine('User token is invalid. Redirecting to login');
|
||||
await router.replaceAll([const LoginRoute()]);
|
||||
await _authService.clearLocalData();
|
||||
}
|
||||
} on ApiException catch (e) {
|
||||
// On an unauthorized request, take us to the login page
|
||||
if (e.code == HttpStatus.unauthorized) {
|
||||
_log.warning("Unauthorized access token.");
|
||||
unawaited(router.replaceAll([const LoginRoute()]).then((_) => _authService.clearLocalData()));
|
||||
if (e.code != HttpStatus.unauthorized) {
|
||||
return;
|
||||
}
|
||||
if (Store.tryGet(StoreKey.accessToken) != token) {
|
||||
return;
|
||||
}
|
||||
_log.warning("Unauthorized access token.");
|
||||
await router.replaceAll([const LoginRoute()]);
|
||||
await _authService.clearLocalData();
|
||||
} catch (e) {
|
||||
// Otherwise, this is not fatal, but we still log the warning
|
||||
_log.warning('Error validating access token from server: $e');
|
||||
} finally {
|
||||
_validateInFlight = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,10 +10,6 @@ enum AppSettingsEnum<T> {
|
||||
selectedAlbumSortOrder<int>(StoreKey.selectedAlbumSortOrder, "selectedAlbumSortOrder", 2),
|
||||
advancedTroubleshooting<bool>(StoreKey.advancedTroubleshooting, null, false),
|
||||
manageLocalMediaAndroid<bool>(StoreKey.manageLocalMediaAndroid, null, false),
|
||||
loopVideo<bool>(StoreKey.loopVideo, "loopVideo", true),
|
||||
loadOriginalVideo<bool>(StoreKey.loadOriginalVideo, "loadOriginalVideo", false),
|
||||
autoPlayVideo<bool>(StoreKey.autoPlayVideo, "autoPlayVideo", true),
|
||||
tapToNavigate<bool>(StoreKey.tapToNavigate, "tapToNavigate", false),
|
||||
allowSelfSignedSSLCert<bool>(StoreKey.selfSignedCert, null, false),
|
||||
selectedAlbumSortReverse<bool>(StoreKey.selectedAlbumSortReverse, null, true),
|
||||
enableHapticFeedback<bool>(StoreKey.enableHapticFeedback, null, true),
|
||||
|
||||
@@ -394,9 +394,13 @@ class BackgroundUploadService {
|
||||
final serverEndpoint = Store.get(StoreKey.serverEndpoint);
|
||||
final url = Uri.parse('$serverEndpoint/assets').toString();
|
||||
final headers = ApiService.getRequestHeaders();
|
||||
final deviceId = Store.get(StoreKey.deviceId);
|
||||
final (baseDirectory, directory, filename) = await Task.split(filePath: file.path);
|
||||
final fieldsMap = {
|
||||
'filename': originalFileName ?? filename,
|
||||
// deviceAssetId/deviceId required by server v2.7.5 and below (drop in v4.0 per #27818).
|
||||
'deviceAssetId': deviceAssetId ?? '',
|
||||
'deviceId': deviceId,
|
||||
'fileCreatedAt': createdAt.toUtc().toIso8601String(),
|
||||
'fileModifiedAt': modifiedAt.toUtc().toIso8601String(),
|
||||
'isFavorite': isFavorite?.toString() ?? 'false',
|
||||
|
||||
@@ -5,6 +5,8 @@ import 'dart:io';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/domain/models/asset/asset_metadata.model.dart';
|
||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||
import 'package:immich_mobile/entities/store.entity.dart';
|
||||
import 'package:immich_mobile/extensions/platform_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/network_capability_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
||||
@@ -319,8 +321,12 @@ class ForegroundUploadService {
|
||||
}
|
||||
|
||||
final originalFileName = entity.isLivePhoto ? p.setExtension(fileName, p.extension(file.path)) : fileName;
|
||||
final deviceId = Store.get(StoreKey.deviceId);
|
||||
|
||||
final fields = {
|
||||
// deviceAssetId/deviceId required by server v2.7.5 and below (drop in v4.0 per #27818).
|
||||
'deviceAssetId': asset.localId!,
|
||||
'deviceId': deviceId,
|
||||
'fileCreatedAt': asset.createdAt.toUtc().toIso8601String(),
|
||||
'fileModifiedAt': asset.updatedAt.toUtc().toIso8601String(),
|
||||
'isFavorite': asset.isFavorite.toString(),
|
||||
@@ -426,6 +432,9 @@ class ForegroundUploadService {
|
||||
final filename = p.basename(file.path);
|
||||
|
||||
final fields = {
|
||||
// deviceAssetId/deviceId required by server v2.7.5 and below (drop in v4.0 per #27818).
|
||||
'deviceAssetId': deviceAssetId,
|
||||
'deviceId': Store.get(StoreKey.deviceId),
|
||||
'fileCreatedAt': fileCreatedAt.toUtc().toIso8601String(),
|
||||
'fileModifiedAt': fileModifiedAt.toUtc().toIso8601String(),
|
||||
'isFavorite': 'false',
|
||||
|
||||
@@ -91,6 +91,11 @@ Future<void> _migrateTo26(Drift drift) async {
|
||||
// Image
|
||||
await migrator.migrateBool(StoreKey.legacyPreferRemoteImage, MetadataKey.imagePreferRemote);
|
||||
await migrator.migrateBool(StoreKey.legacyLoadOriginal, MetadataKey.imageLoadOriginal);
|
||||
// Viewer
|
||||
await migrator.migrateBool(StoreKey.legacyLoopVideo, MetadataKey.viewerLoopVideo);
|
||||
await migrator.migrateBool(StoreKey.legacyLoadOriginalVideo, MetadataKey.viewerLoadOriginalVideo);
|
||||
await migrator.migrateBool(StoreKey.legacyAutoPlayVideo, MetadataKey.viewerAutoPlayVideo);
|
||||
await migrator.migrateBool(StoreKey.legacyTapToNavigate, MetadataKey.viewerTapToNavigate);
|
||||
await migrator.complete();
|
||||
}
|
||||
|
||||
|
||||
+6
-5
@@ -1,18 +1,20 @@
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
||||
import 'package:immich_mobile/services/app_settings.service.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
|
||||
import 'package:immich_mobile/widgets/settings/settings_sub_title.dart';
|
||||
import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart';
|
||||
import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart';
|
||||
|
||||
class ImageViewerTapToNavigateSetting extends HookConsumerWidget {
|
||||
const ImageViewerTapToNavigateSetting({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final tapToNavigate = useAppSettingsState(AppSettingsEnum.tapToNavigate);
|
||||
final tapToNavigate = useState(ref.read(appConfigProvider).viewer.tapToNavigate);
|
||||
useValueChanged<bool, void>(tapToNavigate.value, (_, __) {
|
||||
ref.read(metadataProvider).write(.viewerTapToNavigate, tapToNavigate.value);
|
||||
});
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@@ -22,7 +24,6 @@ class ImageViewerTapToNavigateSetting extends HookConsumerWidget {
|
||||
valueNotifier: tapToNavigate,
|
||||
title: "setting_image_navigation_enable_title".tr(),
|
||||
subtitle: "setting_image_navigation_enable_subtitle".tr(),
|
||||
onChanged: (_) => ref.invalidate(appSettingsServiceProvider),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
@@ -1,20 +1,30 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
||||
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
||||
import 'package:immich_mobile/services/app_settings.service.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
|
||||
import 'package:immich_mobile/widgets/settings/setting_group_title.dart';
|
||||
import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart';
|
||||
import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart';
|
||||
|
||||
class VideoViewerSettings extends HookConsumerWidget {
|
||||
const VideoViewerSettings({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final useLoopVideo = useAppSettingsState(AppSettingsEnum.loopVideo);
|
||||
final useOriginalVideo = useAppSettingsState(AppSettingsEnum.loadOriginalVideo);
|
||||
final useAutoPlayVideo = useAppSettingsState(AppSettingsEnum.autoPlayVideo);
|
||||
final viewer = ref.read(appConfigProvider).viewer;
|
||||
final useAutoPlayVideo = useState(viewer.autoPlayVideo);
|
||||
final useLoopVideo = useState(viewer.loopVideo);
|
||||
final useOriginalVideo = useState(viewer.loadOriginalVideo);
|
||||
|
||||
useValueChanged<bool, void>(useAutoPlayVideo.value, (_, __) {
|
||||
ref.read(metadataProvider).write(.viewerAutoPlayVideo, useAutoPlayVideo.value);
|
||||
});
|
||||
useValueChanged<bool, void>(useLoopVideo.value, (_, __) {
|
||||
ref.read(metadataProvider).write(.viewerLoopVideo, useLoopVideo.value);
|
||||
});
|
||||
useValueChanged<bool, void>(useOriginalVideo.value, (_, __) {
|
||||
ref.read(metadataProvider).write(.viewerLoadOriginalVideo, useOriginalVideo.value);
|
||||
});
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@@ -27,19 +37,16 @@ class VideoViewerSettings extends HookConsumerWidget {
|
||||
valueNotifier: useAutoPlayVideo,
|
||||
title: "setting_video_viewer_auto_play_title".t(context: context),
|
||||
subtitle: "setting_video_viewer_auto_play_subtitle".t(context: context),
|
||||
onChanged: (_) => ref.invalidate(appSettingsServiceProvider),
|
||||
),
|
||||
SettingsSwitchListTile(
|
||||
valueNotifier: useLoopVideo,
|
||||
title: "setting_video_viewer_looping_title".t(context: context),
|
||||
subtitle: "loop_videos_description".t(context: context),
|
||||
onChanged: (_) => ref.invalidate(appSettingsServiceProvider),
|
||||
),
|
||||
SettingsSwitchListTile(
|
||||
valueNotifier: useOriginalVideo,
|
||||
title: "setting_video_viewer_original_video_title".t(context: context),
|
||||
subtitle: "setting_video_viewer_original_video_subtitle".t(context: context),
|
||||
onChanged: (_) => ref.invalidate(appSettingsServiceProvider),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
@@ -79,7 +79,6 @@ void main() {
|
||||
expect(sut.appConfig.theme.mode, ThemeMode.system);
|
||||
|
||||
await MetadataRepository.refresh();
|
||||
|
||||
expect(sut.appConfig.theme.mode, ThemeMode.dark);
|
||||
});
|
||||
|
||||
@@ -90,7 +89,6 @@ void main() {
|
||||
expect(sut.appConfig.theme.mode, ThemeMode.dark);
|
||||
|
||||
await MetadataRepository.refresh();
|
||||
|
||||
expect(sut.appConfig.theme.mode, ThemeMode.system);
|
||||
});
|
||||
|
||||
@@ -135,5 +133,4 @@ void main() {
|
||||
await expectation;
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
Generated
+90
-145
@@ -6,7 +6,7 @@ settings:
|
||||
injectWorkspacePackages: true
|
||||
|
||||
overrides:
|
||||
canvas: 2.11.2
|
||||
canvas: 3.2.3
|
||||
sharp: ^0.34.5
|
||||
webpackbar: ^7.0.0
|
||||
|
||||
@@ -198,7 +198,7 @@ importers:
|
||||
version: 6.1.1(typescript@6.0.3)(vite@8.0.10(@types/node@24.12.2)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.99.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
|
||||
vitest:
|
||||
specifier: ^4.0.0
|
||||
version: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.5)(happy-dom@20.9.0)(jsdom@26.1.0(canvas@2.11.2(encoding@0.1.13)))(vite@8.0.10(@types/node@24.12.2)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.99.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
|
||||
version: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.5)(happy-dom@20.9.0)(jsdom@26.1.0(canvas@3.2.3))(vite@8.0.10(@types/node@24.12.2)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.99.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
|
||||
|
||||
packages/cli:
|
||||
dependencies:
|
||||
@@ -289,7 +289,7 @@ importers:
|
||||
version: 8.0.10(@types/node@24.12.2)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.99.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
|
||||
vitest:
|
||||
specifier: ^4.0.0
|
||||
version: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.5)(happy-dom@20.9.0)(jsdom@26.1.0(canvas@2.11.2))(vite@8.0.10(@types/node@24.12.2)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.99.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
|
||||
version: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.5)(happy-dom@20.9.0)(jsdom@26.1.0(canvas@3.2.3))(vite@8.0.10(@types/node@24.12.2)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.99.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
|
||||
vitest-fetch-mock:
|
||||
specifier: ^0.4.0
|
||||
version: 0.4.5(vitest@4.1.5)
|
||||
@@ -663,7 +663,7 @@ importers:
|
||||
version: 13.15.10
|
||||
'@vitest/coverage-v8':
|
||||
specifier: ^3.0.0
|
||||
version: 3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.12.2)(happy-dom@20.9.0)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
|
||||
version: 3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.12.2)(happy-dom@20.9.0)(jiti@2.6.1)(jsdom@26.1.0(canvas@3.2.3))(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
|
||||
eslint:
|
||||
specifier: ^10.0.0
|
||||
version: 10.2.1(jiti@2.6.1)
|
||||
@@ -717,7 +717,7 @@ importers:
|
||||
version: 6.1.1(typescript@6.0.3)(vite@8.0.10(@types/node@24.12.2)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.99.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
|
||||
vitest:
|
||||
specifier: ^3.0.0
|
||||
version: 3.2.4(@types/debug@4.1.12)(@types/node@24.12.2)(happy-dom@20.9.0)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
|
||||
version: 3.2.4(@types/debug@4.1.12)(@types/node@24.12.2)(happy-dom@20.9.0)(jiti@2.6.1)(jsdom@26.1.0(canvas@3.2.3))(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
|
||||
|
||||
web:
|
||||
dependencies:
|
||||
@@ -973,7 +973,7 @@ importers:
|
||||
version: 8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.99.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
|
||||
vitest:
|
||||
specifier: ^4.0.0
|
||||
version: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@25.6.0)(@vitest/coverage-v8@4.1.5)(happy-dom@20.9.0)(jsdom@26.1.0(canvas@2.11.2))(vite@8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.99.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
|
||||
version: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@25.6.0)(@vitest/coverage-v8@4.1.5)(happy-dom@20.9.0)(jsdom@26.1.0(canvas@3.2.3))(vite@8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.99.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
|
||||
|
||||
packages:
|
||||
|
||||
@@ -6182,9 +6182,9 @@ packages:
|
||||
caniuse-lite@1.0.30001790:
|
||||
resolution: {integrity: sha512-bOoxfJPyYo+ds6W0YfptaCWbFnJYjh2Y1Eow5lRv+vI2u8ganPZqNm1JwNh0t2ELQCqIWg4B3dWEusgAmsoyOw==}
|
||||
|
||||
canvas@2.11.2:
|
||||
resolution: {integrity: sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw==}
|
||||
engines: {node: '>=6'}
|
||||
canvas@3.2.3:
|
||||
resolution: {integrity: sha512-PzE5nJZPz72YUAfo8oTp0u3fqqY7IzlTubneAihqDYAUcBk7ryeCmBbdJBEdaH0bptSOe2VT2Zwcb3UaFyaSWw==}
|
||||
engines: {node: ^18.12.0 || >= 20.9.0}
|
||||
|
||||
ccount@2.0.1:
|
||||
resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
|
||||
@@ -6964,10 +6964,6 @@ packages:
|
||||
decode-named-character-reference@1.2.0:
|
||||
resolution: {integrity: sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==}
|
||||
|
||||
decompress-response@4.2.1:
|
||||
resolution: {integrity: sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
decompress-response@6.0.0:
|
||||
resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -7567,6 +7563,10 @@ packages:
|
||||
resolution: {integrity: sha512-Yn66dSBaWGcUaSbm5Nl4G28rxtceLlWf4PstqJMbLix9sN7w0okWHPEvdudiP56Q5Cjl7v3TLyKKwowUFlbD8g==}
|
||||
engines: {node: '>=20.0.0'}
|
||||
|
||||
expand-template@2.0.3:
|
||||
resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
expect-type@1.3.0:
|
||||
resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
@@ -7866,6 +7866,9 @@ packages:
|
||||
get-tsconfig@4.13.0:
|
||||
resolution: {integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==}
|
||||
|
||||
github-from-package@0.0.0:
|
||||
resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==}
|
||||
|
||||
github-slugger@1.5.0:
|
||||
resolution: {integrity: sha512-wIh+gKBI9Nshz2o46B0B3f5k/W+WI9ZAv6y5Dn5WJ5SK1t0TnDimB4WE5rmTD05ZAIn8HALCZVmCsvj0w0v0lw==}
|
||||
|
||||
@@ -8591,7 +8594,7 @@ packages:
|
||||
resolution: {integrity: sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
canvas: 2.11.2
|
||||
canvas: 3.2.3
|
||||
peerDependenciesMeta:
|
||||
canvas:
|
||||
optional: true
|
||||
@@ -9308,10 +9311,6 @@ packages:
|
||||
resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
mimic-response@2.1.0:
|
||||
resolution: {integrity: sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
mimic-response@3.1.0:
|
||||
resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -9479,6 +9478,9 @@ packages:
|
||||
engines: {node: ^18 || >=20}
|
||||
hasBin: true
|
||||
|
||||
napi-build-utils@2.0.0:
|
||||
resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==}
|
||||
|
||||
natural-compare-lite@1.4.0:
|
||||
resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==}
|
||||
|
||||
@@ -9552,6 +9554,10 @@ packages:
|
||||
no-case@3.0.4:
|
||||
resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==}
|
||||
|
||||
node-abi@3.92.0:
|
||||
resolution: {integrity: sha512-KdHvFWZjEKDf0cakgFjebl371GPsISX2oZHcuyKqM7DtogIsHrqKeLTo8wBHxaXRAQlY2PsPlZmfo+9ZCxEREQ==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
node-abort-controller@3.1.1:
|
||||
resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==}
|
||||
|
||||
@@ -10499,6 +10505,12 @@ packages:
|
||||
resolution: {integrity: sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A==}
|
||||
engines: {node: '>=20'}
|
||||
|
||||
prebuild-install@7.1.3:
|
||||
resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==}
|
||||
engines: {node: '>=10'}
|
||||
deprecated: No longer maintained. Please contact the author of the relevant native addon; alternatives are available.
|
||||
hasBin: true
|
||||
|
||||
prelude-ls@1.2.1:
|
||||
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
@@ -11176,8 +11188,8 @@ packages:
|
||||
simple-concat@1.0.1:
|
||||
resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==}
|
||||
|
||||
simple-get@3.1.1:
|
||||
resolution: {integrity: sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==}
|
||||
simple-get@4.0.1:
|
||||
resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==}
|
||||
|
||||
simple-icons@16.17.0:
|
||||
resolution: {integrity: sha512-bRrGtzM6NLgxeMWmRcfDdrRksECk101lRrCn6jjj6qzUB6lQ+E5smnr52rqS1kLPmbLpS/g6iF463j50M4BT7A==}
|
||||
@@ -11886,6 +11898,9 @@ packages:
|
||||
engines: {node: '>=18.0.0'}
|
||||
hasBin: true
|
||||
|
||||
tunnel-agent@0.6.0:
|
||||
resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==}
|
||||
|
||||
tweetnacl@0.14.5:
|
||||
resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==}
|
||||
|
||||
@@ -15799,22 +15814,6 @@ snapshots:
|
||||
|
||||
'@mapbox/mapbox-gl-rtl-text@0.4.0': {}
|
||||
|
||||
'@mapbox/node-pre-gyp@1.0.11':
|
||||
dependencies:
|
||||
detect-libc: 2.1.2
|
||||
https-proxy-agent: 5.0.1
|
||||
make-dir: 3.1.0
|
||||
node-fetch: 2.7.0
|
||||
nopt: 5.0.0
|
||||
npmlog: 5.0.1
|
||||
rimraf: 3.0.2
|
||||
semver: 7.7.4
|
||||
tar: 6.2.1
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
- supports-color
|
||||
optional: true
|
||||
|
||||
'@mapbox/node-pre-gyp@1.0.11(encoding@0.1.13)':
|
||||
dependencies:
|
||||
detect-libc: 2.1.2
|
||||
@@ -17248,7 +17247,7 @@ snapshots:
|
||||
svelte: 5.55.2
|
||||
optionalDependencies:
|
||||
vite: 8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.99.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
|
||||
vitest: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@25.6.0)(@vitest/coverage-v8@4.1.5)(happy-dom@20.9.0)(jsdom@26.1.0(canvas@2.11.2))(vite@8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.99.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
|
||||
vitest: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@25.6.0)(@vitest/coverage-v8@4.1.5)(happy-dom@20.9.0)(jsdom@26.1.0(canvas@3.2.3))(vite@8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.99.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
|
||||
|
||||
'@testing-library/user-event@14.6.1(@testing-library/dom@10.4.1)':
|
||||
dependencies:
|
||||
@@ -17964,7 +17963,7 @@ snapshots:
|
||||
|
||||
'@vercel/oidc@3.0.5': {}
|
||||
|
||||
'@vitest/coverage-v8@3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.12.2)(happy-dom@20.9.0)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))':
|
||||
'@vitest/coverage-v8@3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.12.2)(happy-dom@20.9.0)(jiti@2.6.1)(jsdom@26.1.0(canvas@3.2.3))(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))':
|
||||
dependencies:
|
||||
'@ampproject/remapping': 2.3.0
|
||||
'@bcoe/v8-coverage': 1.0.2
|
||||
@@ -17979,7 +17978,7 @@ snapshots:
|
||||
std-env: 3.10.0
|
||||
test-exclude: 7.0.2
|
||||
tinyrainbow: 2.0.0
|
||||
vitest: 3.2.4(@types/debug@4.1.12)(@types/node@24.12.2)(happy-dom@20.9.0)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
|
||||
vitest: 3.2.4(@types/debug@4.1.12)(@types/node@24.12.2)(happy-dom@20.9.0)(jiti@2.6.1)(jsdom@26.1.0(canvas@3.2.3))(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@@ -17995,7 +17994,7 @@ snapshots:
|
||||
obug: 2.1.1
|
||||
std-env: 4.1.0
|
||||
tinyrainbow: 3.1.0
|
||||
vitest: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.5)(happy-dom@20.9.0)(jsdom@26.1.0(canvas@2.11.2(encoding@0.1.13)))(vite@8.0.10(@types/node@24.12.2)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.99.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
|
||||
vitest: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.5)(happy-dom@20.9.0)(jsdom@26.1.0(canvas@3.2.3))(vite@8.0.10(@types/node@24.12.2)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.99.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
|
||||
|
||||
'@vitest/expect@3.2.4':
|
||||
dependencies:
|
||||
@@ -18762,24 +18761,10 @@ snapshots:
|
||||
|
||||
caniuse-lite@1.0.30001790: {}
|
||||
|
||||
canvas@2.11.2:
|
||||
canvas@3.2.3:
|
||||
dependencies:
|
||||
'@mapbox/node-pre-gyp': 1.0.11
|
||||
nan: 2.26.2
|
||||
simple-get: 3.1.1
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
- supports-color
|
||||
optional: true
|
||||
|
||||
canvas@2.11.2(encoding@0.1.13):
|
||||
dependencies:
|
||||
'@mapbox/node-pre-gyp': 1.0.11(encoding@0.1.13)
|
||||
nan: 2.26.2
|
||||
simple-get: 3.1.1
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
- supports-color
|
||||
node-addon-api: 7.1.1
|
||||
prebuild-install: 7.1.3
|
||||
optional: true
|
||||
|
||||
ccount@2.0.1: {}
|
||||
@@ -19582,11 +19567,6 @@ snapshots:
|
||||
dependencies:
|
||||
character-entities: 2.0.2
|
||||
|
||||
decompress-response@4.2.1:
|
||||
dependencies:
|
||||
mimic-response: 2.1.0
|
||||
optional: true
|
||||
|
||||
decompress-response@6.0.0:
|
||||
dependencies:
|
||||
mimic-response: 3.1.0
|
||||
@@ -20331,6 +20311,9 @@ snapshots:
|
||||
optionalDependencies:
|
||||
exiftool-vendored.exe: 13.58.0
|
||||
|
||||
expand-template@2.0.3:
|
||||
optional: true
|
||||
|
||||
expect-type@1.3.0: {}
|
||||
|
||||
exponential-backoff@3.1.3: {}
|
||||
@@ -20416,11 +20399,10 @@ snapshots:
|
||||
|
||||
fabric@7.3.1:
|
||||
optionalDependencies:
|
||||
canvas: 2.11.2
|
||||
jsdom: 26.1.0(canvas@2.11.2)
|
||||
canvas: 3.2.3
|
||||
jsdom: 26.1.0(canvas@3.2.3)
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- encoding
|
||||
- supports-color
|
||||
- utf-8-validate
|
||||
|
||||
@@ -20712,6 +20694,9 @@ snapshots:
|
||||
dependencies:
|
||||
resolve-pkg-maps: 1.0.0
|
||||
|
||||
github-from-package@0.0.0:
|
||||
optional: true
|
||||
|
||||
github-slugger@1.5.0: {}
|
||||
|
||||
gl-matrix@3.4.4: {}
|
||||
@@ -21533,7 +21518,7 @@ snapshots:
|
||||
dependencies:
|
||||
argparse: 2.0.1
|
||||
|
||||
jsdom@26.1.0(canvas@2.11.2(encoding@0.1.13)):
|
||||
jsdom@26.1.0(canvas@3.2.3):
|
||||
dependencies:
|
||||
cssstyle: 4.6.0
|
||||
data-urls: 5.0.0
|
||||
@@ -21556,37 +21541,7 @@ snapshots:
|
||||
ws: 8.20.0
|
||||
xml-name-validator: 5.0.0
|
||||
optionalDependencies:
|
||||
canvas: 2.11.2(encoding@0.1.13)
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- supports-color
|
||||
- utf-8-validate
|
||||
optional: true
|
||||
|
||||
jsdom@26.1.0(canvas@2.11.2):
|
||||
dependencies:
|
||||
cssstyle: 4.6.0
|
||||
data-urls: 5.0.0
|
||||
decimal.js: 10.6.0
|
||||
html-encoding-sniffer: 4.0.0
|
||||
http-proxy-agent: 7.0.2
|
||||
https-proxy-agent: 7.0.6
|
||||
is-potential-custom-element-name: 1.0.1
|
||||
nwsapi: 2.2.23
|
||||
parse5: 7.3.0
|
||||
rrweb-cssom: 0.8.0
|
||||
saxes: 6.0.0
|
||||
symbol-tree: 3.2.4
|
||||
tough-cookie: 5.1.2
|
||||
w3c-xmlserializer: 5.0.0
|
||||
webidl-conversions: 7.0.0
|
||||
whatwg-encoding: 3.1.1
|
||||
whatwg-mimetype: 4.0.0
|
||||
whatwg-url: 14.2.0
|
||||
ws: 8.20.0
|
||||
xml-name-validator: 5.0.0
|
||||
optionalDependencies:
|
||||
canvas: 2.11.2
|
||||
canvas: 3.2.3
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- supports-color
|
||||
@@ -22589,9 +22544,6 @@ snapshots:
|
||||
|
||||
mimic-function@5.0.1: {}
|
||||
|
||||
mimic-response@2.1.0:
|
||||
optional: true
|
||||
|
||||
mimic-response@3.1.0: {}
|
||||
|
||||
mimic-response@4.0.0: {}
|
||||
@@ -22745,6 +22697,9 @@ snapshots:
|
||||
|
||||
nanoid@5.1.9: {}
|
||||
|
||||
napi-build-utils@2.0.0:
|
||||
optional: true
|
||||
|
||||
natural-compare-lite@1.4.0: {}
|
||||
|
||||
natural-compare@1.4.0: {}
|
||||
@@ -22817,6 +22772,11 @@ snapshots:
|
||||
lower-case: 2.0.2
|
||||
tslib: 2.8.1
|
||||
|
||||
node-abi@3.92.0:
|
||||
dependencies:
|
||||
semver: 7.7.4
|
||||
optional: true
|
||||
|
||||
node-abort-controller@3.1.1: {}
|
||||
|
||||
node-addon-api@4.3.0: {}
|
||||
@@ -22837,11 +22797,6 @@ snapshots:
|
||||
emojilib: 2.4.0
|
||||
skin-tone: 2.0.0
|
||||
|
||||
node-fetch@2.7.0:
|
||||
dependencies:
|
||||
whatwg-url: 5.0.0
|
||||
optional: true
|
||||
|
||||
node-fetch@2.7.0(encoding@0.1.13):
|
||||
dependencies:
|
||||
whatwg-url: 5.0.0
|
||||
@@ -23808,6 +23763,22 @@ snapshots:
|
||||
|
||||
powershell-utils@0.1.0: {}
|
||||
|
||||
prebuild-install@7.1.3:
|
||||
dependencies:
|
||||
detect-libc: 2.1.2
|
||||
expand-template: 2.0.3
|
||||
github-from-package: 0.0.0
|
||||
minimist: 1.2.8
|
||||
mkdirp-classic: 0.5.3
|
||||
napi-build-utils: 2.0.0
|
||||
node-abi: 3.92.0
|
||||
pump: 3.0.4
|
||||
rc: 1.2.8
|
||||
simple-get: 4.0.1
|
||||
tar-fs: 2.1.4
|
||||
tunnel-agent: 0.6.0
|
||||
optional: true
|
||||
|
||||
prelude-ls@1.2.1: {}
|
||||
|
||||
prettier-linter-helpers@1.0.1:
|
||||
@@ -24731,9 +24702,9 @@ snapshots:
|
||||
simple-concat@1.0.1:
|
||||
optional: true
|
||||
|
||||
simple-get@3.1.1:
|
||||
simple-get@4.0.1:
|
||||
dependencies:
|
||||
decompress-response: 4.2.1
|
||||
decompress-response: 6.0.0
|
||||
once: 1.4.0
|
||||
simple-concat: 1.0.1
|
||||
optional: true
|
||||
@@ -25575,6 +25546,11 @@ snapshots:
|
||||
optionalDependencies:
|
||||
fsevents: 2.3.3
|
||||
|
||||
tunnel-agent@0.6.0:
|
||||
dependencies:
|
||||
safe-buffer: 5.2.1
|
||||
optional: true
|
||||
|
||||
tweetnacl@0.14.5: {}
|
||||
|
||||
type-check@0.4.0:
|
||||
@@ -25984,9 +25960,9 @@ snapshots:
|
||||
|
||||
vitest-fetch-mock@0.4.5(vitest@4.1.5):
|
||||
dependencies:
|
||||
vitest: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.5)(happy-dom@20.9.0)(jsdom@26.1.0(canvas@2.11.2))(vite@8.0.10(@types/node@24.12.2)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.99.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
|
||||
vitest: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.5)(happy-dom@20.9.0)(jsdom@26.1.0(canvas@3.2.3))(vite@8.0.10(@types/node@24.12.2)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.99.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
|
||||
|
||||
vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.12.2)(happy-dom@20.9.0)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3):
|
||||
vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.12.2)(happy-dom@20.9.0)(jiti@2.6.1)(jsdom@26.1.0(canvas@3.2.3))(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3):
|
||||
dependencies:
|
||||
'@types/chai': 5.2.3
|
||||
'@vitest/expect': 3.2.4
|
||||
@@ -26015,7 +25991,7 @@ snapshots:
|
||||
'@types/debug': 4.1.12
|
||||
'@types/node': 24.12.2
|
||||
happy-dom: 20.9.0
|
||||
jsdom: 26.1.0(canvas@2.11.2)
|
||||
jsdom: 26.1.0(canvas@3.2.3)
|
||||
transitivePeerDependencies:
|
||||
- jiti
|
||||
- less
|
||||
@@ -26030,7 +26006,7 @@ snapshots:
|
||||
- tsx
|
||||
- yaml
|
||||
|
||||
vitest@4.1.5(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.5)(happy-dom@20.9.0)(jsdom@26.1.0(canvas@2.11.2(encoding@0.1.13)))(vite@8.0.10(@types/node@24.12.2)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.99.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
|
||||
vitest@4.1.5(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.5)(happy-dom@20.9.0)(jsdom@26.1.0(canvas@3.2.3))(vite@8.0.10(@types/node@24.12.2)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.99.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
|
||||
dependencies:
|
||||
'@vitest/expect': 4.1.5
|
||||
'@vitest/mocker': 4.1.5(vite@8.0.10(@types/node@24.12.2)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.99.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
|
||||
@@ -26057,42 +26033,11 @@ snapshots:
|
||||
'@types/node': 24.12.2
|
||||
'@vitest/coverage-v8': 4.1.5(vitest@4.1.5)
|
||||
happy-dom: 20.9.0
|
||||
jsdom: 26.1.0(canvas@2.11.2(encoding@0.1.13))
|
||||
jsdom: 26.1.0(canvas@3.2.3)
|
||||
transitivePeerDependencies:
|
||||
- msw
|
||||
|
||||
vitest@4.1.5(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.5)(happy-dom@20.9.0)(jsdom@26.1.0(canvas@2.11.2))(vite@8.0.10(@types/node@24.12.2)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.99.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
|
||||
dependencies:
|
||||
'@vitest/expect': 4.1.5
|
||||
'@vitest/mocker': 4.1.5(vite@8.0.10(@types/node@24.12.2)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.99.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
|
||||
'@vitest/pretty-format': 4.1.5
|
||||
'@vitest/runner': 4.1.5
|
||||
'@vitest/snapshot': 4.1.5
|
||||
'@vitest/spy': 4.1.5
|
||||
'@vitest/utils': 4.1.5
|
||||
es-module-lexer: 2.1.0
|
||||
expect-type: 1.3.0
|
||||
magic-string: 0.30.21
|
||||
obug: 2.1.1
|
||||
pathe: 2.0.3
|
||||
picomatch: 4.0.4
|
||||
std-env: 4.1.0
|
||||
tinybench: 2.9.0
|
||||
tinyexec: 1.1.1
|
||||
tinyglobby: 0.2.16
|
||||
tinyrainbow: 3.1.0
|
||||
vite: 8.0.10(@types/node@24.12.2)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.99.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
|
||||
why-is-node-running: 2.3.0
|
||||
optionalDependencies:
|
||||
'@opentelemetry/api': 1.9.1
|
||||
'@types/node': 24.12.2
|
||||
'@vitest/coverage-v8': 4.1.5(vitest@4.1.5)
|
||||
happy-dom: 20.9.0
|
||||
jsdom: 26.1.0(canvas@2.11.2)
|
||||
transitivePeerDependencies:
|
||||
- msw
|
||||
|
||||
vitest@4.1.5(@opentelemetry/api@1.9.1)(@types/node@25.6.0)(@vitest/coverage-v8@4.1.5)(happy-dom@20.9.0)(jsdom@26.1.0(canvas@2.11.2))(vite@8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.99.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
|
||||
vitest@4.1.5(@opentelemetry/api@1.9.1)(@types/node@25.6.0)(@vitest/coverage-v8@4.1.5)(happy-dom@20.9.0)(jsdom@26.1.0(canvas@3.2.3))(vite@8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.99.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
|
||||
dependencies:
|
||||
'@vitest/expect': 4.1.5
|
||||
'@vitest/mocker': 4.1.5(vite@8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.99.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
|
||||
@@ -26119,7 +26064,7 @@ snapshots:
|
||||
'@types/node': 25.6.0
|
||||
'@vitest/coverage-v8': 4.1.5(vitest@4.1.5)
|
||||
happy-dom: 20.9.0
|
||||
jsdom: 26.1.0(canvas@2.11.2)
|
||||
jsdom: 26.1.0(canvas@3.2.3)
|
||||
transitivePeerDependencies:
|
||||
- msw
|
||||
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@ onlyBuiltDependencies:
|
||||
- '@tailwindcss/oxide'
|
||||
- bcrypt
|
||||
overrides:
|
||||
canvas: 2.11.2
|
||||
canvas: 3.2.3
|
||||
sharp: ^0.34.5
|
||||
# pending docusaurus 3.10.1
|
||||
webpackbar: ^7.0.0
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { BinaryField, DefaultReadTaskOptions, ExifTool, Tags } from 'exiftool-vendored';
|
||||
import { BinaryField, DefaultReadTaskOptions, ExifTool, ReadTaskOptions, Tags } from 'exiftool-vendored';
|
||||
import geotz from 'geo-tz';
|
||||
import { LoggingRepository } from 'src/repositories/logging.repository';
|
||||
import { mimeTypes } from 'src/utils/mime-types';
|
||||
@@ -89,7 +89,7 @@ export class MetadataRepository {
|
||||
geoTz: (lat, lon) => geotz.find(lat, lon)[0],
|
||||
geolocation: true,
|
||||
// Enable exiftool LFS to parse metadata for files larger than 2GB.
|
||||
readArgs: ['-api', 'largefilesupport=1'],
|
||||
readArgs: ['-api', 'largefilesupport=1', '--ICC_Profile:DeviceManufacturer', '--ICC_Profile:DeviceModelName'],
|
||||
writeArgs: ['-api', 'largefilesupport=1', '-overwrite_original'],
|
||||
taskTimeoutMillis: 2 * 60 * 1000,
|
||||
});
|
||||
@@ -107,8 +107,8 @@ export class MetadataRepository {
|
||||
}
|
||||
|
||||
readTags(path: string): Promise<ImmichTags> {
|
||||
const args = mimeTypes.isVideo(path) ? ['-ee'] : [];
|
||||
return this.exiftool.read(path, { readArgs: args }).catch((error) => {
|
||||
const options: ReadTaskOptions | undefined = mimeTypes.isVideo(path) ? { readArgs: ['-ee'] } : undefined;
|
||||
return this.exiftool.read(path, options).catch((error) => {
|
||||
this.logger.warn(`Error reading exif data (${path}): ${error}\n${error?.stack}`);
|
||||
return {};
|
||||
}) as Promise<ImmichTags>;
|
||||
|
||||
@@ -212,16 +212,10 @@
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
return {
|
||||
fileCreatedAfter: dateAfter ? new Date(dateAfter).toISOString() : undefined,
|
||||
fileCreatedBefore: dateBefore ? new Date(dateBefore).toISOString() : undefined,
|
||||
};
|
||||
} catch {
|
||||
$mapSettings.dateAfter = '';
|
||||
$mapSettings.dateBefore = '';
|
||||
return {};
|
||||
}
|
||||
return {
|
||||
fileCreatedAfter: dateAfter?.toUTC().toISO(),
|
||||
fileCreatedBefore: dateBefore?.toUTC().toISO(),
|
||||
};
|
||||
}
|
||||
|
||||
async function loadMapMarkers() {
|
||||
@@ -237,7 +231,7 @@
|
||||
{
|
||||
isArchived: includeArchived || undefined,
|
||||
isFavorite: onlyFavorites || undefined,
|
||||
fileCreatedAfter: fileCreatedAfter || undefined,
|
||||
fileCreatedAfter,
|
||||
fileCreatedBefore,
|
||||
withPartners: withPartners || undefined,
|
||||
withSharedAlbums: withSharedAlbums || undefined,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<script lang="ts">
|
||||
import DateInput from '$lib/elements/DateInput.svelte';
|
||||
import type { MapSettings } from '$lib/stores/preferences.store';
|
||||
import { Button, Field, FormModal, Select, Stack, Switch } from '@immich/ui';
|
||||
import { Button, DatePicker, Field, FormModal, Select, Stack, Switch } from '@immich/ui';
|
||||
import { Duration } from 'luxon';
|
||||
import { t } from 'svelte-i18n';
|
||||
import { fly } from 'svelte/transition';
|
||||
@@ -41,29 +40,21 @@
|
||||
|
||||
{#if customDateRange}
|
||||
<div in:fly={{ y: 10, duration: 200 }} class="flex flex-col gap-4">
|
||||
<div class="flex items-center justify-between gap-8">
|
||||
<label class="shrink-0 text-sm immich-form-label" for="date-after">{$t('date_after')}</label>
|
||||
<DateInput
|
||||
class="immich-form-input w-40"
|
||||
type="date"
|
||||
id="date-after"
|
||||
max={settings.dateBefore}
|
||||
bind:value={settings.dateAfter}
|
||||
/>
|
||||
</div>
|
||||
<div class="flex items-center justify-between gap-8">
|
||||
<label class="shrink-0 text-sm immich-form-label" for="date-before">{$t('date_before')}</label>
|
||||
<DateInput class="immich-form-input w-40" type="date" id="date-before" bind:value={settings.dateBefore} />
|
||||
</div>
|
||||
<div class="flex justify-center text-xs">
|
||||
<Field label={$t('date_after')}>
|
||||
<DatePicker bind:value={settings.dateAfter} maxDate={settings.dateBefore} />
|
||||
</Field>
|
||||
<Field label={$t('date_before')}>
|
||||
<DatePicker bind:value={settings.dateBefore} />
|
||||
</Field>
|
||||
<div class="flex justify-center">
|
||||
<Button
|
||||
color="primary"
|
||||
size="small"
|
||||
variant="ghost"
|
||||
onclick={() => {
|
||||
customDateRange = false;
|
||||
settings.dateAfter = '';
|
||||
settings.dateBefore = '';
|
||||
settings.dateAfter = undefined;
|
||||
settings.dateBefore = undefined;
|
||||
}}
|
||||
>
|
||||
{$t('remove_custom_date_range')}
|
||||
@@ -71,7 +62,7 @@
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<div in:fly={{ y: -10, duration: 200 }} class="flex flex-col gap-1">
|
||||
<div in:fly={{ y: -10, duration: 200 }} class="flex flex-col gap-2">
|
||||
<Field label={$t('date_range')}>
|
||||
<Select
|
||||
bind:value={settings.relativeDate}
|
||||
@@ -82,40 +73,38 @@
|
||||
},
|
||||
{
|
||||
label: $t('past_durations.hours', { values: { hours: 24 } }),
|
||||
value: Duration.fromObject({ hours: 24 }).toISO() || '',
|
||||
value: Duration.fromObject({ hours: 24 }).toISO(),
|
||||
},
|
||||
{
|
||||
label: $t('past_durations.days', { values: { days: 7 } }),
|
||||
value: Duration.fromObject({ days: 7 }).toISO() || '',
|
||||
value: Duration.fromObject({ days: 7 }).toISO(),
|
||||
},
|
||||
{
|
||||
label: $t('past_durations.days', { values: { days: 30 } }),
|
||||
value: Duration.fromObject({ days: 30 }).toISO() || '',
|
||||
value: Duration.fromObject({ days: 30 }).toISO(),
|
||||
},
|
||||
{
|
||||
label: $t('past_durations.years', { values: { years: 1 } }),
|
||||
value: Duration.fromObject({ years: 1 }).toISO() || '',
|
||||
value: Duration.fromObject({ years: 1 }).toISO(),
|
||||
},
|
||||
{
|
||||
label: $t('past_durations.years', { values: { years: 3 } }),
|
||||
value: Duration.fromObject({ years: 3 }).toISO() || '',
|
||||
value: Duration.fromObject({ years: 3 }).toISO(),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Field>
|
||||
<div class="text-xs">
|
||||
<Button
|
||||
color="primary"
|
||||
size="small"
|
||||
variant="ghost"
|
||||
onclick={() => {
|
||||
customDateRange = true;
|
||||
settings.relativeDate = '';
|
||||
}}
|
||||
>
|
||||
{$t('use_custom_date_range')}
|
||||
</Button>
|
||||
</div>
|
||||
<Button
|
||||
color="primary"
|
||||
size="small"
|
||||
variant="ghost"
|
||||
onclick={() => {
|
||||
customDateRange = true;
|
||||
settings.relativeDate = '';
|
||||
}}
|
||||
>
|
||||
{$t('use_custom_date_range')}
|
||||
</Button>
|
||||
</div>
|
||||
{/if}
|
||||
</Stack>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script lang="ts">
|
||||
import DateInput from '$lib/elements/DateInput.svelte';
|
||||
import { handleUpdatePersonBirthDate } from '$lib/services/person.service';
|
||||
import { type PersonResponseDto } from '@immich/sdk';
|
||||
import { Button, FormModal, Text } from '@immich/ui';
|
||||
import { Button, DatePicker, Field, FormModal, HelperText } from '@immich/ui';
|
||||
import { mdiCake } from '@mdi/js';
|
||||
import { DateTime } from 'luxon';
|
||||
import { t } from 'svelte-i18n';
|
||||
|
||||
type Props = {
|
||||
@@ -12,32 +12,25 @@
|
||||
};
|
||||
|
||||
let { person, onClose }: Props = $props();
|
||||
let birthDate = $derived(person.birthDate ?? '');
|
||||
let birthDate = $derived(person.birthDate ? DateTime.fromISO(person.birthDate) : undefined);
|
||||
|
||||
const onSubmit = async () => {
|
||||
const success = await handleUpdatePersonBirthDate(person, birthDate);
|
||||
const success = await handleUpdatePersonBirthDate(person, birthDate?.toISODate() ?? '');
|
||||
if (success) {
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
|
||||
const todayFormatted = new Date().toISOString().split('T')[0];
|
||||
</script>
|
||||
|
||||
<FormModal title={$t('set_date_of_birth')} size="small" icon={mdiCake} {onClose} {onSubmit}>
|
||||
<Text size="small">{$t('birthdate_set_description')}</Text>
|
||||
<div class="my-4 flex flex-col gap-2">
|
||||
<DateInput
|
||||
class="immich-form-input"
|
||||
id="birthDate"
|
||||
name="birthDate"
|
||||
type="date"
|
||||
bind:value={birthDate}
|
||||
max={todayFormatted}
|
||||
/>
|
||||
<div class="my-2 flex flex-col gap-2">
|
||||
<Field label={$t('date_of_birth')}>
|
||||
<DatePicker bind:value={birthDate} maxDate={DateTime.now()} />
|
||||
<HelperText>{$t('birthdate_set_description')}</HelperText>
|
||||
</Field>
|
||||
{#if person.birthDate}
|
||||
<div class="flex justify-end">
|
||||
<Button shape="round" color="secondary" size="small" onclick={() => (birthDate = '')}>
|
||||
<Button shape="round" color="secondary" size="small" onclick={() => (birthDate = undefined)}>
|
||||
{$t('clear')}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { DateTime } from 'luxon';
|
||||
import { persisted } from 'svelte-persisted-store';
|
||||
import { browser } from '$app/environment';
|
||||
import { defaultLang } from '$lib/constants';
|
||||
@@ -26,8 +27,8 @@ export interface MapSettings {
|
||||
withPartners: boolean;
|
||||
withSharedAlbums: boolean;
|
||||
relativeDate: string;
|
||||
dateAfter: string;
|
||||
dateBefore: string;
|
||||
dateAfter?: DateTime<true>;
|
||||
dateBefore?: DateTime<true>;
|
||||
}
|
||||
|
||||
const defaultMapSettings = {
|
||||
@@ -37,8 +38,6 @@ const defaultMapSettings = {
|
||||
withPartners: false,
|
||||
withSharedAlbums: false,
|
||||
relativeDate: '',
|
||||
dateAfter: '',
|
||||
dateBefore: '',
|
||||
};
|
||||
|
||||
const persistedObject = <T>(key: string, defaults: T) =>
|
||||
|
||||
Reference in New Issue
Block a user