1
0
forked from Cutlery/immich

Compare commits

..

26 Commits

Author SHA1 Message Date
Alex 9b705e4450 chore(server): open-api memory lane number generation (#8314)
chore(server): openaapi memory lane number generation
2024-03-27 22:27:26 -05:00
Ben Basten e1c2135850 feat(web, a11y): focus management for modals and popups (#8298)
* feat(web, a11y): focus management for modals and popups

* feat: hide asset options dropdown on escape key
2024-03-27 15:55:27 -05:00
Jason Rasmussen 9fe80c25eb fix: memory lane assets in ascending order (#8309)
* fix: memory lane asset order

* chore: deprecate title

* chore: open-api

* chore: rename years => yearsAgo
2024-03-27 16:14:29 -04:00
Sam Holton 13b11a39a9 feat(web) add filter when viewing all people in search box (#7997)
* feat(web) add filter when viewing all people in search box

* chore: svelte check

* pr feedback: fix vertical spacing to eliminate jump when filter appears

* pr feedback

* simplify filter logic

---------

Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
2024-03-27 14:58:38 -05:00
Ethan Margaillan 8bf571bf48 feat(web): better UX when creating a new album (#8270)
* feat(web): ask user before going to newly created album

* feat(web): add button option to notification cards

* feat(web): allow html messages in notification cards

* show album -> view album

* remove 'link' action from notifications

* remove unused type
2024-03-27 14:47:42 -05:00
Jason Rasmussen 613b544bf0 feat(cli): better server info output (#8307)
* feat(cli): server-info command prints url and user email

* chore: clean up

---------

Co-authored-by: 澪 <mio@mio19.uk>
2024-03-27 20:01:36 +01:00
renovate[bot] 916603d2d4 fix(deps): update typescript-projects (#8287)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-27 12:07:49 -04:00
Jason Rasmussen e30eecba2c chore(cli): prepare release (#8296)
chore: prepare release
2024-03-26 18:56:16 +00:00
Alex 3a94be0212 chore(web): style tab button group and search bar (#8292)
* chore(web): style tab button group and search bar

* responsive design
2024-03-26 10:36:15 -05:00
renovate[bot] 6295edcdb7 chore(deps): update base-image to v20240326 (major) (#8291)
chore(deps): update base-image to v20240326

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-26 09:17:48 -04:00
Jordy 335b4937ed fix(docs): fix path in readme files (#8289)
fix(docs): paths for images
2024-03-26 13:07:55 +00:00
indam 06da0469c4 Update README_zh_CN.md to sync with README.md (#8282) 2024-03-26 10:39:37 +00:00
renovate[bot] 1ad893ded4 fix(deps): update typescript-projects (#8281)
* fix(deps): update typescript-projects

* chore: regenerate sql

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>
2024-03-26 02:39:57 +00:00
Jordy 636f5fb933 feat(docs): removed deprecated version line in docker compose files (#8276)
Removed deprecated version line in docker compose files
2024-03-25 19:20:49 -04:00
Mert c45e28ab53 refactor(server): metric repo (#8278)
* refactor

* redundant `implements`

* simplify

* remove `enabled`
2024-03-25 23:15:11 +00:00
Daniel Dietzler c56c04a82b refactor(server): rename filesystem provider -> storage repo (#8277)
rename filesystem provider -> storage repo
2024-03-25 20:45:21 +00:00
Daniel Dietzler d431d37454 feat(server): dark mode support for star history (#8259)
use theme sensitive star history graphic
2024-03-25 08:20:26 -04:00
Daniel Dietzler 1694dd146e feat(server): add immich cli to container (#8239)
add immich cli to server container
2024-03-25 08:18:33 -04:00
Mert 4a6a0aa142 fix(server): places page not working with partner sharing (#8257) 2024-03-25 01:59:11 -04:00
Keshav Prateek Pandey c788160532 chore(docs): Update docker compose readme with wget download option (#8250)
Add download option to wget
2024-03-25 05:01:29 +00:00
James Wilson cc66159f04 fix(web): Correct minor typo in new version check copy (#8253)
Fix typo in new-version-check-settings.svelte
2024-03-24 23:35:06 -04:00
Mert c58a70ac8f feat(server): job metrics (#8255)
* metric repo

* add metric repo

* remove unused import

* formatting

* fix

* try disabling job metrics for e2e

* import otel in test module
2024-03-24 23:02:04 -04:00
Alex 1855aaea99 chore(web): favicon without white bg (#8251) 2024-03-24 16:44:59 -05:00
Alex 3901b5da44 fix(web): default album view (#8247)
* fix(web): default album view

* linting
2024-03-24 20:44:22 +00:00
martin 5dc59b591d refactor(web): albums list (2) (#8214)
* refactor: albums list

* fix: responsive design

* keep albums in sharing
2024-03-24 13:07:20 -05:00
Kokul Shanmugharajah 96a5710932 (docs) Update XMP sidecar docs to include the fact that Immich will look for photo.ext.xmp and photo.xmp (#8235)
Update XMP sidecar docs
2024-03-24 08:45:42 -04:00
150 changed files with 2240 additions and 2104 deletions
+6 -2
View File
@@ -131,6 +131,10 @@ If you feel like this is the right cause and the app is something you are seeing
## Star History
<a href="https://star-history.com/#immich-app/immich">
<img src="https://api.star-history.com/svg?repos=immich-app/immich&type=Date" alt="Star History Chart" width="100%" />
<a href="https://star-history.com/#immich-app/immich&Date">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=immich-app/immich&type=Date&theme=dark" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=immich-app/immich&type=Date" />
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=immich-app/immich&type=Date" width="100%" />
</picture>
</a>
+209 -217
View File
@@ -1,12 +1,12 @@
{
"name": "@immich/cli",
"version": "2.1.0",
"version": "2.2.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@immich/cli",
"version": "2.1.0",
"version": "2.2.0",
"license": "GNU Affero General Public License version 3",
"dependencies": {
"lodash-es": "^4.17.21"
@@ -300,9 +300,9 @@
"dev": true
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz",
"integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz",
"integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==",
"cpu": [
"ppc64"
],
@@ -316,9 +316,9 @@
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz",
"integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz",
"integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==",
"cpu": [
"arm"
],
@@ -332,9 +332,9 @@
}
},
"node_modules/@esbuild/android-arm64": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz",
"integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz",
"integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==",
"cpu": [
"arm64"
],
@@ -348,9 +348,9 @@
}
},
"node_modules/@esbuild/android-x64": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz",
"integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz",
"integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==",
"cpu": [
"x64"
],
@@ -364,9 +364,9 @@
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz",
"integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz",
"integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==",
"cpu": [
"arm64"
],
@@ -380,9 +380,9 @@
}
},
"node_modules/@esbuild/darwin-x64": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz",
"integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz",
"integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==",
"cpu": [
"x64"
],
@@ -396,9 +396,9 @@
}
},
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz",
"integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz",
"integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==",
"cpu": [
"arm64"
],
@@ -412,9 +412,9 @@
}
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz",
"integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz",
"integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==",
"cpu": [
"x64"
],
@@ -428,9 +428,9 @@
}
},
"node_modules/@esbuild/linux-arm": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz",
"integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz",
"integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==",
"cpu": [
"arm"
],
@@ -444,9 +444,9 @@
}
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz",
"integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz",
"integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==",
"cpu": [
"arm64"
],
@@ -460,9 +460,9 @@
}
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz",
"integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz",
"integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==",
"cpu": [
"ia32"
],
@@ -476,9 +476,9 @@
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz",
"integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz",
"integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==",
"cpu": [
"loong64"
],
@@ -492,9 +492,9 @@
}
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz",
"integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz",
"integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==",
"cpu": [
"mips64el"
],
@@ -508,9 +508,9 @@
}
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz",
"integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz",
"integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==",
"cpu": [
"ppc64"
],
@@ -524,9 +524,9 @@
}
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz",
"integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz",
"integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==",
"cpu": [
"riscv64"
],
@@ -540,9 +540,9 @@
}
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz",
"integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz",
"integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==",
"cpu": [
"s390x"
],
@@ -556,9 +556,9 @@
}
},
"node_modules/@esbuild/linux-x64": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz",
"integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz",
"integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==",
"cpu": [
"x64"
],
@@ -572,9 +572,9 @@
}
},
"node_modules/@esbuild/netbsd-x64": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz",
"integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz",
"integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==",
"cpu": [
"x64"
],
@@ -588,9 +588,9 @@
}
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz",
"integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz",
"integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==",
"cpu": [
"x64"
],
@@ -604,9 +604,9 @@
}
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz",
"integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz",
"integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==",
"cpu": [
"x64"
],
@@ -620,9 +620,9 @@
}
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz",
"integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz",
"integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==",
"cpu": [
"arm64"
],
@@ -636,9 +636,9 @@
}
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz",
"integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz",
"integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==",
"cpu": [
"ia32"
],
@@ -652,9 +652,9 @@
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz",
"integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz",
"integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==",
"cpu": [
"x64"
],
@@ -1230,9 +1230,9 @@
}
},
"node_modules/@types/node": {
"version": "20.11.27",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.27.tgz",
"integrity": "sha512-qyUZfMnCg1KEz57r7pzFtSGt49f6RPkPBis3Vo4PbS7roQEDn22hiHzl/Lo1q4i4hDEgBJmBF/NTNg2XR0HbFg==",
"version": "20.11.30",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.30.tgz",
"integrity": "sha512-dHM6ZxwlmuZaRmUPfv1p+KrdD1Dci04FbdEm/9wEMouFqxYoFl5aMkt0VMAUtYRQDyYvD41WJLukhq/ha3YuTw==",
"dev": true,
"dependencies": {
"undici-types": "~5.26.4"
@@ -1251,16 +1251,16 @@
"dev": true
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.2.0.tgz",
"integrity": "sha512-mdekAHOqS9UjlmyF/LSs6AIEvfceV749GFxoBAjwAv0nkevfKHWQFDMcBZWUiIC5ft6ePWivXoS36aKQ0Cy3sw==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.3.1.tgz",
"integrity": "sha512-STEDMVQGww5lhCuNXVSQfbfuNII5E08QWkvAw5Qwf+bj2WT+JkG1uc+5/vXA3AOYMDHVOSpL+9rcbEUiHIm2dw==",
"dev": true,
"dependencies": {
"@eslint-community/regexpp": "^4.5.1",
"@typescript-eslint/scope-manager": "7.2.0",
"@typescript-eslint/type-utils": "7.2.0",
"@typescript-eslint/utils": "7.2.0",
"@typescript-eslint/visitor-keys": "7.2.0",
"@typescript-eslint/scope-manager": "7.3.1",
"@typescript-eslint/type-utils": "7.3.1",
"@typescript-eslint/utils": "7.3.1",
"@typescript-eslint/visitor-keys": "7.3.1",
"debug": "^4.3.4",
"graphemer": "^1.4.0",
"ignore": "^5.2.4",
@@ -1269,7 +1269,7 @@
"ts-api-utils": "^1.0.1"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
"node": "^18.18.0 || >=20.0.0"
},
"funding": {
"type": "opencollective",
@@ -1286,19 +1286,19 @@
}
},
"node_modules/@typescript-eslint/parser": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.2.0.tgz",
"integrity": "sha512-5FKsVcHTk6TafQKQbuIVkXq58Fnbkd2wDL4LB7AURN7RUOu1utVP+G8+6u3ZhEroW3DF6hyo3ZEXxgKgp4KeCg==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.3.1.tgz",
"integrity": "sha512-Rq49+pq7viTRCH48XAbTA+wdLRrB/3sRq4Lpk0oGDm0VmnjBrAOVXH/Laalmwsv2VpekiEfVFwJYVk6/e8uvQw==",
"dev": true,
"dependencies": {
"@typescript-eslint/scope-manager": "7.2.0",
"@typescript-eslint/types": "7.2.0",
"@typescript-eslint/typescript-estree": "7.2.0",
"@typescript-eslint/visitor-keys": "7.2.0",
"@typescript-eslint/scope-manager": "7.3.1",
"@typescript-eslint/types": "7.3.1",
"@typescript-eslint/typescript-estree": "7.3.1",
"@typescript-eslint/visitor-keys": "7.3.1",
"debug": "^4.3.4"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
"node": "^18.18.0 || >=20.0.0"
},
"funding": {
"type": "opencollective",
@@ -1314,16 +1314,16 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.2.0.tgz",
"integrity": "sha512-Qh976RbQM/fYtjx9hs4XkayYujB/aPwglw2choHmf3zBjB4qOywWSdt9+KLRdHubGcoSwBnXUH2sR3hkyaERRg==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.3.1.tgz",
"integrity": "sha512-fVS6fPxldsKY2nFvyT7IP78UO1/I2huG+AYu5AMjCT9wtl6JFiDnsv4uad4jQ0GTFzcUV5HShVeN96/17bTBag==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "7.2.0",
"@typescript-eslint/visitor-keys": "7.2.0"
"@typescript-eslint/types": "7.3.1",
"@typescript-eslint/visitor-keys": "7.3.1"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
"node": "^18.18.0 || >=20.0.0"
},
"funding": {
"type": "opencollective",
@@ -1331,18 +1331,18 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.2.0.tgz",
"integrity": "sha512-xHi51adBHo9O9330J8GQYQwrKBqbIPJGZZVQTHHmy200hvkLZFWJIFtAG/7IYTWUyun6DE6w5InDReePJYJlJA==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.3.1.tgz",
"integrity": "sha512-iFhaysxFsMDQlzJn+vr3OrxN8NmdQkHks4WaqD4QBnt5hsq234wcYdyQ9uquzJJIDAj5W4wQne3yEsYA6OmXGw==",
"dev": true,
"dependencies": {
"@typescript-eslint/typescript-estree": "7.2.0",
"@typescript-eslint/utils": "7.2.0",
"@typescript-eslint/typescript-estree": "7.3.1",
"@typescript-eslint/utils": "7.3.1",
"debug": "^4.3.4",
"ts-api-utils": "^1.0.1"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
"node": "^18.18.0 || >=20.0.0"
},
"funding": {
"type": "opencollective",
@@ -1358,12 +1358,12 @@
}
},
"node_modules/@typescript-eslint/types": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.2.0.tgz",
"integrity": "sha512-XFtUHPI/abFhm4cbCDc5Ykc8npOKBSJePY3a3s+lwumt7XWJuzP5cZcfZ610MIPHjQjNsOLlYK8ASPaNG8UiyA==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.3.1.tgz",
"integrity": "sha512-2tUf3uWggBDl4S4183nivWQ2HqceOZh1U4hhu4p1tPiIJoRRXrab7Y+Y0p+dozYwZVvLPRI6r5wKe9kToF9FIw==",
"dev": true,
"engines": {
"node": "^16.0.0 || >=18.0.0"
"node": "^18.18.0 || >=20.0.0"
},
"funding": {
"type": "opencollective",
@@ -1371,13 +1371,13 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.2.0.tgz",
"integrity": "sha512-cyxS5WQQCoBwSakpMrvMXuMDEbhOo9bNHHrNcEWis6XHx6KF518tkF1wBvKIn/tpq5ZpUYK7Bdklu8qY0MsFIA==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.3.1.tgz",
"integrity": "sha512-tLpuqM46LVkduWP7JO7yVoWshpJuJzxDOPYIVWUUZbW+4dBpgGeUdl/fQkhuV0A8eGnphYw3pp8d2EnvPOfxmQ==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "7.2.0",
"@typescript-eslint/visitor-keys": "7.2.0",
"@typescript-eslint/types": "7.3.1",
"@typescript-eslint/visitor-keys": "7.3.1",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
@@ -1386,7 +1386,7 @@
"ts-api-utils": "^1.0.1"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
"node": "^18.18.0 || >=20.0.0"
},
"funding": {
"type": "opencollective",
@@ -1399,21 +1399,21 @@
}
},
"node_modules/@typescript-eslint/utils": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.2.0.tgz",
"integrity": "sha512-YfHpnMAGb1Eekpm3XRK8hcMwGLGsnT6L+7b2XyRv6ouDuJU1tZir1GS2i0+VXRatMwSI1/UfcyPe53ADkU+IuA==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.3.1.tgz",
"integrity": "sha512-jIERm/6bYQ9HkynYlNZvXpzmXWZGhMbrOvq3jJzOSOlKXsVjrrolzWBjDW6/TvT5Q3WqaN4EkmcfdQwi9tDjBQ==",
"dev": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.4.0",
"@types/json-schema": "^7.0.12",
"@types/semver": "^7.5.0",
"@typescript-eslint/scope-manager": "7.2.0",
"@typescript-eslint/types": "7.2.0",
"@typescript-eslint/typescript-estree": "7.2.0",
"@typescript-eslint/scope-manager": "7.3.1",
"@typescript-eslint/types": "7.3.1",
"@typescript-eslint/typescript-estree": "7.3.1",
"semver": "^7.5.4"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
"node": "^18.18.0 || >=20.0.0"
},
"funding": {
"type": "opencollective",
@@ -1424,16 +1424,16 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.2.0.tgz",
"integrity": "sha512-c6EIQRHhcpl6+tO8EMR+kjkkV+ugUNXOmeASA1rlzkd8EPIriavpWoiEz1HR/VLhbVIdhqnV6E7JZm00cBDx2A==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.3.1.tgz",
"integrity": "sha512-9RMXwQF8knsZvfv9tdi+4D/j7dMG28X/wMJ8Jj6eOHyHWwDW4ngQJcqEczSsqIKKjFiLFr40Mnr7a5ulDD3vmw==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "7.2.0",
"@typescript-eslint/types": "7.3.1",
"eslint-visitor-keys": "^3.4.1"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
"node": "^18.18.0 || >=20.0.0"
},
"funding": {
"type": "opencollective",
@@ -1447,9 +1447,9 @@
"dev": true
},
"node_modules/@vitest/coverage-v8": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-1.3.1.tgz",
"integrity": "sha512-UuBnkSJUNE9rdHjDCPyJ4fYuMkoMtnghes1XohYa4At0MS3OQSAo97FrbwSLRshYsXThMZy1+ybD/byK5llyIg==",
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-1.4.0.tgz",
"integrity": "sha512-4hDGyH1SvKpgZnIByr9LhGgCEuF9DKM34IBLCC/fVfy24Z3+PZ+Ii9hsVBsHvY1umM1aGPEjceRkzxCfcQ10wg==",
"dev": true,
"dependencies": {
"@ampproject/remapping": "^2.2.1",
@@ -1457,12 +1457,13 @@
"debug": "^4.3.4",
"istanbul-lib-coverage": "^3.2.2",
"istanbul-lib-report": "^3.0.1",
"istanbul-lib-source-maps": "^4.0.1",
"istanbul-lib-source-maps": "^5.0.4",
"istanbul-reports": "^3.1.6",
"magic-string": "^0.30.5",
"magicast": "^0.3.3",
"picocolors": "^1.0.0",
"std-env": "^3.5.0",
"strip-literal": "^2.0.0",
"test-exclude": "^6.0.0",
"v8-to-istanbul": "^9.2.0"
},
@@ -1470,17 +1471,17 @@
"url": "https://opencollective.com/vitest"
},
"peerDependencies": {
"vitest": "1.3.1"
"vitest": "1.4.0"
}
},
"node_modules/@vitest/expect": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.3.1.tgz",
"integrity": "sha512-xofQFwIzfdmLLlHa6ag0dPV8YsnKOCP1KdAeVVh34vSjN2dcUiXYCD9htu/9eM7t8Xln4v03U9HLxLpPlsXdZw==",
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.4.0.tgz",
"integrity": "sha512-Jths0sWCJZ8BxjKe+p+eKsoqev1/T8lYcrjavEaz8auEJ4jAVY0GwW3JKmdVU4mmNPLPHixh4GNXP7GFtAiDHA==",
"dev": true,
"dependencies": {
"@vitest/spy": "1.3.1",
"@vitest/utils": "1.3.1",
"@vitest/spy": "1.4.0",
"@vitest/utils": "1.4.0",
"chai": "^4.3.10"
},
"funding": {
@@ -1488,12 +1489,12 @@
}
},
"node_modules/@vitest/runner": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.3.1.tgz",
"integrity": "sha512-5FzF9c3jG/z5bgCnjr8j9LNq/9OxV2uEBAITOXfoe3rdZJTdO7jzThth7FXv/6b+kdY65tpRQB7WaKhNZwX+Kg==",
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.4.0.tgz",
"integrity": "sha512-EDYVSmesqlQ4RD2VvWo3hQgTJ7ZrFQ2VSJdfiJiArkCerDAGeyF1i6dHkmySqk573jLp6d/cfqCN+7wUB5tLgg==",
"dev": true,
"dependencies": {
"@vitest/utils": "1.3.1",
"@vitest/utils": "1.4.0",
"p-limit": "^5.0.0",
"pathe": "^1.1.1"
},
@@ -1529,9 +1530,9 @@
}
},
"node_modules/@vitest/snapshot": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.3.1.tgz",
"integrity": "sha512-EF++BZbt6RZmOlE3SuTPu/NfwBF6q4ABS37HHXzs2LUVPBLx2QoY/K0fKpRChSo8eLiuxcbCVfqKgx/dplCDuQ==",
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.4.0.tgz",
"integrity": "sha512-saAFnt5pPIA5qDGxOHxJ/XxhMFKkUSBJmVt5VgDsAqPTX6JP326r5C/c9UuCMPoXNzuudTPsYDZCoJ5ilpqG2A==",
"dev": true,
"dependencies": {
"magic-string": "^0.30.5",
@@ -1543,9 +1544,9 @@
}
},
"node_modules/@vitest/spy": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.3.1.tgz",
"integrity": "sha512-xAcW+S099ylC9VLU7eZfdT9myV67Nor9w9zhf0mGCYJSO+zM2839tOeROTdikOi/8Qeusffvxb/MyBSOja1Uig==",
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.4.0.tgz",
"integrity": "sha512-Ywau/Qs1DzM/8Uc+yA77CwSegizMlcgTJuYGAi0jujOteJOUf1ujunHThYo243KG9nAyWT3L9ifPYZ5+As/+6Q==",
"dev": true,
"dependencies": {
"tinyspy": "^2.2.0"
@@ -1555,9 +1556,9 @@
}
},
"node_modules/@vitest/utils": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.3.1.tgz",
"integrity": "sha512-d3Waie/299qqRyHTm2DjADeTaNdNSVsnwHPWrs20JMpjh6eiVq7ggggweO8rc4arhf6rRkWuHKwvxGvejUXZZQ==",
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.4.0.tgz",
"integrity": "sha512-mx3Yd1/6e2Vt/PUC98DcqTirtfxUyAZ32uK82r8rZzbtBeBo+nqgnjx/LvqQdWsrvNtm14VmurNgcf4nqY5gJg==",
"dev": true,
"dependencies": {
"diff-sequences": "^29.6.3",
@@ -2037,9 +2038,9 @@
}
},
"node_modules/esbuild": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz",
"integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz",
"integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==",
"dev": true,
"hasInstallScript": true,
"bin": {
@@ -2049,29 +2050,29 @@
"node": ">=12"
},
"optionalDependencies": {
"@esbuild/aix-ppc64": "0.19.12",
"@esbuild/android-arm": "0.19.12",
"@esbuild/android-arm64": "0.19.12",
"@esbuild/android-x64": "0.19.12",
"@esbuild/darwin-arm64": "0.19.12",
"@esbuild/darwin-x64": "0.19.12",
"@esbuild/freebsd-arm64": "0.19.12",
"@esbuild/freebsd-x64": "0.19.12",
"@esbuild/linux-arm": "0.19.12",
"@esbuild/linux-arm64": "0.19.12",
"@esbuild/linux-ia32": "0.19.12",
"@esbuild/linux-loong64": "0.19.12",
"@esbuild/linux-mips64el": "0.19.12",
"@esbuild/linux-ppc64": "0.19.12",
"@esbuild/linux-riscv64": "0.19.12",
"@esbuild/linux-s390x": "0.19.12",
"@esbuild/linux-x64": "0.19.12",
"@esbuild/netbsd-x64": "0.19.12",
"@esbuild/openbsd-x64": "0.19.12",
"@esbuild/sunos-x64": "0.19.12",
"@esbuild/win32-arm64": "0.19.12",
"@esbuild/win32-ia32": "0.19.12",
"@esbuild/win32-x64": "0.19.12"
"@esbuild/aix-ppc64": "0.20.2",
"@esbuild/android-arm": "0.20.2",
"@esbuild/android-arm64": "0.20.2",
"@esbuild/android-x64": "0.20.2",
"@esbuild/darwin-arm64": "0.20.2",
"@esbuild/darwin-x64": "0.20.2",
"@esbuild/freebsd-arm64": "0.20.2",
"@esbuild/freebsd-x64": "0.20.2",
"@esbuild/linux-arm": "0.20.2",
"@esbuild/linux-arm64": "0.20.2",
"@esbuild/linux-ia32": "0.20.2",
"@esbuild/linux-loong64": "0.20.2",
"@esbuild/linux-mips64el": "0.20.2",
"@esbuild/linux-ppc64": "0.20.2",
"@esbuild/linux-riscv64": "0.20.2",
"@esbuild/linux-s390x": "0.20.2",
"@esbuild/linux-x64": "0.20.2",
"@esbuild/netbsd-x64": "0.20.2",
"@esbuild/openbsd-x64": "0.20.2",
"@esbuild/sunos-x64": "0.20.2",
"@esbuild/win32-arm64": "0.20.2",
"@esbuild/win32-ia32": "0.20.2",
"@esbuild/win32-x64": "0.20.2"
}
},
"node_modules/escalade": {
@@ -2858,14 +2859,14 @@
}
},
"node_modules/istanbul-lib-source-maps": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz",
"integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==",
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.4.tgz",
"integrity": "sha512-wHOoEsNJTVltaJp8eVkm8w+GVkVNHT2YDYo53YdzQEL2gWm1hBX5cGFR9hQJtuGLebidVX7et3+dmDZrmclduw==",
"dev": true,
"dependencies": {
"@jridgewell/trace-mapping": "^0.3.23",
"debug": "^4.1.1",
"istanbul-lib-coverage": "^3.0.0",
"source-map": "^0.6.1"
"istanbul-lib-coverage": "^3.0.0"
},
"engines": {
"node": ">=10"
@@ -3488,9 +3489,9 @@
}
},
"node_modules/postcss": {
"version": "8.4.35",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz",
"integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==",
"version": "8.4.38",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz",
"integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==",
"dev": true,
"funding": [
{
@@ -3509,7 +3510,7 @@
"dependencies": {
"nanoid": "^3.3.7",
"picocolors": "^1.0.0",
"source-map-js": "^1.0.2"
"source-map-js": "^1.2.0"
},
"engines": {
"node": "^10 || ^12 || >=14"
@@ -3987,19 +3988,10 @@
"node": ">=8"
}
},
"node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/source-map-js": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
"integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==",
"dev": true,
"engines": {
"node": ">=0.10.0"
@@ -4376,9 +4368,9 @@
}
},
"node_modules/typescript": {
"version": "5.4.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz",
"integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==",
"version": "5.4.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz",
"integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==",
"dev": true,
"bin": {
"tsc": "bin/tsc",
@@ -4464,14 +4456,14 @@
}
},
"node_modules/vite": {
"version": "5.1.6",
"resolved": "https://registry.npmjs.org/vite/-/vite-5.1.6.tgz",
"integrity": "sha512-yYIAZs9nVfRJ/AiOLCA91zzhjsHUgMjB+EigzFb6W2XTLO8JixBCKCjvhKZaye+NKYHCrkv3Oh50dH9EdLU2RA==",
"version": "5.2.3",
"resolved": "https://registry.npmjs.org/vite/-/vite-5.2.3.tgz",
"integrity": "sha512-+i1oagbvkVIhEy9TnEV+fgXsng13nZM90JQbrcPrf6DvW2mXARlz+DK7DLiDP+qeKoD1FCVx/1SpFL1CLq9Mhw==",
"dev": true,
"dependencies": {
"esbuild": "^0.19.3",
"postcss": "^8.4.35",
"rollup": "^4.2.0"
"esbuild": "^0.20.1",
"postcss": "^8.4.36",
"rollup": "^4.13.0"
},
"bin": {
"vite": "bin/vite.js"
@@ -4519,9 +4511,9 @@
}
},
"node_modules/vite-node": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.3.1.tgz",
"integrity": "sha512-azbRrqRxlWTJEVbzInZCTchx0X69M/XPTCz4H+TLvlTcR/xH/3hkRqhOakT41fMJCMzXTu4UvegkZiEoJAWvng==",
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.4.0.tgz",
"integrity": "sha512-VZDAseqjrHgNd4Kh8icYHWzTKSCZMhia7GyHfhtzLW33fZlG9SwsB6CEhgyVOWkJfJ2pFLrp/Gj1FSfAiqH9Lw==",
"dev": true,
"dependencies": {
"cac": "^6.7.14",
@@ -4560,16 +4552,16 @@
}
},
"node_modules/vitest": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/vitest/-/vitest-1.3.1.tgz",
"integrity": "sha512-/1QJqXs8YbCrfv/GPQ05wAZf2eakUPLPa18vkJAKE7RXOKfVHqMZZ1WlTjiwl6Gcn65M5vpNUB6EFLnEdRdEXQ==",
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/vitest/-/vitest-1.4.0.tgz",
"integrity": "sha512-gujzn0g7fmwf83/WzrDTnncZt2UiXP41mHuFYFrdwaLRVQ6JYQEiME2IfEjU3vcFL3VKa75XhI3lFgn+hfVsQw==",
"dev": true,
"dependencies": {
"@vitest/expect": "1.3.1",
"@vitest/runner": "1.3.1",
"@vitest/snapshot": "1.3.1",
"@vitest/spy": "1.3.1",
"@vitest/utils": "1.3.1",
"@vitest/expect": "1.4.0",
"@vitest/runner": "1.4.0",
"@vitest/snapshot": "1.4.0",
"@vitest/spy": "1.4.0",
"@vitest/utils": "1.4.0",
"acorn-walk": "^8.3.2",
"chai": "^4.3.10",
"debug": "^4.3.4",
@@ -4583,7 +4575,7 @@
"tinybench": "^2.5.1",
"tinypool": "^0.8.2",
"vite": "^5.0.0",
"vite-node": "1.3.1",
"vite-node": "1.4.0",
"why-is-node-running": "^2.2.2"
},
"bin": {
@@ -4598,8 +4590,8 @@
"peerDependencies": {
"@edge-runtime/vm": "*",
"@types/node": "^18.0.0 || >=20.0.0",
"@vitest/browser": "1.3.1",
"@vitest/ui": "1.3.1",
"@vitest/browser": "1.4.0",
"@vitest/ui": "1.4.0",
"happy-dom": "*",
"jsdom": "*"
},
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@immich/cli",
"version": "2.1.0",
"version": "2.2.0",
"description": "Command Line Interface (CLI) for Immich",
"type": "module",
"exports": "./dist/index.js",
+18 -9
View File
@@ -1,15 +1,24 @@
import { getAssetStatistics, getServerVersion, getSupportedMediaTypes } from '@immich/sdk';
import { getAssetStatistics, getMyUserInfo, getServerVersion, getSupportedMediaTypes } from '@immich/sdk';
import { BaseOptions, authenticate } from 'src/utils';
export const serverInfo = async (options: BaseOptions) => {
await authenticate(options);
const { url } = await authenticate(options);
const versionInfo = await getServerVersion();
const mediaTypes = await getSupportedMediaTypes();
const stats = await getAssetStatistics({});
const [versionInfo, mediaTypes, stats, userInfo] = await Promise.all([
getServerVersion(),
getSupportedMediaTypes(),
getAssetStatistics({}),
getMyUserInfo(),
]);
console.log(`Server Version: ${versionInfo.major}.${versionInfo.minor}.${versionInfo.patch}`);
console.log(`Image Types: ${mediaTypes.image.map((extension) => extension.replace('.', ''))}`);
console.log(`Video Types: ${mediaTypes.video.map((extension) => extension.replace('.', ''))}`);
console.log(`Statistics:\n Images: ${stats.images}\n Videos: ${stats.videos}\n Total: ${stats.total}`);
console.log(`Server Info (via ${userInfo.email})`);
console.log(` Url: ${url}`);
console.log(` Version: ${versionInfo.major}.${versionInfo.minor}.${versionInfo.patch}`);
console.log(` Formats:`);
console.log(` Images: ${mediaTypes.image.map((extension) => extension.replace('.', ''))}`);
console.log(` Videos: ${mediaTypes.video.map((extension) => extension.replace('.', ''))}`);
console.log(` Statistics:`);
console.log(` Images: ${stats.images}`);
console.log(` Videos: ${stats.videos}`);
console.log(` Total: ${stats.total}`);
};
+11 -5
View File
@@ -15,21 +15,25 @@ export interface BaseOptions {
export type AuthDto = { url: string; key: string };
type OldAuthDto = { instanceUrl: string; apiKey: string };
export const authenticate = async (options: BaseOptions): Promise<void> => {
export const authenticate = async (options: BaseOptions): Promise<AuthDto> => {
const { configDirectory: configDir, url, key } = options;
// provided in command
if (url && key) {
await connect(url, key);
return;
return connect(url, key);
}
// fallback to auth file
const config = await readAuthFile(configDir);
await connect(config.url, config.key);
const auth = await connect(config.url, config.key);
if (auth.url !== config.url) {
await writeAuthFile(configDir, auth);
}
return auth;
};
export const connect = async (url: string, key: string): Promise<void> => {
export const connect = async (url: string, key: string) => {
const wellKnownUrl = new URL('.well-known/immich', url);
try {
const wellKnown = await fetch(wellKnownUrl).then((response) => response.json());
@@ -50,6 +54,8 @@ export const connect = async (url: string, key: string): Promise<void> => {
logError(error, 'Failed to connect to server');
process.exit(1);
}
return { url, key };
};
export const logError = (error: unknown, message: string) => {
-2
View File
@@ -2,8 +2,6 @@
# - https://immich.app/docs/developer/setup
# - https://immich.app/docs/developer/troubleshooting
version: '3.8'
name: immich-dev
x-server-build: &server-common
-2
View File
@@ -1,5 +1,3 @@
version: '3.8'
name: immich-prod
x-server-build: &server-common
-2
View File
@@ -1,5 +1,3 @@
version: '3.8'
#
# WARNING: Make sure to use the docker-compose.yml of the current release:
#
+1 -1
View File
@@ -6,7 +6,7 @@ Immich can ingest XMP sidecars on file upload (via the CLI) as well as detect ne
XMP sidecars are external XML files that contain metadata related to media files. Many applications read and write these files either exclusively or in addition to the metadata written to image files. They can be a powerful tool for editing and storing metadata of a media file without modifying the media file itself. When Immich receives or detects an XMP sidecar for a media file, it will attempt to extract the metadata from both the sidecar as well as the media file. It will prioritize the metadata for fields in the sidecar but will fall back and use the metadata in the media file if necessary.
When importing files via the CLI bulk uploader, Immich will automatically detect XMP sidecar files as files that exist next to the original media file and have the exact same name with an additional `.xmp` file extension (i.e., `PXL_20230401_203352928.MP.jpg` and `PXL_20230401_203352928.MP.jpg.xmp`).
When importing files via the CLI bulk uploader or parsing photo metadata for external libraries, Immich will automatically detect XMP sidecar files as files that exist next to the original media file. Immich will look files that have the same name as the photo, but with the `.xmp` file extension. The same name can either include the photo's file extension or without the photo's file extension. For example, for a photo named `PXL_20230401_203352928.MP.jpg`, Immich will look for an XMP file named either `PXL_20230401_203352928.MP.jpg.xmp` or `PXL_20230401_203352928.MP.xmp`. If both `PXL_20230401_203352928.MP.jpg.xmp` and `PXL_20230401_203352928.MP.xmp` are present, Immich will prefer `PXL_20230401_203352928.MP.jpg.xmp`.
There are 2 administrator jobs associated with sidecar files: `SYNC` and `DISCOVER`. The sync job will re-scan all media with existing sidecar files and queue them for a metadata refresh. This is a great use case when third-party applications are used to modify the metadata of media. The discover job will attempt to scan the filesystem for new sidecar files for all media that does not currently have a sidecar file associated with it.
+1 -1
View File
@@ -45,7 +45,7 @@ SELECT * FROM "assets" JOIN "exif" ON "assets"."id" = "exif"."assetId" WHERE "ex
```
```sql title="Without thumbnails"
SELECT * FROM "assets" WHERE "assets"."previewPath" IS NULL OR "assets"."thumbnailPath" IS NULL;
SELECT * FROM "assets" WHERE "assets"."resizePath" IS NULL OR "assets"."webpPath" IS NULL;
```
```sql title="By type"
+2 -4
View File
@@ -115,10 +115,8 @@ The default configuration looks like this:
"template": "{{y}}/{{y}}-{{MM}}-{{dd}}/{{filename}}"
},
"thumbnail": {
"thumbnailFormat": "webp",
"thumbnailSize": 250,
"previewFormat": "jpeg",
"previewSize": 1440,
"webpSize": 250,
"jpegSize": 1440,
"quality": 80,
"colorspace": "p3"
},
+3 -3
View File
@@ -21,7 +21,7 @@ cd ./immich-app
Download [`docker-compose.yml`][compose-file] and [`example.env`][env-file], either by running the following commands:
```bash title="Get docker-compose.yml file"
wget https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml
wget -O docker-compose.yml https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml
```
```bash title="Get .env file"
@@ -29,11 +29,11 @@ wget -O .env https://github.com/immich-app/immich/releases/latest/download/examp
```
```bash title="(Optional) Get hwaccel.transcoding.yml file"
wget https://github.com/immich-app/immich/releases/latest/download/hwaccel.transcoding.yml
wget -O hwaccel.transcoding.yml https://github.com/immich-app/immich/releases/latest/download/hwaccel.transcoding.yml
```
```bash title="(Optional) Get hwaccel.ml.yml file"
wget https://github.com/immich-app/immich/releases/latest/download/hwaccel.ml.yml
wget -O hwaccel.ml.yml https://github.com/immich-app/immich/releases/latest/download/hwaccel.ml.yml
```
or by downloading from your browser and moving the files to the directory that you created.
+17 -17
View File
@@ -4264,9 +4264,9 @@
}
},
"node_modules/autoprefixer": {
"version": "10.4.18",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.18.tgz",
"integrity": "sha512-1DKbDfsr6KUElM6wg+0zRNkB/Q7WcKYAaK+pzXn+Xqmszm/5Xa9coeNdtP88Vi+dPzZnMjhge8GIV49ZQkDa+g==",
"version": "10.4.19",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz",
"integrity": "sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==",
"funding": [
{
"type": "opencollective",
@@ -4283,7 +4283,7 @@
],
"dependencies": {
"browserslist": "^4.23.0",
"caniuse-lite": "^1.0.30001591",
"caniuse-lite": "^1.0.30001599",
"fraction.js": "^4.3.7",
"normalize-range": "^0.1.2",
"picocolors": "^1.0.0",
@@ -4728,9 +4728,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001597",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001597.tgz",
"integrity": "sha512-7LjJvmQU6Sj7bL0j5b5WY/3n7utXUJvAe1lxhsHDbLmwX9mdL86Yjtr+5SRCyf8qME4M7pU2hswj0FpyBVCv9w==",
"version": "1.0.30001600",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001600.tgz",
"integrity": "sha512-+2S9/2JFhYmYaDpZvo0lKkfvuKIglrx68MwOBqMGHhQsNkLjB5xtc/TGoEPs+MxjSyN/72qer2g97nzR641mOQ==",
"funding": [
{
"type": "opencollective",
@@ -12691,9 +12691,9 @@
}
},
"node_modules/postcss": {
"version": "8.4.35",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz",
"integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==",
"version": "8.4.38",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz",
"integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==",
"funding": [
{
"type": "opencollective",
@@ -12711,7 +12711,7 @@
"dependencies": {
"nanoid": "^3.3.7",
"picocolors": "^1.0.0",
"source-map-js": "^1.0.2"
"source-map-js": "^1.2.0"
},
"engines": {
"node": "^10 || ^12 || >=14"
@@ -15295,9 +15295,9 @@
}
},
"node_modules/source-map-js": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
"integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==",
"engines": {
"node": ">=0.10.0"
}
@@ -16141,9 +16141,9 @@
}
},
"node_modules/typescript": {
"version": "5.4.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz",
"integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==",
"version": "5.4.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz",
"integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
+60 -60
View File
@@ -38,7 +38,7 @@
},
"../cli": {
"name": "@immich/cli",
"version": "2.1.0",
"version": "2.2.0",
"dev": true,
"license": "GNU Affero General Public License version 3",
"dependencies": {
@@ -1158,9 +1158,9 @@
"dev": true
},
"node_modules/@types/node": {
"version": "20.11.28",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.28.tgz",
"integrity": "sha512-M/GPWVS2wLkSkNHVeLkrF2fD5Lx5UC4PxA0uZcKc6QqbIQUJyW1jVjueJYi1z8n0I5PxYrtpnPnWglE+y9A0KA==",
"version": "20.11.30",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.30.tgz",
"integrity": "sha512-dHM6ZxwlmuZaRmUPfv1p+KrdD1Dci04FbdEm/9wEMouFqxYoFl5aMkt0VMAUtYRQDyYvD41WJLukhq/ha3YuTw==",
"dev": true,
"dependencies": {
"undici-types": "~5.26.4"
@@ -1173,9 +1173,9 @@
"dev": true
},
"node_modules/@types/pg": {
"version": "8.11.2",
"resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.11.2.tgz",
"integrity": "sha512-G2Mjygf2jFMU/9hCaTYxJrwdObdcnuQde1gndooZSOHsNSaCehAuwc7EIuSA34Do8Jx2yZ19KtvW8P0j4EuUXw==",
"version": "8.11.4",
"resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.11.4.tgz",
"integrity": "sha512-yw3Bwbda6vO+NvI1Ue/YKOwtl31AYvvd/e73O3V4ZkNzuGpTDndLSyc0dQRB2xrQqDePd20pEGIfqSp/GH3pRw==",
"dev": true,
"dependencies": {
"@types/node": "*",
@@ -1277,16 +1277,16 @@
}
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.2.0.tgz",
"integrity": "sha512-mdekAHOqS9UjlmyF/LSs6AIEvfceV749GFxoBAjwAv0nkevfKHWQFDMcBZWUiIC5ft6ePWivXoS36aKQ0Cy3sw==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.3.1.tgz",
"integrity": "sha512-STEDMVQGww5lhCuNXVSQfbfuNII5E08QWkvAw5Qwf+bj2WT+JkG1uc+5/vXA3AOYMDHVOSpL+9rcbEUiHIm2dw==",
"dev": true,
"dependencies": {
"@eslint-community/regexpp": "^4.5.1",
"@typescript-eslint/scope-manager": "7.2.0",
"@typescript-eslint/type-utils": "7.2.0",
"@typescript-eslint/utils": "7.2.0",
"@typescript-eslint/visitor-keys": "7.2.0",
"@typescript-eslint/scope-manager": "7.3.1",
"@typescript-eslint/type-utils": "7.3.1",
"@typescript-eslint/utils": "7.3.1",
"@typescript-eslint/visitor-keys": "7.3.1",
"debug": "^4.3.4",
"graphemer": "^1.4.0",
"ignore": "^5.2.4",
@@ -1295,7 +1295,7 @@
"ts-api-utils": "^1.0.1"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
"node": "^18.18.0 || >=20.0.0"
},
"funding": {
"type": "opencollective",
@@ -1312,19 +1312,19 @@
}
},
"node_modules/@typescript-eslint/parser": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.2.0.tgz",
"integrity": "sha512-5FKsVcHTk6TafQKQbuIVkXq58Fnbkd2wDL4LB7AURN7RUOu1utVP+G8+6u3ZhEroW3DF6hyo3ZEXxgKgp4KeCg==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.3.1.tgz",
"integrity": "sha512-Rq49+pq7viTRCH48XAbTA+wdLRrB/3sRq4Lpk0oGDm0VmnjBrAOVXH/Laalmwsv2VpekiEfVFwJYVk6/e8uvQw==",
"dev": true,
"dependencies": {
"@typescript-eslint/scope-manager": "7.2.0",
"@typescript-eslint/types": "7.2.0",
"@typescript-eslint/typescript-estree": "7.2.0",
"@typescript-eslint/visitor-keys": "7.2.0",
"@typescript-eslint/scope-manager": "7.3.1",
"@typescript-eslint/types": "7.3.1",
"@typescript-eslint/typescript-estree": "7.3.1",
"@typescript-eslint/visitor-keys": "7.3.1",
"debug": "^4.3.4"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
"node": "^18.18.0 || >=20.0.0"
},
"funding": {
"type": "opencollective",
@@ -1340,16 +1340,16 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.2.0.tgz",
"integrity": "sha512-Qh976RbQM/fYtjx9hs4XkayYujB/aPwglw2choHmf3zBjB4qOywWSdt9+KLRdHubGcoSwBnXUH2sR3hkyaERRg==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.3.1.tgz",
"integrity": "sha512-fVS6fPxldsKY2nFvyT7IP78UO1/I2huG+AYu5AMjCT9wtl6JFiDnsv4uad4jQ0GTFzcUV5HShVeN96/17bTBag==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "7.2.0",
"@typescript-eslint/visitor-keys": "7.2.0"
"@typescript-eslint/types": "7.3.1",
"@typescript-eslint/visitor-keys": "7.3.1"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
"node": "^18.18.0 || >=20.0.0"
},
"funding": {
"type": "opencollective",
@@ -1357,18 +1357,18 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.2.0.tgz",
"integrity": "sha512-xHi51adBHo9O9330J8GQYQwrKBqbIPJGZZVQTHHmy200hvkLZFWJIFtAG/7IYTWUyun6DE6w5InDReePJYJlJA==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.3.1.tgz",
"integrity": "sha512-iFhaysxFsMDQlzJn+vr3OrxN8NmdQkHks4WaqD4QBnt5hsq234wcYdyQ9uquzJJIDAj5W4wQne3yEsYA6OmXGw==",
"dev": true,
"dependencies": {
"@typescript-eslint/typescript-estree": "7.2.0",
"@typescript-eslint/utils": "7.2.0",
"@typescript-eslint/typescript-estree": "7.3.1",
"@typescript-eslint/utils": "7.3.1",
"debug": "^4.3.4",
"ts-api-utils": "^1.0.1"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
"node": "^18.18.0 || >=20.0.0"
},
"funding": {
"type": "opencollective",
@@ -1384,12 +1384,12 @@
}
},
"node_modules/@typescript-eslint/types": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.2.0.tgz",
"integrity": "sha512-XFtUHPI/abFhm4cbCDc5Ykc8npOKBSJePY3a3s+lwumt7XWJuzP5cZcfZ610MIPHjQjNsOLlYK8ASPaNG8UiyA==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.3.1.tgz",
"integrity": "sha512-2tUf3uWggBDl4S4183nivWQ2HqceOZh1U4hhu4p1tPiIJoRRXrab7Y+Y0p+dozYwZVvLPRI6r5wKe9kToF9FIw==",
"dev": true,
"engines": {
"node": "^16.0.0 || >=18.0.0"
"node": "^18.18.0 || >=20.0.0"
},
"funding": {
"type": "opencollective",
@@ -1397,13 +1397,13 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.2.0.tgz",
"integrity": "sha512-cyxS5WQQCoBwSakpMrvMXuMDEbhOo9bNHHrNcEWis6XHx6KF518tkF1wBvKIn/tpq5ZpUYK7Bdklu8qY0MsFIA==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.3.1.tgz",
"integrity": "sha512-tLpuqM46LVkduWP7JO7yVoWshpJuJzxDOPYIVWUUZbW+4dBpgGeUdl/fQkhuV0A8eGnphYw3pp8d2EnvPOfxmQ==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "7.2.0",
"@typescript-eslint/visitor-keys": "7.2.0",
"@typescript-eslint/types": "7.3.1",
"@typescript-eslint/visitor-keys": "7.3.1",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
@@ -1412,7 +1412,7 @@
"ts-api-utils": "^1.0.1"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
"node": "^18.18.0 || >=20.0.0"
},
"funding": {
"type": "opencollective",
@@ -1449,21 +1449,21 @@
}
},
"node_modules/@typescript-eslint/utils": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.2.0.tgz",
"integrity": "sha512-YfHpnMAGb1Eekpm3XRK8hcMwGLGsnT6L+7b2XyRv6ouDuJU1tZir1GS2i0+VXRatMwSI1/UfcyPe53ADkU+IuA==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.3.1.tgz",
"integrity": "sha512-jIERm/6bYQ9HkynYlNZvXpzmXWZGhMbrOvq3jJzOSOlKXsVjrrolzWBjDW6/TvT5Q3WqaN4EkmcfdQwi9tDjBQ==",
"dev": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.4.0",
"@types/json-schema": "^7.0.12",
"@types/semver": "^7.5.0",
"@typescript-eslint/scope-manager": "7.2.0",
"@typescript-eslint/types": "7.2.0",
"@typescript-eslint/typescript-estree": "7.2.0",
"@typescript-eslint/scope-manager": "7.3.1",
"@typescript-eslint/types": "7.3.1",
"@typescript-eslint/typescript-estree": "7.3.1",
"semver": "^7.5.4"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
"node": "^18.18.0 || >=20.0.0"
},
"funding": {
"type": "opencollective",
@@ -1474,16 +1474,16 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.2.0.tgz",
"integrity": "sha512-c6EIQRHhcpl6+tO8EMR+kjkkV+ugUNXOmeASA1rlzkd8EPIriavpWoiEz1HR/VLhbVIdhqnV6E7JZm00cBDx2A==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.3.1.tgz",
"integrity": "sha512-9RMXwQF8knsZvfv9tdi+4D/j7dMG28X/wMJ8Jj6eOHyHWwDW4ngQJcqEczSsqIKKjFiLFr40Mnr7a5ulDD3vmw==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "7.2.0",
"@typescript-eslint/types": "7.3.1",
"eslint-visitor-keys": "^3.4.1"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
"node": "^18.18.0 || >=20.0.0"
},
"funding": {
"type": "opencollective",
@@ -4790,9 +4790,9 @@
}
},
"node_modules/typescript": {
"version": "5.4.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz",
"integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==",
"version": "5.4.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz",
"integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==",
"dev": true,
"bin": {
"tsc": "bin/tsc",
+10 -7
View File
@@ -11,13 +11,16 @@ describe(`immich server-info`, () => {
it('should return the server info', async () => {
const { stderr, stdout, exitCode } = await immichCli(['server-info']);
expect(stdout.split('\n')).toEqual([
expect.stringContaining('Server Version:'),
expect.stringContaining('Image Types:'),
expect.stringContaining('Video Types:'),
'Statistics:',
' Images: 0',
' Videos: 0',
' Total: 0',
expect.stringContaining('Server Info (via admin@immich.cloud'),
' Url: http://127.0.0.1:2283/api',
expect.stringContaining('Version:'),
' Formats:',
expect.stringContaining('Images:'),
expect.stringContaining('Videos:'),
' Statistics:',
' Images: 0',
' Videos: 0',
' Total: 0',
]);
expect(stderr).toBe('');
expect(exitCode).toBe(0);
-3
View File
@@ -72,7 +72,6 @@ doc/FileChecksumResponseDto.md
doc/FileReportDto.md
doc/FileReportFixDto.md
doc/FileReportItemDto.md
doc/ImageFormat.md
doc/JobApi.md
doc/JobCommand.md
doc/JobCommandDto.md
@@ -283,7 +282,6 @@ lib/model/file_checksum_response_dto.dart
lib/model/file_report_dto.dart
lib/model/file_report_fix_dto.dart
lib/model/file_report_item_dto.dart
lib/model/image_format.dart
lib/model/job_command.dart
lib/model/job_command_dto.dart
lib/model/job_counts_dto.dart
@@ -461,7 +459,6 @@ test/file_checksum_response_dto_test.dart
test/file_report_dto_test.dart
test/file_report_fix_dto_test.dart
test/file_report_item_dto_test.dart
test/image_format_test.dart
test/job_api_test.dart
test/job_command_dto_test.dart
test/job_command_test.dart
-1
View File
@@ -277,7 +277,6 @@ Class | Method | HTTP request | Description
- [FileReportDto](doc//FileReportDto.md)
- [FileReportFixDto](doc//FileReportFixDto.md)
- [FileReportItemDto](doc//FileReportItemDto.md)
- [ImageFormat](doc//ImageFormat.md)
- [JobCommand](doc//JobCommand.md)
- [JobCommandDto](doc//JobCommandDto.md)
- [JobCountsDto](doc//JobCountsDto.md)
+6 -6
View File
@@ -1040,7 +1040,7 @@ void (empty response body)
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
# **searchAssets**
> List<AssetResponseDto> searchAssets(checksum, city, country, createdAfter, createdBefore, deviceAssetId, deviceId, encodedVideoPath, id, isArchived, isEncoded, isExternal, isFavorite, isMotion, isNotInAlbum, isOffline, isReadOnly, isVisible, lensModel, libraryId, make, model, order, originalFileName, originalPath, page, personIds, previewPath, size, state, takenAfter, takenBefore, thumbnailPath, trashedAfter, trashedBefore, type, updatedAfter, updatedBefore, withArchived, withDeleted, withExif, withPeople, withStacked)
> List<AssetResponseDto> searchAssets(checksum, city, country, createdAfter, createdBefore, deviceAssetId, deviceId, encodedVideoPath, id, isArchived, isEncoded, isExternal, isFavorite, isMotion, isNotInAlbum, isOffline, isReadOnly, isVisible, lensModel, libraryId, make, model, order, originalFileName, originalPath, page, personIds, resizePath, size, state, takenAfter, takenBefore, trashedAfter, trashedBefore, type, updatedAfter, updatedBefore, webpPath, withArchived, withDeleted, withExif, withPeople, withStacked)
@@ -1090,17 +1090,17 @@ final originalFileName = originalFileName_example; // String |
final originalPath = originalPath_example; // String |
final page = 8.14; // num |
final personIds = []; // List<String> |
final previewPath = previewPath_example; // String |
final resizePath = resizePath_example; // String |
final size = 8.14; // num |
final state = state_example; // String |
final takenAfter = 2013-10-20T19:20:30+01:00; // DateTime |
final takenBefore = 2013-10-20T19:20:30+01:00; // DateTime |
final thumbnailPath = thumbnailPath_example; // String |
final trashedAfter = 2013-10-20T19:20:30+01:00; // DateTime |
final trashedBefore = 2013-10-20T19:20:30+01:00; // DateTime |
final type = ; // AssetTypeEnum |
final updatedAfter = 2013-10-20T19:20:30+01:00; // DateTime |
final updatedBefore = 2013-10-20T19:20:30+01:00; // DateTime |
final webpPath = webpPath_example; // String |
final withArchived = true; // bool |
final withDeleted = true; // bool |
final withExif = true; // bool |
@@ -1108,7 +1108,7 @@ final withPeople = true; // bool |
final withStacked = true; // bool |
try {
final result = api_instance.searchAssets(checksum, city, country, createdAfter, createdBefore, deviceAssetId, deviceId, encodedVideoPath, id, isArchived, isEncoded, isExternal, isFavorite, isMotion, isNotInAlbum, isOffline, isReadOnly, isVisible, lensModel, libraryId, make, model, order, originalFileName, originalPath, page, personIds, previewPath, size, state, takenAfter, takenBefore, thumbnailPath, trashedAfter, trashedBefore, type, updatedAfter, updatedBefore, withArchived, withDeleted, withExif, withPeople, withStacked);
final result = api_instance.searchAssets(checksum, city, country, createdAfter, createdBefore, deviceAssetId, deviceId, encodedVideoPath, id, isArchived, isEncoded, isExternal, isFavorite, isMotion, isNotInAlbum, isOffline, isReadOnly, isVisible, lensModel, libraryId, make, model, order, originalFileName, originalPath, page, personIds, resizePath, size, state, takenAfter, takenBefore, trashedAfter, trashedBefore, type, updatedAfter, updatedBefore, webpPath, withArchived, withDeleted, withExif, withPeople, withStacked);
print(result);
} catch (e) {
print('Exception when calling AssetApi->searchAssets: $e\n');
@@ -1146,17 +1146,17 @@ Name | Type | Description | Notes
**originalPath** | **String**| | [optional]
**page** | **num**| | [optional]
**personIds** | [**List<String>**](String.md)| | [optional] [default to const []]
**previewPath** | **String**| | [optional]
**resizePath** | **String**| | [optional]
**size** | **num**| | [optional]
**state** | **String**| | [optional]
**takenAfter** | **DateTime**| | [optional]
**takenBefore** | **DateTime**| | [optional]
**thumbnailPath** | **String**| | [optional]
**trashedAfter** | **DateTime**| | [optional]
**trashedBefore** | **DateTime**| | [optional]
**type** | [**AssetTypeEnum**](.md)| | [optional]
**updatedAfter** | **DateTime**| | [optional]
**updatedBefore** | **DateTime**| | [optional]
**webpPath** | **String**| | [optional]
**withArchived** | **bool**| | [optional] [default to false]
**withDeleted** | **bool**| | [optional]
**withExif** | **bool**| | [optional]
+1 -1
View File
@@ -12,7 +12,7 @@ Name | Type | Description | Notes
**deviceAssetId** | **String** | |
**deviceId** | **String** | |
**id** | **String** | |
**previewPath** | **String** | |
**resizePath** | **String** | |
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+1 -1
View File
@@ -12,7 +12,7 @@ Name | Type | Description | Notes
**deviceId** | **String** | |
**id** | **String** | |
**object** | **String** | |
**previewPath** | **String** | |
**resizePath** | **String** | |
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
-14
View File
@@ -1,14 +0,0 @@
# openapi.model.ImageFormat
## Load the model package
```dart
import 'package:openapi/api.dart';
```
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+1
View File
@@ -10,6 +10,7 @@ Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**assets** | [**List<AssetResponseDto>**](AssetResponseDto.md) | | [default to const []]
**title** | **String** | |
**yearsAgo** | **int** | |
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+2 -2
View File
@@ -35,17 +35,17 @@ Name | Type | Description | Notes
**originalPath** | **String** | | [optional]
**page** | **num** | | [optional]
**personIds** | **List<String>** | | [optional] [default to const []]
**previewPath** | **String** | | [optional]
**resizePath** | **String** | | [optional]
**size** | **num** | | [optional]
**state** | **String** | | [optional]
**takenAfter** | [**DateTime**](DateTime.md) | | [optional]
**takenBefore** | [**DateTime**](DateTime.md) | | [optional]
**thumbnailPath** | **String** | | [optional]
**trashedAfter** | [**DateTime**](DateTime.md) | | [optional]
**trashedBefore** | [**DateTime**](DateTime.md) | | [optional]
**type** | [**AssetTypeEnum**](AssetTypeEnum.md) | | [optional]
**updatedAfter** | [**DateTime**](DateTime.md) | | [optional]
**updatedBefore** | [**DateTime**](DateTime.md) | | [optional]
**webpPath** | **String** | | [optional]
**withArchived** | **bool** | | [optional] [default to false]
**withDeleted** | **bool** | | [optional]
**withExif** | **bool** | | [optional]
+1 -1
View File
@@ -9,7 +9,6 @@ import 'package:openapi/api.dart';
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**ffmpeg** | [**SystemConfigFFmpegDto**](SystemConfigFFmpegDto.md) | |
**image** | [**SystemConfigThumbnailDto**](SystemConfigThumbnailDto.md) | |
**job** | [**SystemConfigJobDto**](SystemConfigJobDto.md) | |
**library_** | [**SystemConfigLibraryDto**](SystemConfigLibraryDto.md) | |
**logging** | [**SystemConfigLoggingDto**](SystemConfigLoggingDto.md) | |
@@ -22,6 +21,7 @@ Name | Type | Description | Notes
**server** | [**SystemConfigServerDto**](SystemConfigServerDto.md) | |
**storageTemplate** | [**SystemConfigStorageTemplateDto**](SystemConfigStorageTemplateDto.md) | |
**theme** | [**SystemConfigThemeDto**](SystemConfigThemeDto.md) | |
**thumbnail** | [**SystemConfigThumbnailDto**](SystemConfigThumbnailDto.md) | |
**trash** | [**SystemConfigTrashDto**](SystemConfigTrashDto.md) | |
**user** | [**SystemConfigUserDto**](SystemConfigUserDto.md) | |
+2 -4
View File
@@ -9,11 +9,9 @@ import 'package:openapi/api.dart';
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**colorspace** | [**Colorspace**](Colorspace.md) | |
**previewFormat** | [**ImageFormat**](ImageFormat.md) | |
**previewSize** | **int** | |
**jpegSize** | **int** | |
**quality** | **int** | |
**thumbnailFormat** | [**ImageFormat**](ImageFormat.md) | |
**thumbnailSize** | **int** | |
**webpSize** | **int** | |
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
-1
View File
@@ -111,7 +111,6 @@ part 'model/file_checksum_response_dto.dart';
part 'model/file_report_dto.dart';
part 'model/file_report_fix_dto.dart';
part 'model/file_report_item_dto.dart';
part 'model/image_format.dart';
part 'model/job_command.dart';
part 'model/job_command_dto.dart';
part 'model/job_counts_dto.dart';
+14 -14
View File
@@ -1180,7 +1180,7 @@ class AssetApi {
///
/// * [List<String>] personIds:
///
/// * [String] previewPath:
/// * [String] resizePath:
///
/// * [num] size:
///
@@ -1190,8 +1190,6 @@ class AssetApi {
///
/// * [DateTime] takenBefore:
///
/// * [String] thumbnailPath:
///
/// * [DateTime] trashedAfter:
///
/// * [DateTime] trashedBefore:
@@ -1202,6 +1200,8 @@ class AssetApi {
///
/// * [DateTime] updatedBefore:
///
/// * [String] webpPath:
///
/// * [bool] withArchived:
///
/// * [bool] withDeleted:
@@ -1211,7 +1211,7 @@ class AssetApi {
/// * [bool] withPeople:
///
/// * [bool] withStacked:
Future<Response> searchAssetsWithHttpInfo({ String? checksum, String? city, String? country, DateTime? createdAfter, DateTime? createdBefore, String? deviceAssetId, String? deviceId, String? encodedVideoPath, String? id, bool? isArchived, bool? isEncoded, bool? isExternal, bool? isFavorite, bool? isMotion, bool? isNotInAlbum, bool? isOffline, bool? isReadOnly, bool? isVisible, String? lensModel, String? libraryId, String? make, String? model, AssetOrder? order, String? originalFileName, String? originalPath, num? page, List<String>? personIds, String? previewPath, num? size, String? state, DateTime? takenAfter, DateTime? takenBefore, String? thumbnailPath, DateTime? trashedAfter, DateTime? trashedBefore, AssetTypeEnum? type, DateTime? updatedAfter, DateTime? updatedBefore, bool? withArchived, bool? withDeleted, bool? withExif, bool? withPeople, bool? withStacked, }) async {
Future<Response> searchAssetsWithHttpInfo({ String? checksum, String? city, String? country, DateTime? createdAfter, DateTime? createdBefore, String? deviceAssetId, String? deviceId, String? encodedVideoPath, String? id, bool? isArchived, bool? isEncoded, bool? isExternal, bool? isFavorite, bool? isMotion, bool? isNotInAlbum, bool? isOffline, bool? isReadOnly, bool? isVisible, String? lensModel, String? libraryId, String? make, String? model, AssetOrder? order, String? originalFileName, String? originalPath, num? page, List<String>? personIds, String? resizePath, num? size, String? state, DateTime? takenAfter, DateTime? takenBefore, DateTime? trashedAfter, DateTime? trashedBefore, AssetTypeEnum? type, DateTime? updatedAfter, DateTime? updatedBefore, String? webpPath, bool? withArchived, bool? withDeleted, bool? withExif, bool? withPeople, bool? withStacked, }) async {
// ignore: prefer_const_declarations
final path = r'/assets';
@@ -1303,8 +1303,8 @@ class AssetApi {
if (personIds != null) {
queryParams.addAll(_queryParams('multi', 'personIds', personIds));
}
if (previewPath != null) {
queryParams.addAll(_queryParams('', 'previewPath', previewPath));
if (resizePath != null) {
queryParams.addAll(_queryParams('', 'resizePath', resizePath));
}
if (size != null) {
queryParams.addAll(_queryParams('', 'size', size));
@@ -1318,9 +1318,6 @@ class AssetApi {
if (takenBefore != null) {
queryParams.addAll(_queryParams('', 'takenBefore', takenBefore));
}
if (thumbnailPath != null) {
queryParams.addAll(_queryParams('', 'thumbnailPath', thumbnailPath));
}
if (trashedAfter != null) {
queryParams.addAll(_queryParams('', 'trashedAfter', trashedAfter));
}
@@ -1336,6 +1333,9 @@ class AssetApi {
if (updatedBefore != null) {
queryParams.addAll(_queryParams('', 'updatedBefore', updatedBefore));
}
if (webpPath != null) {
queryParams.addAll(_queryParams('', 'webpPath', webpPath));
}
if (withArchived != null) {
queryParams.addAll(_queryParams('', 'withArchived', withArchived));
}
@@ -1422,7 +1422,7 @@ class AssetApi {
///
/// * [List<String>] personIds:
///
/// * [String] previewPath:
/// * [String] resizePath:
///
/// * [num] size:
///
@@ -1432,8 +1432,6 @@ class AssetApi {
///
/// * [DateTime] takenBefore:
///
/// * [String] thumbnailPath:
///
/// * [DateTime] trashedAfter:
///
/// * [DateTime] trashedBefore:
@@ -1444,6 +1442,8 @@ class AssetApi {
///
/// * [DateTime] updatedBefore:
///
/// * [String] webpPath:
///
/// * [bool] withArchived:
///
/// * [bool] withDeleted:
@@ -1453,8 +1453,8 @@ class AssetApi {
/// * [bool] withPeople:
///
/// * [bool] withStacked:
Future<List<AssetResponseDto>?> searchAssets({ String? checksum, String? city, String? country, DateTime? createdAfter, DateTime? createdBefore, String? deviceAssetId, String? deviceId, String? encodedVideoPath, String? id, bool? isArchived, bool? isEncoded, bool? isExternal, bool? isFavorite, bool? isMotion, bool? isNotInAlbum, bool? isOffline, bool? isReadOnly, bool? isVisible, String? lensModel, String? libraryId, String? make, String? model, AssetOrder? order, String? originalFileName, String? originalPath, num? page, List<String>? personIds, String? previewPath, num? size, String? state, DateTime? takenAfter, DateTime? takenBefore, String? thumbnailPath, DateTime? trashedAfter, DateTime? trashedBefore, AssetTypeEnum? type, DateTime? updatedAfter, DateTime? updatedBefore, bool? withArchived, bool? withDeleted, bool? withExif, bool? withPeople, bool? withStacked, }) async {
final response = await searchAssetsWithHttpInfo( checksum: checksum, city: city, country: country, createdAfter: createdAfter, createdBefore: createdBefore, deviceAssetId: deviceAssetId, deviceId: deviceId, encodedVideoPath: encodedVideoPath, id: id, isArchived: isArchived, isEncoded: isEncoded, isExternal: isExternal, isFavorite: isFavorite, isMotion: isMotion, isNotInAlbum: isNotInAlbum, isOffline: isOffline, isReadOnly: isReadOnly, isVisible: isVisible, lensModel: lensModel, libraryId: libraryId, make: make, model: model, order: order, originalFileName: originalFileName, originalPath: originalPath, page: page, personIds: personIds, previewPath: previewPath, size: size, state: state, takenAfter: takenAfter, takenBefore: takenBefore, thumbnailPath: thumbnailPath, trashedAfter: trashedAfter, trashedBefore: trashedBefore, type: type, updatedAfter: updatedAfter, updatedBefore: updatedBefore, withArchived: withArchived, withDeleted: withDeleted, withExif: withExif, withPeople: withPeople, withStacked: withStacked, );
Future<List<AssetResponseDto>?> searchAssets({ String? checksum, String? city, String? country, DateTime? createdAfter, DateTime? createdBefore, String? deviceAssetId, String? deviceId, String? encodedVideoPath, String? id, bool? isArchived, bool? isEncoded, bool? isExternal, bool? isFavorite, bool? isMotion, bool? isNotInAlbum, bool? isOffline, bool? isReadOnly, bool? isVisible, String? lensModel, String? libraryId, String? make, String? model, AssetOrder? order, String? originalFileName, String? originalPath, num? page, List<String>? personIds, String? resizePath, num? size, String? state, DateTime? takenAfter, DateTime? takenBefore, DateTime? trashedAfter, DateTime? trashedBefore, AssetTypeEnum? type, DateTime? updatedAfter, DateTime? updatedBefore, String? webpPath, bool? withArchived, bool? withDeleted, bool? withExif, bool? withPeople, bool? withStacked, }) async {
final response = await searchAssetsWithHttpInfo( checksum: checksum, city: city, country: country, createdAfter: createdAfter, createdBefore: createdBefore, deviceAssetId: deviceAssetId, deviceId: deviceId, encodedVideoPath: encodedVideoPath, id: id, isArchived: isArchived, isEncoded: isEncoded, isExternal: isExternal, isFavorite: isFavorite, isMotion: isMotion, isNotInAlbum: isNotInAlbum, isOffline: isOffline, isReadOnly: isReadOnly, isVisible: isVisible, lensModel: lensModel, libraryId: libraryId, make: make, model: model, order: order, originalFileName: originalFileName, originalPath: originalPath, page: page, personIds: personIds, resizePath: resizePath, size: size, state: state, takenAfter: takenAfter, takenBefore: takenBefore, trashedAfter: trashedAfter, trashedBefore: trashedBefore, type: type, updatedAfter: updatedAfter, updatedBefore: updatedBefore, webpPath: webpPath, withArchived: withArchived, withDeleted: withDeleted, withExif: withExif, withPeople: withPeople, withStacked: withStacked, );
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
-2
View File
@@ -304,8 +304,6 @@ class ApiClient {
return FileReportFixDto.fromJson(value);
case 'FileReportItemDto':
return FileReportItemDto.fromJson(value);
case 'ImageFormat':
return ImageFormatTypeTransformer().decode(value);
case 'JobCommand':
return JobCommandTypeTransformer().decode(value);
case 'JobCommandDto':
-3
View File
@@ -79,9 +79,6 @@ String parameterToString(dynamic value) {
if (value is EntityType) {
return EntityTypeTypeTransformer().encode(value).toString();
}
if (value is ImageFormat) {
return ImageFormatTypeTransformer().encode(value).toString();
}
if (value is JobCommand) {
return JobCommandTypeTransformer().encode(value).toString();
}
@@ -17,7 +17,7 @@ class CuratedLocationsResponseDto {
required this.deviceAssetId,
required this.deviceId,
required this.id,
required this.previewPath,
required this.resizePath,
});
String city;
@@ -28,7 +28,7 @@ class CuratedLocationsResponseDto {
String id;
String previewPath;
String resizePath;
@override
bool operator ==(Object other) => identical(this, other) || other is CuratedLocationsResponseDto &&
@@ -36,7 +36,7 @@ class CuratedLocationsResponseDto {
other.deviceAssetId == deviceAssetId &&
other.deviceId == deviceId &&
other.id == id &&
other.previewPath == previewPath;
other.resizePath == resizePath;
@override
int get hashCode =>
@@ -45,10 +45,10 @@ class CuratedLocationsResponseDto {
(deviceAssetId.hashCode) +
(deviceId.hashCode) +
(id.hashCode) +
(previewPath.hashCode);
(resizePath.hashCode);
@override
String toString() => 'CuratedLocationsResponseDto[city=$city, deviceAssetId=$deviceAssetId, deviceId=$deviceId, id=$id, previewPath=$previewPath]';
String toString() => 'CuratedLocationsResponseDto[city=$city, deviceAssetId=$deviceAssetId, deviceId=$deviceId, id=$id, resizePath=$resizePath]';
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
@@ -56,7 +56,7 @@ class CuratedLocationsResponseDto {
json[r'deviceAssetId'] = this.deviceAssetId;
json[r'deviceId'] = this.deviceId;
json[r'id'] = this.id;
json[r'previewPath'] = this.previewPath;
json[r'resizePath'] = this.resizePath;
return json;
}
@@ -72,7 +72,7 @@ class CuratedLocationsResponseDto {
deviceAssetId: mapValueOfType<String>(json, r'deviceAssetId')!,
deviceId: mapValueOfType<String>(json, r'deviceId')!,
id: mapValueOfType<String>(json, r'id')!,
previewPath: mapValueOfType<String>(json, r'previewPath')!,
resizePath: mapValueOfType<String>(json, r'resizePath')!,
);
}
return null;
@@ -124,7 +124,7 @@ class CuratedLocationsResponseDto {
'deviceAssetId',
'deviceId',
'id',
'previewPath',
'resizePath',
};
}
+8 -8
View File
@@ -17,7 +17,7 @@ class CuratedObjectsResponseDto {
required this.deviceId,
required this.id,
required this.object,
required this.previewPath,
required this.resizePath,
});
String deviceAssetId;
@@ -28,7 +28,7 @@ class CuratedObjectsResponseDto {
String object;
String previewPath;
String resizePath;
@override
bool operator ==(Object other) => identical(this, other) || other is CuratedObjectsResponseDto &&
@@ -36,7 +36,7 @@ class CuratedObjectsResponseDto {
other.deviceId == deviceId &&
other.id == id &&
other.object == object &&
other.previewPath == previewPath;
other.resizePath == resizePath;
@override
int get hashCode =>
@@ -45,10 +45,10 @@ class CuratedObjectsResponseDto {
(deviceId.hashCode) +
(id.hashCode) +
(object.hashCode) +
(previewPath.hashCode);
(resizePath.hashCode);
@override
String toString() => 'CuratedObjectsResponseDto[deviceAssetId=$deviceAssetId, deviceId=$deviceId, id=$id, object=$object, previewPath=$previewPath]';
String toString() => 'CuratedObjectsResponseDto[deviceAssetId=$deviceAssetId, deviceId=$deviceId, id=$id, object=$object, resizePath=$resizePath]';
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
@@ -56,7 +56,7 @@ class CuratedObjectsResponseDto {
json[r'deviceId'] = this.deviceId;
json[r'id'] = this.id;
json[r'object'] = this.object;
json[r'previewPath'] = this.previewPath;
json[r'resizePath'] = this.resizePath;
return json;
}
@@ -72,7 +72,7 @@ class CuratedObjectsResponseDto {
deviceId: mapValueOfType<String>(json, r'deviceId')!,
id: mapValueOfType<String>(json, r'id')!,
object: mapValueOfType<String>(json, r'object')!,
previewPath: mapValueOfType<String>(json, r'previewPath')!,
resizePath: mapValueOfType<String>(json, r'resizePath')!,
);
}
return null;
@@ -124,7 +124,7 @@ class CuratedObjectsResponseDto {
'deviceId',
'id',
'object',
'previewPath',
'resizePath',
};
}
-85
View File
@@ -1,85 +0,0 @@
//
// AUTO-GENERATED FILE, DO NOT MODIFY!
//
// @dart=2.12
// ignore_for_file: unused_element, unused_import
// ignore_for_file: always_put_required_named_parameters_first
// ignore_for_file: constant_identifier_names
// ignore_for_file: lines_longer_than_80_chars
part of openapi.api;
class ImageFormat {
/// Instantiate a new enum with the provided [value].
const ImageFormat._(this.value);
/// The underlying value of this enum member.
final String value;
@override
String toString() => value;
String toJson() => value;
static const jpeg = ImageFormat._(r'jpeg');
static const webp = ImageFormat._(r'webp');
/// List of all possible values in this [enum][ImageFormat].
static const values = <ImageFormat>[
jpeg,
webp,
];
static ImageFormat? fromJson(dynamic value) => ImageFormatTypeTransformer().decode(value);
static List<ImageFormat> listFromJson(dynamic json, {bool growable = false,}) {
final result = <ImageFormat>[];
if (json is List && json.isNotEmpty) {
for (final row in json) {
final value = ImageFormat.fromJson(row);
if (value != null) {
result.add(value);
}
}
}
return result.toList(growable: growable);
}
}
/// Transformation class that can [encode] an instance of [ImageFormat] to String,
/// and [decode] dynamic data back to [ImageFormat].
class ImageFormatTypeTransformer {
factory ImageFormatTypeTransformer() => _instance ??= const ImageFormatTypeTransformer._();
const ImageFormatTypeTransformer._();
String encode(ImageFormat data) => data.value;
/// Decodes a [dynamic value][data] to a ImageFormat.
///
/// If [allowNull] is true and the [dynamic value][data] cannot be decoded successfully,
/// then null is returned. However, if [allowNull] is false and the [dynamic value][data]
/// cannot be decoded successfully, then an [UnimplementedError] is thrown.
///
/// The [allowNull] is very handy when an API changes and a new enum value is added or removed,
/// and users are still using an old app with the old code.
ImageFormat? decode(dynamic data, {bool allowNull = true}) {
if (data != null) {
switch (data) {
case r'jpeg': return ImageFormat.jpeg;
case r'webp': return ImageFormat.webp;
default:
if (!allowNull) {
throw ArgumentError('Unknown enum value to decode: $data');
}
}
}
return null;
}
/// Singleton [ImageFormatTypeTransformer] instance.
static ImageFormatTypeTransformer? _instance;
}
+11 -3
View File
@@ -15,30 +15,36 @@ class MemoryLaneResponseDto {
MemoryLaneResponseDto({
this.assets = const [],
required this.title,
required this.yearsAgo,
});
List<AssetResponseDto> assets;
String title;
int yearsAgo;
@override
bool operator ==(Object other) => identical(this, other) || other is MemoryLaneResponseDto &&
_deepEquality.equals(other.assets, assets) &&
other.title == title;
other.title == title &&
other.yearsAgo == yearsAgo;
@override
int get hashCode =>
// ignore: unnecessary_parenthesis
(assets.hashCode) +
(title.hashCode);
(title.hashCode) +
(yearsAgo.hashCode);
@override
String toString() => 'MemoryLaneResponseDto[assets=$assets, title=$title]';
String toString() => 'MemoryLaneResponseDto[assets=$assets, title=$title, yearsAgo=$yearsAgo]';
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
json[r'assets'] = this.assets;
json[r'title'] = this.title;
json[r'yearsAgo'] = this.yearsAgo;
return json;
}
@@ -52,6 +58,7 @@ class MemoryLaneResponseDto {
return MemoryLaneResponseDto(
assets: AssetResponseDto.listFromJson(json[r'assets']),
title: mapValueOfType<String>(json, r'title')!,
yearsAgo: mapValueOfType<int>(json, r'yearsAgo')!,
);
}
return null;
@@ -101,6 +108,7 @@ class MemoryLaneResponseDto {
static const requiredKeys = <String>{
'assets',
'title',
'yearsAgo',
};
}
+26 -26
View File
@@ -40,17 +40,17 @@ class MetadataSearchDto {
this.originalPath,
this.page,
this.personIds = const [],
this.previewPath,
this.resizePath,
this.size,
this.state,
this.takenAfter,
this.takenBefore,
this.thumbnailPath,
this.trashedAfter,
this.trashedBefore,
this.type,
this.updatedAfter,
this.updatedBefore,
this.webpPath,
this.withArchived = false,
this.withDeleted,
this.withExif,
@@ -274,7 +274,7 @@ class MetadataSearchDto {
/// source code must fall back to having a nullable type.
/// Consider adding a "default:" property in the specification file to hide this note.
///
String? previewPath;
String? resizePath;
///
/// Please note: This property should have been non-nullable! Since the specification file
@@ -308,14 +308,6 @@ class MetadataSearchDto {
///
DateTime? takenBefore;
///
/// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated
/// source code must fall back to having a nullable type.
/// Consider adding a "default:" property in the specification file to hide this note.
///
String? thumbnailPath;
///
/// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated
@@ -356,6 +348,14 @@ class MetadataSearchDto {
///
DateTime? updatedBefore;
///
/// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated
/// source code must fall back to having a nullable type.
/// Consider adding a "default:" property in the specification file to hide this note.
///
String? webpPath;
bool withArchived;
///
@@ -419,17 +419,17 @@ class MetadataSearchDto {
other.originalPath == originalPath &&
other.page == page &&
_deepEquality.equals(other.personIds, personIds) &&
other.previewPath == previewPath &&
other.resizePath == resizePath &&
other.size == size &&
other.state == state &&
other.takenAfter == takenAfter &&
other.takenBefore == takenBefore &&
other.thumbnailPath == thumbnailPath &&
other.trashedAfter == trashedAfter &&
other.trashedBefore == trashedBefore &&
other.type == type &&
other.updatedAfter == updatedAfter &&
other.updatedBefore == updatedBefore &&
other.webpPath == webpPath &&
other.withArchived == withArchived &&
other.withDeleted == withDeleted &&
other.withExif == withExif &&
@@ -466,17 +466,17 @@ class MetadataSearchDto {
(originalPath == null ? 0 : originalPath!.hashCode) +
(page == null ? 0 : page!.hashCode) +
(personIds.hashCode) +
(previewPath == null ? 0 : previewPath!.hashCode) +
(resizePath == null ? 0 : resizePath!.hashCode) +
(size == null ? 0 : size!.hashCode) +
(state == null ? 0 : state!.hashCode) +
(takenAfter == null ? 0 : takenAfter!.hashCode) +
(takenBefore == null ? 0 : takenBefore!.hashCode) +
(thumbnailPath == null ? 0 : thumbnailPath!.hashCode) +
(trashedAfter == null ? 0 : trashedAfter!.hashCode) +
(trashedBefore == null ? 0 : trashedBefore!.hashCode) +
(type == null ? 0 : type!.hashCode) +
(updatedAfter == null ? 0 : updatedAfter!.hashCode) +
(updatedBefore == null ? 0 : updatedBefore!.hashCode) +
(webpPath == null ? 0 : webpPath!.hashCode) +
(withArchived.hashCode) +
(withDeleted == null ? 0 : withDeleted!.hashCode) +
(withExif == null ? 0 : withExif!.hashCode) +
@@ -484,7 +484,7 @@ class MetadataSearchDto {
(withStacked == null ? 0 : withStacked!.hashCode);
@override
String toString() => 'MetadataSearchDto[checksum=$checksum, city=$city, country=$country, createdAfter=$createdAfter, createdBefore=$createdBefore, deviceAssetId=$deviceAssetId, deviceId=$deviceId, encodedVideoPath=$encodedVideoPath, id=$id, isArchived=$isArchived, isEncoded=$isEncoded, isExternal=$isExternal, isFavorite=$isFavorite, isMotion=$isMotion, isNotInAlbum=$isNotInAlbum, isOffline=$isOffline, isReadOnly=$isReadOnly, isVisible=$isVisible, lensModel=$lensModel, libraryId=$libraryId, make=$make, model=$model, order=$order, originalFileName=$originalFileName, originalPath=$originalPath, page=$page, personIds=$personIds, previewPath=$previewPath, size=$size, state=$state, takenAfter=$takenAfter, takenBefore=$takenBefore, thumbnailPath=$thumbnailPath, trashedAfter=$trashedAfter, trashedBefore=$trashedBefore, type=$type, updatedAfter=$updatedAfter, updatedBefore=$updatedBefore, withArchived=$withArchived, withDeleted=$withDeleted, withExif=$withExif, withPeople=$withPeople, withStacked=$withStacked]';
String toString() => 'MetadataSearchDto[checksum=$checksum, city=$city, country=$country, createdAfter=$createdAfter, createdBefore=$createdBefore, deviceAssetId=$deviceAssetId, deviceId=$deviceId, encodedVideoPath=$encodedVideoPath, id=$id, isArchived=$isArchived, isEncoded=$isEncoded, isExternal=$isExternal, isFavorite=$isFavorite, isMotion=$isMotion, isNotInAlbum=$isNotInAlbum, isOffline=$isOffline, isReadOnly=$isReadOnly, isVisible=$isVisible, lensModel=$lensModel, libraryId=$libraryId, make=$make, model=$model, order=$order, originalFileName=$originalFileName, originalPath=$originalPath, page=$page, personIds=$personIds, resizePath=$resizePath, size=$size, state=$state, takenAfter=$takenAfter, takenBefore=$takenBefore, trashedAfter=$trashedAfter, trashedBefore=$trashedBefore, type=$type, updatedAfter=$updatedAfter, updatedBefore=$updatedBefore, webpPath=$webpPath, withArchived=$withArchived, withDeleted=$withDeleted, withExif=$withExif, withPeople=$withPeople, withStacked=$withStacked]';
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
@@ -619,10 +619,10 @@ class MetadataSearchDto {
// json[r'page'] = null;
}
json[r'personIds'] = this.personIds;
if (this.previewPath != null) {
json[r'previewPath'] = this.previewPath;
if (this.resizePath != null) {
json[r'resizePath'] = this.resizePath;
} else {
// json[r'previewPath'] = null;
// json[r'resizePath'] = null;
}
if (this.size != null) {
json[r'size'] = this.size;
@@ -644,11 +644,6 @@ class MetadataSearchDto {
} else {
// json[r'takenBefore'] = null;
}
if (this.thumbnailPath != null) {
json[r'thumbnailPath'] = this.thumbnailPath;
} else {
// json[r'thumbnailPath'] = null;
}
if (this.trashedAfter != null) {
json[r'trashedAfter'] = this.trashedAfter!.toUtc().toIso8601String();
} else {
@@ -673,6 +668,11 @@ class MetadataSearchDto {
json[r'updatedBefore'] = this.updatedBefore!.toUtc().toIso8601String();
} else {
// json[r'updatedBefore'] = null;
}
if (this.webpPath != null) {
json[r'webpPath'] = this.webpPath;
} else {
// json[r'webpPath'] = null;
}
json[r'withArchived'] = this.withArchived;
if (this.withDeleted != null) {
@@ -735,17 +735,17 @@ class MetadataSearchDto {
personIds: json[r'personIds'] is Iterable
? (json[r'personIds'] as Iterable).cast<String>().toList(growable: false)
: const [],
previewPath: mapValueOfType<String>(json, r'previewPath'),
resizePath: mapValueOfType<String>(json, r'resizePath'),
size: num.parse('${json[r'size']}'),
state: mapValueOfType<String>(json, r'state'),
takenAfter: mapDateTime(json, r'takenAfter', r''),
takenBefore: mapDateTime(json, r'takenBefore', r''),
thumbnailPath: mapValueOfType<String>(json, r'thumbnailPath'),
trashedAfter: mapDateTime(json, r'trashedAfter', r''),
trashedBefore: mapDateTime(json, r'trashedBefore', r''),
type: AssetTypeEnum.fromJson(json[r'type']),
updatedAfter: mapDateTime(json, r'updatedAfter', r''),
updatedBefore: mapDateTime(json, r'updatedBefore', r''),
webpPath: mapValueOfType<String>(json, r'webpPath'),
withArchived: mapValueOfType<bool>(json, r'withArchived') ?? false,
withDeleted: mapValueOfType<bool>(json, r'withDeleted'),
withExif: mapValueOfType<bool>(json, r'withExif'),
+6 -6
View File
@@ -24,8 +24,8 @@ class PathType {
String toJson() => value;
static const original = PathType._(r'original');
static const preview = PathType._(r'preview');
static const thumbnail = PathType._(r'thumbnail');
static const jpegThumbnail = PathType._(r'jpeg_thumbnail');
static const webpThumbnail = PathType._(r'webp_thumbnail');
static const encodedVideo = PathType._(r'encoded_video');
static const sidecar = PathType._(r'sidecar');
static const face = PathType._(r'face');
@@ -34,8 +34,8 @@ class PathType {
/// List of all possible values in this [enum][PathType].
static const values = <PathType>[
original,
preview,
thumbnail,
jpegThumbnail,
webpThumbnail,
encodedVideo,
sidecar,
face,
@@ -79,8 +79,8 @@ class PathTypeTypeTransformer {
if (data != null) {
switch (data) {
case r'original': return PathType.original;
case r'preview': return PathType.preview;
case r'thumbnail': return PathType.thumbnail;
case r'jpeg_thumbnail': return PathType.jpegThumbnail;
case r'webp_thumbnail': return PathType.webpThumbnail;
case r'encoded_video': return PathType.encodedVideo;
case r'sidecar': return PathType.sidecar;
case r'face': return PathType.face;
+9 -9
View File
@@ -14,7 +14,6 @@ class SystemConfigDto {
/// Returns a new [SystemConfigDto] instance.
SystemConfigDto({
required this.ffmpeg,
required this.image,
required this.job,
required this.library_,
required this.logging,
@@ -27,14 +26,13 @@ class SystemConfigDto {
required this.server,
required this.storageTemplate,
required this.theme,
required this.thumbnail,
required this.trash,
required this.user,
});
SystemConfigFFmpegDto ffmpeg;
SystemConfigThumbnailDto image;
SystemConfigJobDto job;
SystemConfigLibraryDto library_;
@@ -59,6 +57,8 @@ class SystemConfigDto {
SystemConfigThemeDto theme;
SystemConfigThumbnailDto thumbnail;
SystemConfigTrashDto trash;
SystemConfigUserDto user;
@@ -66,7 +66,6 @@ class SystemConfigDto {
@override
bool operator ==(Object other) => identical(this, other) || other is SystemConfigDto &&
other.ffmpeg == ffmpeg &&
other.image == image &&
other.job == job &&
other.library_ == library_ &&
other.logging == logging &&
@@ -79,6 +78,7 @@ class SystemConfigDto {
other.server == server &&
other.storageTemplate == storageTemplate &&
other.theme == theme &&
other.thumbnail == thumbnail &&
other.trash == trash &&
other.user == user;
@@ -86,7 +86,6 @@ class SystemConfigDto {
int get hashCode =>
// ignore: unnecessary_parenthesis
(ffmpeg.hashCode) +
(image.hashCode) +
(job.hashCode) +
(library_.hashCode) +
(logging.hashCode) +
@@ -99,16 +98,16 @@ class SystemConfigDto {
(server.hashCode) +
(storageTemplate.hashCode) +
(theme.hashCode) +
(thumbnail.hashCode) +
(trash.hashCode) +
(user.hashCode);
@override
String toString() => 'SystemConfigDto[ffmpeg=$ffmpeg, image=$image, job=$job, library_=$library_, logging=$logging, machineLearning=$machineLearning, map=$map, newVersionCheck=$newVersionCheck, oauth=$oauth, passwordLogin=$passwordLogin, reverseGeocoding=$reverseGeocoding, server=$server, storageTemplate=$storageTemplate, theme=$theme, trash=$trash, user=$user]';
String toString() => 'SystemConfigDto[ffmpeg=$ffmpeg, job=$job, library_=$library_, logging=$logging, machineLearning=$machineLearning, map=$map, newVersionCheck=$newVersionCheck, oauth=$oauth, passwordLogin=$passwordLogin, reverseGeocoding=$reverseGeocoding, server=$server, storageTemplate=$storageTemplate, theme=$theme, thumbnail=$thumbnail, trash=$trash, user=$user]';
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
json[r'ffmpeg'] = this.ffmpeg;
json[r'image'] = this.image;
json[r'job'] = this.job;
json[r'library'] = this.library_;
json[r'logging'] = this.logging;
@@ -121,6 +120,7 @@ class SystemConfigDto {
json[r'server'] = this.server;
json[r'storageTemplate'] = this.storageTemplate;
json[r'theme'] = this.theme;
json[r'thumbnail'] = this.thumbnail;
json[r'trash'] = this.trash;
json[r'user'] = this.user;
return json;
@@ -135,7 +135,6 @@ class SystemConfigDto {
return SystemConfigDto(
ffmpeg: SystemConfigFFmpegDto.fromJson(json[r'ffmpeg'])!,
image: SystemConfigThumbnailDto.fromJson(json[r'image'])!,
job: SystemConfigJobDto.fromJson(json[r'job'])!,
library_: SystemConfigLibraryDto.fromJson(json[r'library'])!,
logging: SystemConfigLoggingDto.fromJson(json[r'logging'])!,
@@ -148,6 +147,7 @@ class SystemConfigDto {
server: SystemConfigServerDto.fromJson(json[r'server'])!,
storageTemplate: SystemConfigStorageTemplateDto.fromJson(json[r'storageTemplate'])!,
theme: SystemConfigThemeDto.fromJson(json[r'theme'])!,
thumbnail: SystemConfigThumbnailDto.fromJson(json[r'thumbnail'])!,
trash: SystemConfigTrashDto.fromJson(json[r'trash'])!,
user: SystemConfigUserDto.fromJson(json[r'user'])!,
);
@@ -198,7 +198,6 @@ class SystemConfigDto {
/// The list of required keys that must be present in a JSON.
static const requiredKeys = <String>{
'ffmpeg',
'image',
'job',
'library',
'logging',
@@ -211,6 +210,7 @@ class SystemConfigDto {
'server',
'storageTemplate',
'theme',
'thumbnail',
'trash',
'user',
};
+15 -31
View File
@@ -14,55 +14,43 @@ class SystemConfigThumbnailDto {
/// Returns a new [SystemConfigThumbnailDto] instance.
SystemConfigThumbnailDto({
required this.colorspace,
required this.previewFormat,
required this.previewSize,
required this.jpegSize,
required this.quality,
required this.thumbnailFormat,
required this.thumbnailSize,
required this.webpSize,
});
Colorspace colorspace;
ImageFormat previewFormat;
int previewSize;
int jpegSize;
int quality;
ImageFormat thumbnailFormat;
int thumbnailSize;
int webpSize;
@override
bool operator ==(Object other) => identical(this, other) || other is SystemConfigThumbnailDto &&
other.colorspace == colorspace &&
other.previewFormat == previewFormat &&
other.previewSize == previewSize &&
other.jpegSize == jpegSize &&
other.quality == quality &&
other.thumbnailFormat == thumbnailFormat &&
other.thumbnailSize == thumbnailSize;
other.webpSize == webpSize;
@override
int get hashCode =>
// ignore: unnecessary_parenthesis
(colorspace.hashCode) +
(previewFormat.hashCode) +
(previewSize.hashCode) +
(jpegSize.hashCode) +
(quality.hashCode) +
(thumbnailFormat.hashCode) +
(thumbnailSize.hashCode);
(webpSize.hashCode);
@override
String toString() => 'SystemConfigThumbnailDto[colorspace=$colorspace, previewFormat=$previewFormat, previewSize=$previewSize, quality=$quality, thumbnailFormat=$thumbnailFormat, thumbnailSize=$thumbnailSize]';
String toString() => 'SystemConfigThumbnailDto[colorspace=$colorspace, jpegSize=$jpegSize, quality=$quality, webpSize=$webpSize]';
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
json[r'colorspace'] = this.colorspace;
json[r'previewFormat'] = this.previewFormat;
json[r'previewSize'] = this.previewSize;
json[r'jpegSize'] = this.jpegSize;
json[r'quality'] = this.quality;
json[r'thumbnailFormat'] = this.thumbnailFormat;
json[r'thumbnailSize'] = this.thumbnailSize;
json[r'webpSize'] = this.webpSize;
return json;
}
@@ -75,11 +63,9 @@ class SystemConfigThumbnailDto {
return SystemConfigThumbnailDto(
colorspace: Colorspace.fromJson(json[r'colorspace'])!,
previewFormat: ImageFormat.fromJson(json[r'previewFormat'])!,
previewSize: mapValueOfType<int>(json, r'previewSize')!,
jpegSize: mapValueOfType<int>(json, r'jpegSize')!,
quality: mapValueOfType<int>(json, r'quality')!,
thumbnailFormat: ImageFormat.fromJson(json[r'thumbnailFormat'])!,
thumbnailSize: mapValueOfType<int>(json, r'thumbnailSize')!,
webpSize: mapValueOfType<int>(json, r'webpSize')!,
);
}
return null;
@@ -128,11 +114,9 @@ class SystemConfigThumbnailDto {
/// The list of required keys that must be present in a JSON.
static const requiredKeys = <String>{
'colorspace',
'previewFormat',
'previewSize',
'jpegSize',
'quality',
'thumbnailFormat',
'thumbnailSize',
'webpSize',
};
}
+6 -6
View File
@@ -23,13 +23,13 @@ class ThumbnailFormat {
String toJson() => value;
static const jpeg = ThumbnailFormat._(r'jpeg');
static const webp = ThumbnailFormat._(r'webp');
static const JPEG = ThumbnailFormat._(r'JPEG');
static const WEBP = ThumbnailFormat._(r'WEBP');
/// List of all possible values in this [enum][ThumbnailFormat].
static const values = <ThumbnailFormat>[
jpeg,
webp,
JPEG,
WEBP,
];
static ThumbnailFormat? fromJson(dynamic value) => ThumbnailFormatTypeTransformer().decode(value);
@@ -68,8 +68,8 @@ class ThumbnailFormatTypeTransformer {
ThumbnailFormat? decode(dynamic data, {bool allowNull = true}) {
if (data != null) {
switch (data) {
case r'jpeg': return ThumbnailFormat.jpeg;
case r'webp': return ThumbnailFormat.webp;
case r'JPEG': return ThumbnailFormat.JPEG;
case r'WEBP': return ThumbnailFormat.WEBP;
default:
if (!allowNull) {
throw ArgumentError('Unknown enum value to decode: $data');
+1 -1
View File
@@ -110,7 +110,7 @@ void main() {
// TODO
});
//Future<List<AssetResponseDto>> searchAssets({ String checksum, String city, String country, DateTime createdAfter, DateTime createdBefore, String deviceAssetId, String deviceId, String encodedVideoPath, String id, bool isArchived, bool isEncoded, bool isExternal, bool isFavorite, bool isMotion, bool isNotInAlbum, bool isOffline, bool isReadOnly, bool isVisible, String lensModel, String libraryId, String make, String model, AssetOrder order, String originalFileName, String originalPath, num page, List<String> personIds, String previewPath, num size, String state, DateTime takenAfter, DateTime takenBefore, String thumbnailPath, DateTime trashedAfter, DateTime trashedBefore, AssetTypeEnum type, DateTime updatedAfter, DateTime updatedBefore, bool withArchived, bool withDeleted, bool withExif, bool withPeople, bool withStacked }) async
//Future<List<AssetResponseDto>> searchAssets({ String checksum, String city, String country, DateTime createdAfter, DateTime createdBefore, String deviceAssetId, String deviceId, String encodedVideoPath, String id, bool isArchived, bool isEncoded, bool isExternal, bool isFavorite, bool isMotion, bool isNotInAlbum, bool isOffline, bool isReadOnly, bool isVisible, String lensModel, String libraryId, String make, String model, AssetOrder order, String originalFileName, String originalPath, num page, List<String> personIds, String resizePath, num size, String state, DateTime takenAfter, DateTime takenBefore, DateTime trashedAfter, DateTime trashedBefore, AssetTypeEnum type, DateTime updatedAfter, DateTime updatedBefore, String webpPath, bool withArchived, bool withDeleted, bool withExif, bool withPeople, bool withStacked }) async
test('test searchAssets', () async {
// TODO
});
@@ -36,8 +36,8 @@ void main() {
// TODO
});
// String previewPath
test('to test the property `previewPath`', () async {
// String resizePath
test('to test the property `resizePath`', () async {
// TODO
});
+2 -2
View File
@@ -36,8 +36,8 @@ void main() {
// TODO
});
// String previewPath
test('to test the property `previewPath`', () async {
// String resizePath
test('to test the property `resizePath`', () async {
// TODO
});
-21
View File
@@ -1,21 +0,0 @@
//
// AUTO-GENERATED FILE, DO NOT MODIFY!
//
// @dart=2.12
// ignore_for_file: unused_element, unused_import
// ignore_for_file: always_put_required_named_parameters_first
// ignore_for_file: constant_identifier_names
// ignore_for_file: lines_longer_than_80_chars
import 'package:openapi/api.dart';
import 'package:test/test.dart';
// tests for ImageFormat
void main() {
group('test ImageFormat', () {
});
}
+5
View File
@@ -26,6 +26,11 @@ void main() {
// TODO
});
// int yearsAgo
test('to test the property `yearsAgo`', () async {
// TODO
});
});
+7 -7
View File
@@ -151,8 +151,8 @@ void main() {
// TODO
});
// String previewPath
test('to test the property `previewPath`', () async {
// String resizePath
test('to test the property `resizePath`', () async {
// TODO
});
@@ -176,11 +176,6 @@ void main() {
// TODO
});
// String thumbnailPath
test('to test the property `thumbnailPath`', () async {
// TODO
});
// DateTime trashedAfter
test('to test the property `trashedAfter`', () async {
// TODO
@@ -206,6 +201,11 @@ void main() {
// TODO
});
// String webpPath
test('to test the property `webpPath`', () async {
// TODO
});
// bool withArchived (default value: false)
test('to test the property `withArchived`', () async {
// TODO
+5 -5
View File
@@ -21,11 +21,6 @@ void main() {
// TODO
});
// SystemConfigThumbnailDto image
test('to test the property `image`', () async {
// TODO
});
// SystemConfigJobDto job
test('to test the property `job`', () async {
// TODO
@@ -86,6 +81,11 @@ void main() {
// TODO
});
// SystemConfigThumbnailDto thumbnail
test('to test the property `thumbnail`', () async {
// TODO
});
// SystemConfigTrashDto trash
test('to test the property `trash`', () async {
// TODO
+4 -14
View File
@@ -21,13 +21,8 @@ void main() {
// TODO
});
// ImageFormat previewFormat
test('to test the property `previewFormat`', () async {
// TODO
});
// int previewSize
test('to test the property `previewSize`', () async {
// int jpegSize
test('to test the property `jpegSize`', () async {
// TODO
});
@@ -36,13 +31,8 @@ void main() {
// TODO
});
// ImageFormat thumbnailFormat
test('to test the property `thumbnailFormat`', () async {
// TODO
});
// int thumbnailSize
test('to test the property `thumbnailSize`', () async {
// int webpSize
test('to test the property `webpSize`', () async {
// TODO
});
+35 -45
View File
@@ -2382,7 +2382,7 @@
}
},
{
"name": "previewPath",
"name": "resizePath",
"required": false,
"in": "query",
"schema": {
@@ -2423,14 +2423,6 @@
"type": "string"
}
},
{
"name": "thumbnailPath",
"required": false,
"in": "query",
"schema": {
"type": "string"
}
},
{
"name": "trashedAfter",
"required": false,
@@ -2475,6 +2467,14 @@
"type": "string"
}
},
{
"name": "webpPath",
"required": false,
"in": "query",
"schema": {
"type": "string"
}
},
{
"name": "withArchived",
"required": false,
@@ -7776,7 +7776,7 @@
"id": {
"type": "string"
},
"previewPath": {
"resizePath": {
"type": "string"
}
},
@@ -7785,7 +7785,7 @@
"deviceAssetId",
"deviceId",
"id",
"previewPath"
"resizePath"
],
"type": "object"
},
@@ -7803,7 +7803,7 @@
"object": {
"type": "string"
},
"previewPath": {
"resizePath": {
"type": "string"
}
},
@@ -7812,7 +7812,7 @@
"deviceId",
"id",
"object",
"previewPath"
"resizePath"
],
"type": "object"
},
@@ -8106,13 +8106,6 @@
],
"type": "object"
},
"ImageFormat": {
"enum": [
"jpeg",
"webp"
],
"type": "string"
},
"JobCommand": {
"enum": [
"start",
@@ -8434,12 +8427,17 @@
"type": "array"
},
"title": {
"deprecated": true,
"type": "string"
},
"yearsAgo": {
"type": "integer"
}
},
"required": [
"assets",
"title"
"title",
"yearsAgo"
],
"type": "object"
},
@@ -8549,7 +8547,7 @@
},
"type": "array"
},
"previewPath": {
"resizePath": {
"type": "string"
},
"size": {
@@ -8566,9 +8564,6 @@
"format": "date-time",
"type": "string"
},
"thumbnailPath": {
"type": "string"
},
"trashedAfter": {
"format": "date-time",
"type": "string"
@@ -8588,6 +8583,9 @@
"format": "date-time",
"type": "string"
},
"webpPath": {
"type": "string"
},
"withArchived": {
"default": false,
"type": "boolean"
@@ -8740,8 +8738,8 @@
"PathType": {
"enum": [
"original",
"preview",
"thumbnail",
"jpeg_thumbnail",
"webp_thumbnail",
"encoded_video",
"sidecar",
"face",
@@ -9737,9 +9735,6 @@
"ffmpeg": {
"$ref": "#/components/schemas/SystemConfigFFmpegDto"
},
"image": {
"$ref": "#/components/schemas/SystemConfigThumbnailDto"
},
"job": {
"$ref": "#/components/schemas/SystemConfigJobDto"
},
@@ -9776,6 +9771,9 @@
"theme": {
"$ref": "#/components/schemas/SystemConfigThemeDto"
},
"thumbnail": {
"$ref": "#/components/schemas/SystemConfigThumbnailDto"
},
"trash": {
"$ref": "#/components/schemas/SystemConfigTrashDto"
},
@@ -9785,7 +9783,6 @@
},
"required": [
"ffmpeg",
"image",
"job",
"library",
"logging",
@@ -9798,6 +9795,7 @@
"server",
"storageTemplate",
"theme",
"thumbnail",
"trash",
"user"
],
@@ -10250,29 +10248,21 @@
"colorspace": {
"$ref": "#/components/schemas/Colorspace"
},
"previewFormat": {
"$ref": "#/components/schemas/ImageFormat"
},
"previewSize": {
"jpegSize": {
"type": "integer"
},
"quality": {
"type": "integer"
},
"thumbnailFormat": {
"$ref": "#/components/schemas/ImageFormat"
},
"thumbnailSize": {
"webpSize": {
"type": "integer"
}
},
"required": [
"colorspace",
"previewFormat",
"previewSize",
"jpegSize",
"quality",
"thumbnailFormat",
"thumbnailSize"
"webpSize"
],
"type": "object"
},
@@ -10335,8 +10325,8 @@
},
"ThumbnailFormat": {
"enum": [
"jpeg",
"webp"
"JPEG",
"WEBP"
],
"type": "string"
},
+6 -6
View File
@@ -22,18 +22,18 @@
"integrity": "sha512-V33FjR6V+AkGRWYQW3XPm5BLn2loGl2ujSeja1TzdjjEn2zjGgl3ve0dcFf/jEwPZEOqQZl6YwIgIB/clXVqWw=="
},
"node_modules/@types/node": {
"version": "20.11.28",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.28.tgz",
"integrity": "sha512-M/GPWVS2wLkSkNHVeLkrF2fD5Lx5UC4PxA0uZcKc6QqbIQUJyW1jVjueJYi1z8n0I5PxYrtpnPnWglE+y9A0KA==",
"version": "20.11.30",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.30.tgz",
"integrity": "sha512-dHM6ZxwlmuZaRmUPfv1p+KrdD1Dci04FbdEm/9wEMouFqxYoFl5aMkt0VMAUtYRQDyYvD41WJLukhq/ha3YuTw==",
"dev": true,
"dependencies": {
"undici-types": "~5.26.4"
}
},
"node_modules/typescript": {
"version": "5.4.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz",
"integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==",
"version": "5.4.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz",
"integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==",
"dev": true,
"bin": {
"tsc": "bin/tsc",
+25 -30
View File
@@ -242,14 +242,14 @@ export type CuratedLocationsResponseDto = {
deviceAssetId: string;
deviceId: string;
id: string;
previewPath: string;
resizePath: string;
};
export type CuratedObjectsResponseDto = {
deviceAssetId: string;
deviceId: string;
id: string;
"object": string;
previewPath: string;
resizePath: string;
};
export type CheckExistingAssetsDto = {
deviceAssetIds: string[];
@@ -273,6 +273,7 @@ export type MapMarkerResponseDto = {
export type MemoryLaneResponseDto = {
assets: AssetResponseDto[];
title: string;
yearsAgo: number;
};
export type UpdateStackParentDto = {
newParentId: string;
@@ -643,17 +644,17 @@ export type MetadataSearchDto = {
originalPath?: string;
page?: number;
personIds?: string[];
previewPath?: string;
resizePath?: string;
size?: number;
state?: string;
takenAfter?: string;
takenBefore?: string;
thumbnailPath?: string;
trashedAfter?: string;
trashedBefore?: string;
"type"?: AssetTypeEnum;
updatedAfter?: string;
updatedBefore?: string;
webpPath?: string;
withArchived?: boolean;
withDeleted?: boolean;
withExif?: boolean;
@@ -830,14 +831,6 @@ export type SystemConfigFFmpegDto = {
transcode: TranscodePolicy;
twoPass: boolean;
};
export type SystemConfigThumbnailDto = {
colorspace: Colorspace;
previewFormat: ImageFormat;
previewSize: number;
quality: number;
thumbnailFormat: ImageFormat;
thumbnailSize: number;
};
export type JobSettingsDto = {
concurrency: number;
};
@@ -930,6 +923,12 @@ export type SystemConfigStorageTemplateDto = {
export type SystemConfigThemeDto = {
customCss: string;
};
export type SystemConfigThumbnailDto = {
colorspace: Colorspace;
jpegSize: number;
quality: number;
webpSize: number;
};
export type SystemConfigTrashDto = {
days: number;
enabled: boolean;
@@ -939,7 +938,6 @@ export type SystemConfigUserDto = {
};
export type SystemConfigDto = {
ffmpeg: SystemConfigFFmpegDto;
image: SystemConfigThumbnailDto;
job: SystemConfigJobDto;
library: SystemConfigLibraryDto;
logging: SystemConfigLoggingDto;
@@ -952,6 +950,7 @@ export type SystemConfigDto = {
server: SystemConfigServerDto;
storageTemplate: SystemConfigStorageTemplateDto;
theme: SystemConfigThemeDto;
thumbnail: SystemConfigThumbnailDto;
trash: SystemConfigTrashDto;
user: SystemConfigUserDto;
};
@@ -1564,7 +1563,7 @@ export function updateAsset({ id, updateAssetDto }: {
body: updateAssetDto
})));
}
export function searchAssets({ checksum, city, country, createdAfter, createdBefore, deviceAssetId, deviceId, encodedVideoPath, id, isArchived, isEncoded, isExternal, isFavorite, isMotion, isNotInAlbum, isOffline, isReadOnly, isVisible, lensModel, libraryId, make, model, order, originalFileName, originalPath, page, personIds, previewPath, size, state, takenAfter, takenBefore, thumbnailPath, trashedAfter, trashedBefore, $type, updatedAfter, updatedBefore, withArchived, withDeleted, withExif, withPeople, withStacked }: {
export function searchAssets({ checksum, city, country, createdAfter, createdBefore, deviceAssetId, deviceId, encodedVideoPath, id, isArchived, isEncoded, isExternal, isFavorite, isMotion, isNotInAlbum, isOffline, isReadOnly, isVisible, lensModel, libraryId, make, model, order, originalFileName, originalPath, page, personIds, resizePath, size, state, takenAfter, takenBefore, trashedAfter, trashedBefore, $type, updatedAfter, updatedBefore, webpPath, withArchived, withDeleted, withExif, withPeople, withStacked }: {
checksum?: string;
city?: string;
country?: string;
@@ -1592,17 +1591,17 @@ export function searchAssets({ checksum, city, country, createdAfter, createdBef
originalPath?: string;
page?: number;
personIds?: string[];
previewPath?: string;
resizePath?: string;
size?: number;
state?: string;
takenAfter?: string;
takenBefore?: string;
thumbnailPath?: string;
trashedAfter?: string;
trashedBefore?: string;
$type?: AssetTypeEnum;
updatedAfter?: string;
updatedBefore?: string;
webpPath?: string;
withArchived?: boolean;
withDeleted?: boolean;
withExif?: boolean;
@@ -1640,17 +1639,17 @@ export function searchAssets({ checksum, city, country, createdAfter, createdBef
originalPath,
page,
personIds,
previewPath,
resizePath,
size,
state,
takenAfter,
takenBefore,
thumbnailPath,
trashedAfter,
trashedBefore,
"type": $type,
updatedAfter,
updatedBefore,
webpPath,
withArchived,
withDeleted,
withExif,
@@ -2787,8 +2786,8 @@ export enum AssetJobName {
TranscodeVideo = "transcode-video"
}
export enum ThumbnailFormat {
Jpeg = "jpeg",
Webp = "webp"
Jpeg = "JPEG",
Webp = "WEBP"
}
export enum TimeBucketSize {
Day = "DAY",
@@ -2805,8 +2804,8 @@ export enum PathEntityType {
}
export enum PathType {
Original = "original",
Preview = "preview",
Thumbnail = "thumbnail",
JpegThumbnail = "jpeg_thumbnail",
WebpThumbnail = "webp_thumbnail",
EncodedVideo = "encoded_video",
Sidecar = "sidecar",
Face = "face",
@@ -2888,14 +2887,6 @@ export enum TranscodePolicy {
Required = "required",
Disabled = "disabled"
}
export enum Colorspace {
Srgb = "srgb",
P3 = "p3"
}
export enum ImageFormat {
Jpeg = "jpeg",
Webp = "webp"
}
export enum LogLevel {
Verbose = "verbose",
Debug = "debug",
@@ -2912,6 +2903,10 @@ export enum ModelType {
FacialRecognition = "facial-recognition",
Clip = "clip"
}
export enum Colorspace {
Srgb = "srgb",
P3 = "p3"
}
export enum MapTheme {
Light = "light",
Dark = "dark"
+2 -2
View File
@@ -9,12 +9,12 @@
</p>
<p align="center">
<img src="design/immich-logo-stacked-light.svg" width="300" title="Iniciar sessió amb URL personalitzada">
<img src="../design/immich-logo-stacked-light.svg" width="300" title="Iniciar sessió amb URL personalitzada">
</p>
<h3 align="center">Immich - Solució de còpia de seguretat d'alta rendiment per a fotos i vídeos auto-allotjada</h3>
<br/>
<a href="https://immich.app">
<img src="design/immich-screenshots.png" title="Captura de pantalla principal">
<img src="../design/immich-screenshots.png" title="Captura de pantalla principal">
</a>
<br/>
<p align="center">
+2 -2
View File
@@ -9,12 +9,12 @@
</p>
<p align="center">
<img src="design/immich-logo-stacked-light.svg" width="300" title="Login mit eigener URL">
<img src="../design/immich-logo-stacked-light.svg" width="300" title="Login mit eigener URL">
</p>
<h3 align="center">Immich - Hoch performante, selbst gehostete Backup-Lösung für Fotos und Videos</h3>
<br/>
<a href="https://immich.app">
<img src="design/immich-screenshots.png" title="Haupt-Screenshot">
<img src="../design/immich-screenshots.png" title="Haupt-Screenshot">
</a>
<br/>
<p align="center">
+2 -2
View File
@@ -9,12 +9,12 @@
</p>
<p align="center">
<img src="design/immich-logo-stacked-light.svg" width="300" title="Iniciar sesión con URL personalizada">
<img src="../design/immich-logo-stacked-light.svg" width="300" title="Iniciar sesión con URL personalizada">
</p>
<h3 align="center">Immich: Una solución Self-Hosted de copia de seguridad de fotos y videos de alto rendimiento</h3>
<br/>
<a href="https://immich.app">
<img src="design/immich-screenshots.png" title="Captura de pantalla principal">
<img src="../design/immich-screenshots.png" title="Captura de pantalla principal">
</a>
<br/>
<p align="center">
+2 -2
View File
@@ -9,12 +9,12 @@
</p>
<p align="center">
<img src="design/immich-logo-stacked-light.svg" width="300" title="Login With Custom URL">
<img src="../design/immich-logo-stacked-light.svg" width="300" title="Login With Custom URL">
</p>
<h3 align="center">Immich - Solution de sauvegarde performante et auto-hébergée des photos et des vidéos</h3>
<br/>
<a href="https://immich.app">
<img src="design/immich-screenshots.png" title="Main Screenshot">
<img src="../design/immich-screenshots.png" title="Main Screenshot">
</a>
<br/>
<p align="center">
+2 -2
View File
@@ -9,12 +9,12 @@
</p>
<p align="center">
<img src="design/immich-logo-stacked-light.svg" width="300" title="Login With Custom URL">
<img src="../design/immich-logo-stacked-light.svg" width="300" title="Login With Custom URL">
</p>
<h3 align="center">Immich - Soluzione self-hosted ad alte prestazioni per backup di foto e video</h3>
<br/>
<a href="https://immich.app">
<img src="design/immich-screenshots.png" title="Main Screenshot">
<img src="../design/immich-screenshots.png" title="Main Screenshot">
</a>
<br/>
<p align="center">
+2 -2
View File
@@ -9,12 +9,12 @@
</p>
<p align="center">
<img src="design/immich-logo-stacked-light.svg" width="300" title="Login With Custom URL">
<img src="../design/immich-logo-stacked-light.svg" width="300" title="Login With Custom URL">
</p>
<h3 align="center">Immich - 高性能なセルフホスト 写真/ビデオバックアップソリューション</h3>
<br/>
<a href="https://immich.app">
<img src="design/immich-screenshots.png" title="Main Screenshot">
<img src="../design/immich-screenshots.png" title="Main Screenshot">
</a>
<br/>
<p align="center">
+2 -2
View File
@@ -9,12 +9,12 @@
</p>
<p align="center">
<img src="design/immich-logo-stacked-light.svg" width="300" title="Login With Custom URL">
<img src="../design/immich-logo-stacked-light.svg" width="300" title="Login With Custom URL">
</p>
<h3 align="center">Immich - 고성능 자체 호스팅 사진 및 동영상 백업 솔루션</h3>
<br/>
<a href="https://immich.app">
<img src="design/immich-screenshots.png" title="Main Screenshot">
<img src="../design/immich-screenshots.png" title="Main Screenshot">
</a>
<br/>
<p align="center">
+2 -2
View File
@@ -9,12 +9,12 @@
</p>
<p align="center">
<img src="design/immich-logo-stacked-light.svg" width="300" title="Login met aangepaste URL">
<img src="../design/immich-logo-stacked-light.svg" width="300" title="Login met aangepaste URL">
</p>
<h3 align="center">Immich - Hoogwaardige, self-hosted back-up oplossing voor foto's en video's</h3>
<br/>
<a href="https://immich.app">
<img src="design/immich-screenshots.png" title="Main Screenshot">
<img src="../design/immich-screenshots.png" title="Main Screenshot">
</a>
<br/>
<p align="center">
+2 -2
View File
@@ -9,12 +9,12 @@
</p>
<p align="center">
<img src="design/immich-logo-stacked-light.svg" width="300" title="Login With Custom URL">
<img src="../design/immich-logo-stacked-light.svg" width="300" title="Login With Custom URL">
</p>
<h3 align="center">Immich - Высокопроизводительное решение для автономоного создания фото и видео архивов</h3>
<br/>
<a href="https://immich.app">
<img src="design/immich-screenshots.png" title="Main Screenshot">
<img src="../design/immich-screenshots.png" title="Main Screenshot">
</a>
<br/>
<p align="center">
+2 -2
View File
@@ -9,12 +9,12 @@
</p>
<p align="center">
<img src="design/immich-logo-stacked-light.svg" width="300" title="Login With Custom URL">
<img src="../design/immich-logo-stacked-light.svg" width="300" title="Login With Custom URL">
</p>
<h3 align="center">Immich - Yüksek performanslı, kendine ait barındırılan fotoğraf ve video yedekleme çözümü</h3>
<br/>
<a href="https://immich.app">
<img src="design/immich-screenshots.png" title="Main Screenshot">
<img src="../design/immich-screenshots.png" title="Main Screenshot">
</a>
<br/>
<p align="center">
+19 -4
View File
@@ -9,7 +9,7 @@
</p>
<p align="center">
<img src="design/immich-logo-stacked-light.svg" width="300" title="Login With Custom URL">
<img src="../design/immich-logo-stacked-light.svg" width="300" title="Login With Custom URL">
</p>
<h3 align="center">Immich - 高性能的自托管照片和视频备份方案</h3>
<p align="center">
@@ -17,7 +17,7 @@
</p>
<br/>
<a href="https://immich.app">
<img src="design/immich-screenshots.png" title="界面截图">
<img src="../design/immich-screenshots.png" title="界面截图">
</a>
<br/>
@@ -73,17 +73,22 @@
规格: 甲骨文免费虚拟机套餐——阿姆斯特丹 4核 2.4Ghz ARM64 CPU, 24GB RAM。
```
## 活跃度
![活跃度](https://repobeats.axiom.co/api/embed/9e86d9dc3ddd137161f2f6d2e758d7863b1789cb.svg "Repobeats analytics image")
# 功能特性
| 功能特性 | 移动端 | 网页端 |
|---------------------------------------------|--------|--------|
| 上传并查看照片和视频 | 是 | 是 |
| 软件运行时自动备份 | 是 | N/A |
| 忽略重复的项目 | 是 | 是 |
| 选择需要备份的相册 | 是 | N/A |
| 下载照片和视频到本地 | 是 | 是 |
| 多用户支持 | 是 | 是 |
| 相册与共享相册 | 是 | 是 |
| 可拖动的快速导航栏 | 是 | 是 |
| 可拖动的快速滚动条 | 是 | 是 |
| 支持RAW格式 | 是 | 是 |
| 元数据视图(EXIF、地图) | 是 | 是 |
| 通过元数据、对象、人脸和标签进行搜索 | 是 | 是 |
@@ -93,6 +98,7 @@
| OAuth 支持 | 是 | 是 |
| API Keys | N/A | 是 |
| 实况照片备份和查看 | 是 | 是 |
| 支持360度全景图显示 | 否 | 是 |
| 用户自定义存储结构 | 是 | 是 |
| 公共分享 | 否 | 是 |
| 归档与收藏功能 | 是 | 是 |
@@ -104,7 +110,6 @@
| 只读相册 | 是 | 是 |
| 照片堆叠 | 是 | 是 |
# 支持本项目
我已经致力于本项目并且我将会持续更新文档、新增功能和修复问题。但是独木不成林,我需要您给予我坚持下去的动力。
@@ -126,3 +131,13 @@
<a href="https://github.com/alextran1502/immich/graphs/contributors">
<img src="https://contrib.rocks/image?repo=immich-app/immich" width="100%"/>
</a>
## Star增长曲线
<a href="https://star-history.com/#immich-app/immich&Date">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=immich-app/immich&type=Date&theme=dark" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=immich-app/immich&type=Date" />
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=immich-app/immich&type=Date" width="100%" />
</picture>
</a>
+3 -3
View File
@@ -1,5 +1,5 @@
# dev build
FROM ghcr.io/immich-app/base-server-dev:20240319@sha256:9c9492d59b51a0c340ea3f61a1c9a483122b86b008c7fbe1759d7b858c5e6af5 as dev
FROM ghcr.io/immich-app/base-server-dev:20240326@sha256:d945aba864051b30888617f36446f86b72c4bc7ad6476b9dd2aaa0c4c4e3c945 as dev
RUN apt-get install --no-install-recommends -yqq tini
WORKDIR /usr/src/app
@@ -40,7 +40,7 @@ RUN npm run build
# prod build
FROM ghcr.io/immich-app/base-server-prod:20240319@sha256:6bfcb2f2b84d3070be95ab09ba614e2ff3e832e566a283f516a276a8ae6a147b
FROM ghcr.io/immich-app/base-server-prod:20240326@sha256:28ad98fed8d746b5f92de49ff776cfdff7399df163ebeda2f37a01f473965841
WORKDIR /usr/src/app
ENV NODE_ENV=production \
@@ -53,7 +53,7 @@ COPY --from=web /usr/src/app/build ./www
COPY server/resources resources
COPY server/package.json server/package-lock.json ./
COPY server/start*.sh ./
RUN npm link && npm cache clean --force
RUN npm link && npm install -g @immich/cli && npm cache clean --force
COPY LICENSE /licenses/LICENSE.txt
COPY LICENSE /LICENSE
ENV PATH="${PATH}:/usr/src/app/bin"
+176 -243
View File
@@ -883,9 +883,9 @@
}
},
"node_modules/@babel/runtime": {
"version": "7.24.0",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.0.tgz",
"integrity": "sha512-Chk32uHMg6TnQdvw2e9IlqPpFX/6NLuK0Ys2PqLb7/gL5uFn9mXvK715FGLlOLQrcO4qIkNHkvPGktzzXexsFw==",
"version": "7.24.1",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.1.tgz",
"integrity": "sha512-+BIznRzyqBf+2wCTxcKE3wDjfGeCoVE61KSHGpkzqrLi8qxqFwBeUFyId2cxkTmm55fzDGnm0+yCxaxygrLUnQ==",
"dependencies": {
"regenerator-runtime": "^0.14.0"
},
@@ -2538,9 +2538,9 @@
}
},
"node_modules/@nestjs/common": {
"version": "10.3.3",
"resolved": "https://registry.npmjs.org/@nestjs/common/-/common-10.3.3.tgz",
"integrity": "sha512-LAkTe8/CF0uNWM0ecuDwUNTHCi1lVSITmmR4FQ6Ftz1E7ujQCnJ5pMRzd8JRN14vdBkxZZ8VbVF0BDUKoKNxMQ==",
"version": "10.3.5",
"resolved": "https://registry.npmjs.org/@nestjs/common/-/common-10.3.5.tgz",
"integrity": "sha512-XWxbDf2ey/jAyEa3/XpckgfzJZ9j3I05ZkEFx7cAlebFuVKeq5UDDb5Sq9O7hMmbH9xdQj3pYT19SSj01hKeug==",
"dependencies": {
"iterare": "1.2.1",
"tslib": "2.6.2",
@@ -2592,9 +2592,9 @@
}
},
"node_modules/@nestjs/core": {
"version": "10.3.3",
"resolved": "https://registry.npmjs.org/@nestjs/core/-/core-10.3.3.tgz",
"integrity": "sha512-kxJWggQAPX3RuZx9JVec69eSLaYLNIox2emkZJpfBJ5Qq7cAq7edQIt1r4LGjTKq6kFubNTPsqhWf5y7yFRBPw==",
"version": "10.3.5",
"resolved": "https://registry.npmjs.org/@nestjs/core/-/core-10.3.5.tgz",
"integrity": "sha512-U7SrGD9/Mu4eUtxfZYiGdY38FcksEyJegs4dQZ8B19nnusw0aTocPEy4HVsmx0LLO4sG+fBLLYzCDDr9kFwXAQ==",
"hasInstallScript": true,
"dependencies": {
"@nuxtjs/opencollective": "0.3.2",
@@ -2660,13 +2660,13 @@
}
},
"node_modules/@nestjs/platform-express": {
"version": "10.3.3",
"resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-10.3.3.tgz",
"integrity": "sha512-GGKSEU48Os7nYFIsUM0nutuFUGn5AbeP8gzFBiBCAtiuJWrXZXpZ58pMBYxAbMf7IrcOZFInHEukjHGAQU0OZw==",
"version": "10.3.5",
"resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-10.3.5.tgz",
"integrity": "sha512-IhVomwLvdLlv4zCdQK2ROT/nInk1i8m4K48lAUHJV5UVktgVmg0WbQga2/9KywaTjNbx+eWhZXXFii+vtFRAOw==",
"dependencies": {
"body-parser": "1.20.2",
"cors": "2.8.5",
"express": "4.18.2",
"express": "4.18.3",
"multer": "1.4.4-lts.1",
"tslib": "2.6.2"
},
@@ -2680,11 +2680,11 @@
}
},
"node_modules/@nestjs/platform-socket.io": {
"version": "10.3.3",
"resolved": "https://registry.npmjs.org/@nestjs/platform-socket.io/-/platform-socket.io-10.3.3.tgz",
"integrity": "sha512-QqM9BMTdYPvXOqx3oWrv130HOtc2krPvfgqgDsPWkBLfR+TssrA5QDaTW8HSjEQAfmugvHwhEAAU4+yXRl6tKg==",
"version": "10.3.5",
"resolved": "https://registry.npmjs.org/@nestjs/platform-socket.io/-/platform-socket.io-10.3.5.tgz",
"integrity": "sha512-G2N3sTd9tZ7XQQ7RlrpaQdt1/IBztVHuKg686QmBTLVlRHZ1AMOmXouBk+q5SINT1XURiABa8tQh1Ydx0OEh9w==",
"dependencies": {
"socket.io": "4.7.4",
"socket.io": "4.7.5",
"tslib": "2.6.2"
},
"funding": {
@@ -2765,9 +2765,9 @@
}
},
"node_modules/@nestjs/testing": {
"version": "10.3.3",
"resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-10.3.3.tgz",
"integrity": "sha512-kX20GfjAImL5grd/i69uD/x7sc00BaqGcP2dRG3ilqshQUuy5DOmspLCr3a2C8xmVU7kzK4spT0oTxhe6WcCAA==",
"version": "10.3.5",
"resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-10.3.5.tgz",
"integrity": "sha512-j30/lxH0BayeDTigapYtQn/XhMRR7CzlFsm3dHoWViWQv0qT1r2ffe3927BbBLX3N/ZzglE10OAqW06ADZV8dw==",
"dev": true,
"dependencies": {
"tslib": "2.6.2"
@@ -2807,9 +2807,9 @@
}
},
"node_modules/@nestjs/websockets": {
"version": "10.3.3",
"resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-10.3.3.tgz",
"integrity": "sha512-cR5cB0bLS87vd0iu7Nud/4x2EH1Vs0aIgwGWd0eH/5SAw0rrDNU81PiOde+rnMXETbxvSVfOZuLRyn7/WQtGUg==",
"version": "10.3.5",
"resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-10.3.5.tgz",
"integrity": "sha512-6w383LUBFHoZ0eFODqEHN2NoIRUwbTd37Hc1KqtZZihhFUzscC/0LMAV20o9LdfS/Xjog5ShNTxvOHuzNBnE4A==",
"dependencies": {
"iterare": "1.2.1",
"object-hash": "3.0.0",
@@ -4693,9 +4693,9 @@
}
},
"node_modules/@types/node": {
"version": "20.11.28",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.28.tgz",
"integrity": "sha512-M/GPWVS2wLkSkNHVeLkrF2fD5Lx5UC4PxA0uZcKc6QqbIQUJyW1jVjueJYi1z8n0I5PxYrtpnPnWglE+y9A0KA==",
"version": "20.11.30",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.30.tgz",
"integrity": "sha512-dHM6ZxwlmuZaRmUPfv1p+KrdD1Dci04FbdEm/9wEMouFqxYoFl5aMkt0VMAUtYRQDyYvD41WJLukhq/ha3YuTw==",
"dependencies": {
"undici-types": "~5.26.4"
}
@@ -4920,16 +4920,16 @@
"dev": true
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.2.0.tgz",
"integrity": "sha512-mdekAHOqS9UjlmyF/LSs6AIEvfceV749GFxoBAjwAv0nkevfKHWQFDMcBZWUiIC5ft6ePWivXoS36aKQ0Cy3sw==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.3.1.tgz",
"integrity": "sha512-STEDMVQGww5lhCuNXVSQfbfuNII5E08QWkvAw5Qwf+bj2WT+JkG1uc+5/vXA3AOYMDHVOSpL+9rcbEUiHIm2dw==",
"dev": true,
"dependencies": {
"@eslint-community/regexpp": "^4.5.1",
"@typescript-eslint/scope-manager": "7.2.0",
"@typescript-eslint/type-utils": "7.2.0",
"@typescript-eslint/utils": "7.2.0",
"@typescript-eslint/visitor-keys": "7.2.0",
"@typescript-eslint/scope-manager": "7.3.1",
"@typescript-eslint/type-utils": "7.3.1",
"@typescript-eslint/utils": "7.3.1",
"@typescript-eslint/visitor-keys": "7.3.1",
"debug": "^4.3.4",
"graphemer": "^1.4.0",
"ignore": "^5.2.4",
@@ -4938,7 +4938,7 @@
"ts-api-utils": "^1.0.1"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
"node": "^18.18.0 || >=20.0.0"
},
"funding": {
"type": "opencollective",
@@ -4955,19 +4955,19 @@
}
},
"node_modules/@typescript-eslint/parser": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.2.0.tgz",
"integrity": "sha512-5FKsVcHTk6TafQKQbuIVkXq58Fnbkd2wDL4LB7AURN7RUOu1utVP+G8+6u3ZhEroW3DF6hyo3ZEXxgKgp4KeCg==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.3.1.tgz",
"integrity": "sha512-Rq49+pq7viTRCH48XAbTA+wdLRrB/3sRq4Lpk0oGDm0VmnjBrAOVXH/Laalmwsv2VpekiEfVFwJYVk6/e8uvQw==",
"dev": true,
"dependencies": {
"@typescript-eslint/scope-manager": "7.2.0",
"@typescript-eslint/types": "7.2.0",
"@typescript-eslint/typescript-estree": "7.2.0",
"@typescript-eslint/visitor-keys": "7.2.0",
"@typescript-eslint/scope-manager": "7.3.1",
"@typescript-eslint/types": "7.3.1",
"@typescript-eslint/typescript-estree": "7.3.1",
"@typescript-eslint/visitor-keys": "7.3.1",
"debug": "^4.3.4"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
"node": "^18.18.0 || >=20.0.0"
},
"funding": {
"type": "opencollective",
@@ -4983,16 +4983,16 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.2.0.tgz",
"integrity": "sha512-Qh976RbQM/fYtjx9hs4XkayYujB/aPwglw2choHmf3zBjB4qOywWSdt9+KLRdHubGcoSwBnXUH2sR3hkyaERRg==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.3.1.tgz",
"integrity": "sha512-fVS6fPxldsKY2nFvyT7IP78UO1/I2huG+AYu5AMjCT9wtl6JFiDnsv4uad4jQ0GTFzcUV5HShVeN96/17bTBag==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "7.2.0",
"@typescript-eslint/visitor-keys": "7.2.0"
"@typescript-eslint/types": "7.3.1",
"@typescript-eslint/visitor-keys": "7.3.1"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
"node": "^18.18.0 || >=20.0.0"
},
"funding": {
"type": "opencollective",
@@ -5000,18 +5000,18 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.2.0.tgz",
"integrity": "sha512-xHi51adBHo9O9330J8GQYQwrKBqbIPJGZZVQTHHmy200hvkLZFWJIFtAG/7IYTWUyun6DE6w5InDReePJYJlJA==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.3.1.tgz",
"integrity": "sha512-iFhaysxFsMDQlzJn+vr3OrxN8NmdQkHks4WaqD4QBnt5hsq234wcYdyQ9uquzJJIDAj5W4wQne3yEsYA6OmXGw==",
"dev": true,
"dependencies": {
"@typescript-eslint/typescript-estree": "7.2.0",
"@typescript-eslint/utils": "7.2.0",
"@typescript-eslint/typescript-estree": "7.3.1",
"@typescript-eslint/utils": "7.3.1",
"debug": "^4.3.4",
"ts-api-utils": "^1.0.1"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
"node": "^18.18.0 || >=20.0.0"
},
"funding": {
"type": "opencollective",
@@ -5027,12 +5027,12 @@
}
},
"node_modules/@typescript-eslint/types": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.2.0.tgz",
"integrity": "sha512-XFtUHPI/abFhm4cbCDc5Ykc8npOKBSJePY3a3s+lwumt7XWJuzP5cZcfZ610MIPHjQjNsOLlYK8ASPaNG8UiyA==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.3.1.tgz",
"integrity": "sha512-2tUf3uWggBDl4S4183nivWQ2HqceOZh1U4hhu4p1tPiIJoRRXrab7Y+Y0p+dozYwZVvLPRI6r5wKe9kToF9FIw==",
"dev": true,
"engines": {
"node": "^16.0.0 || >=18.0.0"
"node": "^18.18.0 || >=20.0.0"
},
"funding": {
"type": "opencollective",
@@ -5040,13 +5040,13 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.2.0.tgz",
"integrity": "sha512-cyxS5WQQCoBwSakpMrvMXuMDEbhOo9bNHHrNcEWis6XHx6KF518tkF1wBvKIn/tpq5ZpUYK7Bdklu8qY0MsFIA==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.3.1.tgz",
"integrity": "sha512-tLpuqM46LVkduWP7JO7yVoWshpJuJzxDOPYIVWUUZbW+4dBpgGeUdl/fQkhuV0A8eGnphYw3pp8d2EnvPOfxmQ==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "7.2.0",
"@typescript-eslint/visitor-keys": "7.2.0",
"@typescript-eslint/types": "7.3.1",
"@typescript-eslint/visitor-keys": "7.3.1",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
@@ -5055,7 +5055,7 @@
"ts-api-utils": "^1.0.1"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
"node": "^18.18.0 || >=20.0.0"
},
"funding": {
"type": "opencollective",
@@ -5092,21 +5092,21 @@
}
},
"node_modules/@typescript-eslint/utils": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.2.0.tgz",
"integrity": "sha512-YfHpnMAGb1Eekpm3XRK8hcMwGLGsnT6L+7b2XyRv6ouDuJU1tZir1GS2i0+VXRatMwSI1/UfcyPe53ADkU+IuA==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.3.1.tgz",
"integrity": "sha512-jIERm/6bYQ9HkynYlNZvXpzmXWZGhMbrOvq3jJzOSOlKXsVjrrolzWBjDW6/TvT5Q3WqaN4EkmcfdQwi9tDjBQ==",
"dev": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.4.0",
"@types/json-schema": "^7.0.12",
"@types/semver": "^7.5.0",
"@typescript-eslint/scope-manager": "7.2.0",
"@typescript-eslint/types": "7.2.0",
"@typescript-eslint/typescript-estree": "7.2.0",
"@typescript-eslint/scope-manager": "7.3.1",
"@typescript-eslint/types": "7.3.1",
"@typescript-eslint/typescript-estree": "7.3.1",
"semver": "^7.5.4"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
"node": "^18.18.0 || >=20.0.0"
},
"funding": {
"type": "opencollective",
@@ -5117,16 +5117,16 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.2.0.tgz",
"integrity": "sha512-c6EIQRHhcpl6+tO8EMR+kjkkV+ugUNXOmeASA1rlzkd8EPIriavpWoiEz1HR/VLhbVIdhqnV6E7JZm00cBDx2A==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.3.1.tgz",
"integrity": "sha512-9RMXwQF8knsZvfv9tdi+4D/j7dMG28X/wMJ8Jj6eOHyHWwDW4ngQJcqEczSsqIKKjFiLFr40Mnr7a5ulDD3vmw==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "7.2.0",
"@typescript-eslint/types": "7.3.1",
"eslint-visitor-keys": "^3.4.1"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
"node": "^18.18.0 || >=20.0.0"
},
"funding": {
"type": "opencollective",
@@ -7774,13 +7774,13 @@
}
},
"node_modules/express": {
"version": "4.18.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
"integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==",
"version": "4.18.3",
"resolved": "https://registry.npmjs.org/express/-/express-4.18.3.tgz",
"integrity": "sha512-6VyCijWQ+9O7WuVMTRBTl+cjNNIzD5cY5mQ1WM8r/LEkI2u8EYpOotESNwzNlyCn3g+dmjKYI6BmNneSr/FSRw==",
"dependencies": {
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
"body-parser": "1.20.1",
"body-parser": "1.20.2",
"content-disposition": "0.5.4",
"content-type": "~1.0.4",
"cookie": "0.5.0",
@@ -7814,29 +7814,6 @@
"node": ">= 0.10.0"
}
},
"node_modules/express/node_modules/body-parser": {
"version": "1.20.1",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
"integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
"dependencies": {
"bytes": "3.1.2",
"content-type": "~1.0.4",
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"on-finished": "2.4.1",
"qs": "6.11.0",
"raw-body": "2.5.1",
"type-is": "~1.6.18",
"unpipe": "1.0.0"
},
"engines": {
"node": ">= 0.8",
"npm": "1.2.8000 || >= 1.4.16"
}
},
"node_modules/express/node_modules/cookie": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
@@ -7863,20 +7840,6 @@
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
},
"node_modules/express/node_modules/raw-body": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
"integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
"dependencies": {
"bytes": "3.1.2",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"unpipe": "1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/extend": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
@@ -8786,9 +8749,9 @@
}
},
"node_modules/i18n-iso-countries": {
"version": "7.10.1",
"resolved": "https://registry.npmjs.org/i18n-iso-countries/-/i18n-iso-countries-7.10.1.tgz",
"integrity": "sha512-9DXmAMkfGcGNE+E/2fE85UUjjkPeT0LHMA8d+kcxXiO+s50W28lxiICel8f8qWZmCNic1cuhN1+nw7ZazMQJFA==",
"version": "7.11.0",
"resolved": "https://registry.npmjs.org/i18n-iso-countries/-/i18n-iso-countries-7.11.0.tgz",
"integrity": "sha512-MP2+aAJwvBTuruaMEj+mPEhu4D9rn03GkkbuP8/xkvNzhVwNe2cAg1ivkL5Oj+vwqEwvIcf5C7Q+5Y/UZNLBHw==",
"dependencies": {
"diacritics": "1.3.0"
},
@@ -12597,9 +12560,9 @@
"integrity": "sha512-YiuPbxpCj4hD9Qs06hGAz/OZhQ0eDuALN0lRWJez0eD/RevzKqGdUx1IOMUnXgpr+sXZLq3g8ERwbAH0bCb8vg=="
},
"node_modules/socket.io": {
"version": "4.7.4",
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.4.tgz",
"integrity": "sha512-DcotgfP1Zg9iP/dH9zvAQcWrE0TtbMVwXmlV4T4mqsvY+gw+LqUGPfx2AoVyRk0FLME+GQhufDMyacFmw7ksqw==",
"version": "4.7.5",
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.5.tgz",
"integrity": "sha512-DmeAkF6cwM9jSfmp6Dr/5/mfMwb5Z5qRrSXLpo3Fq5SqyU8CMF15jIN4ZhfSwu35ksM1qmHZDQ/DK5XTccSTvA==",
"dependencies": {
"accepts": "~1.3.4",
"base64id": "~2.0.0",
@@ -12714,9 +12677,9 @@
"dev": true
},
"node_modules/sql-formatter": {
"version": "15.2.0",
"resolved": "https://registry.npmjs.org/sql-formatter/-/sql-formatter-15.2.0.tgz",
"integrity": "sha512-k1gDOblvmtzmrBT687Y167ElwQI/8KrlhfKeIUXsi6jw7Rp5n3G8TkMFZF0Z9NG7rAzHKXUlJ8kfmcIfMf5lFg==",
"version": "15.3.0",
"resolved": "https://registry.npmjs.org/sql-formatter/-/sql-formatter-15.3.0.tgz",
"integrity": "sha512-1aDYVEX+dwOSCkRYns4HEGupRZoaivcsNpU4IzR+MVC+cWFYK9/dce7pr4aId4+ED2iK9PNs3j1Vdf8C+SIvDg==",
"dev": true,
"dependencies": {
"argparse": "^2.0.1",
@@ -13967,9 +13930,9 @@
}
},
"node_modules/typescript": {
"version": "5.4.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz",
"integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==",
"version": "5.4.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz",
"integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==",
"devOptional": true,
"bin": {
"tsc": "bin/tsc",
@@ -15116,9 +15079,9 @@
}
},
"@babel/runtime": {
"version": "7.24.0",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.0.tgz",
"integrity": "sha512-Chk32uHMg6TnQdvw2e9IlqPpFX/6NLuK0Ys2PqLb7/gL5uFn9mXvK715FGLlOLQrcO4qIkNHkvPGktzzXexsFw==",
"version": "7.24.1",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.1.tgz",
"integrity": "sha512-+BIznRzyqBf+2wCTxcKE3wDjfGeCoVE61KSHGpkzqrLi8qxqFwBeUFyId2cxkTmm55fzDGnm0+yCxaxygrLUnQ==",
"requires": {
"regenerator-runtime": "^0.14.0"
}
@@ -16192,9 +16155,9 @@
}
},
"@nestjs/common": {
"version": "10.3.3",
"resolved": "https://registry.npmjs.org/@nestjs/common/-/common-10.3.3.tgz",
"integrity": "sha512-LAkTe8/CF0uNWM0ecuDwUNTHCi1lVSITmmR4FQ6Ftz1E7ujQCnJ5pMRzd8JRN14vdBkxZZ8VbVF0BDUKoKNxMQ==",
"version": "10.3.5",
"resolved": "https://registry.npmjs.org/@nestjs/common/-/common-10.3.5.tgz",
"integrity": "sha512-XWxbDf2ey/jAyEa3/XpckgfzJZ9j3I05ZkEFx7cAlebFuVKeq5UDDb5Sq9O7hMmbH9xdQj3pYT19SSj01hKeug==",
"requires": {
"iterare": "1.2.1",
"tslib": "2.6.2",
@@ -16220,9 +16183,9 @@
}
},
"@nestjs/core": {
"version": "10.3.3",
"resolved": "https://registry.npmjs.org/@nestjs/core/-/core-10.3.3.tgz",
"integrity": "sha512-kxJWggQAPX3RuZx9JVec69eSLaYLNIox2emkZJpfBJ5Qq7cAq7edQIt1r4LGjTKq6kFubNTPsqhWf5y7yFRBPw==",
"version": "10.3.5",
"resolved": "https://registry.npmjs.org/@nestjs/core/-/core-10.3.5.tgz",
"integrity": "sha512-U7SrGD9/Mu4eUtxfZYiGdY38FcksEyJegs4dQZ8B19nnusw0aTocPEy4HVsmx0LLO4sG+fBLLYzCDDr9kFwXAQ==",
"requires": {
"@nuxtjs/opencollective": "0.3.2",
"fast-safe-stringify": "2.1.1",
@@ -16247,23 +16210,23 @@
"requires": {}
},
"@nestjs/platform-express": {
"version": "10.3.3",
"resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-10.3.3.tgz",
"integrity": "sha512-GGKSEU48Os7nYFIsUM0nutuFUGn5AbeP8gzFBiBCAtiuJWrXZXpZ58pMBYxAbMf7IrcOZFInHEukjHGAQU0OZw==",
"version": "10.3.5",
"resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-10.3.5.tgz",
"integrity": "sha512-IhVomwLvdLlv4zCdQK2ROT/nInk1i8m4K48lAUHJV5UVktgVmg0WbQga2/9KywaTjNbx+eWhZXXFii+vtFRAOw==",
"requires": {
"body-parser": "1.20.2",
"cors": "2.8.5",
"express": "4.18.2",
"express": "4.18.3",
"multer": "1.4.4-lts.1",
"tslib": "2.6.2"
}
},
"@nestjs/platform-socket.io": {
"version": "10.3.3",
"resolved": "https://registry.npmjs.org/@nestjs/platform-socket.io/-/platform-socket.io-10.3.3.tgz",
"integrity": "sha512-QqM9BMTdYPvXOqx3oWrv130HOtc2krPvfgqgDsPWkBLfR+TssrA5QDaTW8HSjEQAfmugvHwhEAAU4+yXRl6tKg==",
"version": "10.3.5",
"resolved": "https://registry.npmjs.org/@nestjs/platform-socket.io/-/platform-socket.io-10.3.5.tgz",
"integrity": "sha512-G2N3sTd9tZ7XQQ7RlrpaQdt1/IBztVHuKg686QmBTLVlRHZ1AMOmXouBk+q5SINT1XURiABa8tQh1Ydx0OEh9w==",
"requires": {
"socket.io": "4.7.4",
"socket.io": "4.7.5",
"tslib": "2.6.2"
}
},
@@ -16311,9 +16274,9 @@
}
},
"@nestjs/testing": {
"version": "10.3.3",
"resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-10.3.3.tgz",
"integrity": "sha512-kX20GfjAImL5grd/i69uD/x7sc00BaqGcP2dRG3ilqshQUuy5DOmspLCr3a2C8xmVU7kzK4spT0oTxhe6WcCAA==",
"version": "10.3.5",
"resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-10.3.5.tgz",
"integrity": "sha512-j30/lxH0BayeDTigapYtQn/XhMRR7CzlFsm3dHoWViWQv0qT1r2ffe3927BbBLX3N/ZzglE10OAqW06ADZV8dw==",
"dev": true,
"requires": {
"tslib": "2.6.2"
@@ -16328,9 +16291,9 @@
}
},
"@nestjs/websockets": {
"version": "10.3.3",
"resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-10.3.3.tgz",
"integrity": "sha512-cR5cB0bLS87vd0iu7Nud/4x2EH1Vs0aIgwGWd0eH/5SAw0rrDNU81PiOde+rnMXETbxvSVfOZuLRyn7/WQtGUg==",
"version": "10.3.5",
"resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-10.3.5.tgz",
"integrity": "sha512-6w383LUBFHoZ0eFODqEHN2NoIRUwbTd37Hc1KqtZZihhFUzscC/0LMAV20o9LdfS/Xjog5ShNTxvOHuzNBnE4A==",
"requires": {
"iterare": "1.2.1",
"object-hash": "3.0.0",
@@ -17756,9 +17719,9 @@
}
},
"@types/node": {
"version": "20.11.28",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.28.tgz",
"integrity": "sha512-M/GPWVS2wLkSkNHVeLkrF2fD5Lx5UC4PxA0uZcKc6QqbIQUJyW1jVjueJYi1z8n0I5PxYrtpnPnWglE+y9A0KA==",
"version": "20.11.30",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.30.tgz",
"integrity": "sha512-dHM6ZxwlmuZaRmUPfv1p+KrdD1Dci04FbdEm/9wEMouFqxYoFl5aMkt0VMAUtYRQDyYvD41WJLukhq/ha3YuTw==",
"requires": {
"undici-types": "~5.26.4"
}
@@ -17970,16 +17933,16 @@
"dev": true
},
"@typescript-eslint/eslint-plugin": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.2.0.tgz",
"integrity": "sha512-mdekAHOqS9UjlmyF/LSs6AIEvfceV749GFxoBAjwAv0nkevfKHWQFDMcBZWUiIC5ft6ePWivXoS36aKQ0Cy3sw==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.3.1.tgz",
"integrity": "sha512-STEDMVQGww5lhCuNXVSQfbfuNII5E08QWkvAw5Qwf+bj2WT+JkG1uc+5/vXA3AOYMDHVOSpL+9rcbEUiHIm2dw==",
"dev": true,
"requires": {
"@eslint-community/regexpp": "^4.5.1",
"@typescript-eslint/scope-manager": "7.2.0",
"@typescript-eslint/type-utils": "7.2.0",
"@typescript-eslint/utils": "7.2.0",
"@typescript-eslint/visitor-keys": "7.2.0",
"@typescript-eslint/scope-manager": "7.3.1",
"@typescript-eslint/type-utils": "7.3.1",
"@typescript-eslint/utils": "7.3.1",
"@typescript-eslint/visitor-keys": "7.3.1",
"debug": "^4.3.4",
"graphemer": "^1.4.0",
"ignore": "^5.2.4",
@@ -17989,54 +17952,54 @@
}
},
"@typescript-eslint/parser": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.2.0.tgz",
"integrity": "sha512-5FKsVcHTk6TafQKQbuIVkXq58Fnbkd2wDL4LB7AURN7RUOu1utVP+G8+6u3ZhEroW3DF6hyo3ZEXxgKgp4KeCg==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.3.1.tgz",
"integrity": "sha512-Rq49+pq7viTRCH48XAbTA+wdLRrB/3sRq4Lpk0oGDm0VmnjBrAOVXH/Laalmwsv2VpekiEfVFwJYVk6/e8uvQw==",
"dev": true,
"requires": {
"@typescript-eslint/scope-manager": "7.2.0",
"@typescript-eslint/types": "7.2.0",
"@typescript-eslint/typescript-estree": "7.2.0",
"@typescript-eslint/visitor-keys": "7.2.0",
"@typescript-eslint/scope-manager": "7.3.1",
"@typescript-eslint/types": "7.3.1",
"@typescript-eslint/typescript-estree": "7.3.1",
"@typescript-eslint/visitor-keys": "7.3.1",
"debug": "^4.3.4"
}
},
"@typescript-eslint/scope-manager": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.2.0.tgz",
"integrity": "sha512-Qh976RbQM/fYtjx9hs4XkayYujB/aPwglw2choHmf3zBjB4qOywWSdt9+KLRdHubGcoSwBnXUH2sR3hkyaERRg==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.3.1.tgz",
"integrity": "sha512-fVS6fPxldsKY2nFvyT7IP78UO1/I2huG+AYu5AMjCT9wtl6JFiDnsv4uad4jQ0GTFzcUV5HShVeN96/17bTBag==",
"dev": true,
"requires": {
"@typescript-eslint/types": "7.2.0",
"@typescript-eslint/visitor-keys": "7.2.0"
"@typescript-eslint/types": "7.3.1",
"@typescript-eslint/visitor-keys": "7.3.1"
}
},
"@typescript-eslint/type-utils": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.2.0.tgz",
"integrity": "sha512-xHi51adBHo9O9330J8GQYQwrKBqbIPJGZZVQTHHmy200hvkLZFWJIFtAG/7IYTWUyun6DE6w5InDReePJYJlJA==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.3.1.tgz",
"integrity": "sha512-iFhaysxFsMDQlzJn+vr3OrxN8NmdQkHks4WaqD4QBnt5hsq234wcYdyQ9uquzJJIDAj5W4wQne3yEsYA6OmXGw==",
"dev": true,
"requires": {
"@typescript-eslint/typescript-estree": "7.2.0",
"@typescript-eslint/utils": "7.2.0",
"@typescript-eslint/typescript-estree": "7.3.1",
"@typescript-eslint/utils": "7.3.1",
"debug": "^4.3.4",
"ts-api-utils": "^1.0.1"
}
},
"@typescript-eslint/types": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.2.0.tgz",
"integrity": "sha512-XFtUHPI/abFhm4cbCDc5Ykc8npOKBSJePY3a3s+lwumt7XWJuzP5cZcfZ610MIPHjQjNsOLlYK8ASPaNG8UiyA==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.3.1.tgz",
"integrity": "sha512-2tUf3uWggBDl4S4183nivWQ2HqceOZh1U4hhu4p1tPiIJoRRXrab7Y+Y0p+dozYwZVvLPRI6r5wKe9kToF9FIw==",
"dev": true
},
"@typescript-eslint/typescript-estree": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.2.0.tgz",
"integrity": "sha512-cyxS5WQQCoBwSakpMrvMXuMDEbhOo9bNHHrNcEWis6XHx6KF518tkF1wBvKIn/tpq5ZpUYK7Bdklu8qY0MsFIA==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.3.1.tgz",
"integrity": "sha512-tLpuqM46LVkduWP7JO7yVoWshpJuJzxDOPYIVWUUZbW+4dBpgGeUdl/fQkhuV0A8eGnphYw3pp8d2EnvPOfxmQ==",
"dev": true,
"requires": {
"@typescript-eslint/types": "7.2.0",
"@typescript-eslint/visitor-keys": "7.2.0",
"@typescript-eslint/types": "7.3.1",
"@typescript-eslint/visitor-keys": "7.3.1",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
@@ -18066,27 +18029,27 @@
}
},
"@typescript-eslint/utils": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.2.0.tgz",
"integrity": "sha512-YfHpnMAGb1Eekpm3XRK8hcMwGLGsnT6L+7b2XyRv6ouDuJU1tZir1GS2i0+VXRatMwSI1/UfcyPe53ADkU+IuA==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.3.1.tgz",
"integrity": "sha512-jIERm/6bYQ9HkynYlNZvXpzmXWZGhMbrOvq3jJzOSOlKXsVjrrolzWBjDW6/TvT5Q3WqaN4EkmcfdQwi9tDjBQ==",
"dev": true,
"requires": {
"@eslint-community/eslint-utils": "^4.4.0",
"@types/json-schema": "^7.0.12",
"@types/semver": "^7.5.0",
"@typescript-eslint/scope-manager": "7.2.0",
"@typescript-eslint/types": "7.2.0",
"@typescript-eslint/typescript-estree": "7.2.0",
"@typescript-eslint/scope-manager": "7.3.1",
"@typescript-eslint/types": "7.3.1",
"@typescript-eslint/typescript-estree": "7.3.1",
"semver": "^7.5.4"
}
},
"@typescript-eslint/visitor-keys": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.2.0.tgz",
"integrity": "sha512-c6EIQRHhcpl6+tO8EMR+kjkkV+ugUNXOmeASA1rlzkd8EPIriavpWoiEz1HR/VLhbVIdhqnV6E7JZm00cBDx2A==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.3.1.tgz",
"integrity": "sha512-9RMXwQF8knsZvfv9tdi+4D/j7dMG28X/wMJ8Jj6eOHyHWwDW4ngQJcqEczSsqIKKjFiLFr40Mnr7a5ulDD3vmw==",
"dev": true,
"requires": {
"@typescript-eslint/types": "7.2.0",
"@typescript-eslint/types": "7.3.1",
"eslint-visitor-keys": "^3.4.1"
}
},
@@ -20052,13 +20015,13 @@
}
},
"express": {
"version": "4.18.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
"integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==",
"version": "4.18.3",
"resolved": "https://registry.npmjs.org/express/-/express-4.18.3.tgz",
"integrity": "sha512-6VyCijWQ+9O7WuVMTRBTl+cjNNIzD5cY5mQ1WM8r/LEkI2u8EYpOotESNwzNlyCn3g+dmjKYI6BmNneSr/FSRw==",
"requires": {
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
"body-parser": "1.20.1",
"body-parser": "1.20.2",
"content-disposition": "0.5.4",
"content-type": "~1.0.4",
"cookie": "0.5.0",
@@ -20089,25 +20052,6 @@
"vary": "~1.1.2"
},
"dependencies": {
"body-parser": {
"version": "1.20.1",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
"integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
"requires": {
"bytes": "3.1.2",
"content-type": "~1.0.4",
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"on-finished": "2.4.1",
"qs": "6.11.0",
"raw-body": "2.5.1",
"type-is": "~1.6.18",
"unpipe": "1.0.0"
}
},
"cookie": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
@@ -20130,17 +20074,6 @@
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
},
"raw-body": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
"integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
"requires": {
"bytes": "3.1.2",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"unpipe": "1.0.0"
}
}
}
},
@@ -20827,9 +20760,9 @@
"dev": true
},
"i18n-iso-countries": {
"version": "7.10.1",
"resolved": "https://registry.npmjs.org/i18n-iso-countries/-/i18n-iso-countries-7.10.1.tgz",
"integrity": "sha512-9DXmAMkfGcGNE+E/2fE85UUjjkPeT0LHMA8d+kcxXiO+s50W28lxiICel8f8qWZmCNic1cuhN1+nw7ZazMQJFA==",
"version": "7.11.0",
"resolved": "https://registry.npmjs.org/i18n-iso-countries/-/i18n-iso-countries-7.11.0.tgz",
"integrity": "sha512-MP2+aAJwvBTuruaMEj+mPEhu4D9rn03GkkbuP8/xkvNzhVwNe2cAg1ivkL5Oj+vwqEwvIcf5C7Q+5Y/UZNLBHw==",
"requires": {
"diacritics": "1.3.0"
}
@@ -23712,9 +23645,9 @@
"integrity": "sha512-YiuPbxpCj4hD9Qs06hGAz/OZhQ0eDuALN0lRWJez0eD/RevzKqGdUx1IOMUnXgpr+sXZLq3g8ERwbAH0bCb8vg=="
},
"socket.io": {
"version": "4.7.4",
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.4.tgz",
"integrity": "sha512-DcotgfP1Zg9iP/dH9zvAQcWrE0TtbMVwXmlV4T4mqsvY+gw+LqUGPfx2AoVyRk0FLME+GQhufDMyacFmw7ksqw==",
"version": "4.7.5",
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.5.tgz",
"integrity": "sha512-DmeAkF6cwM9jSfmp6Dr/5/mfMwb5Z5qRrSXLpo3Fq5SqyU8CMF15jIN4ZhfSwu35ksM1qmHZDQ/DK5XTccSTvA==",
"requires": {
"accepts": "~1.3.4",
"base64id": "~2.0.0",
@@ -23816,9 +23749,9 @@
"dev": true
},
"sql-formatter": {
"version": "15.2.0",
"resolved": "https://registry.npmjs.org/sql-formatter/-/sql-formatter-15.2.0.tgz",
"integrity": "sha512-k1gDOblvmtzmrBT687Y167ElwQI/8KrlhfKeIUXsi6jw7Rp5n3G8TkMFZF0Z9NG7rAzHKXUlJ8kfmcIfMf5lFg==",
"version": "15.3.0",
"resolved": "https://registry.npmjs.org/sql-formatter/-/sql-formatter-15.3.0.tgz",
"integrity": "sha512-1aDYVEX+dwOSCkRYns4HEGupRZoaivcsNpU4IzR+MVC+cWFYK9/dce7pr4aId4+ED2iK9PNs3j1Vdf8C+SIvDg==",
"dev": true,
"requires": {
"argparse": "^2.0.1",
@@ -24671,9 +24604,9 @@
}
},
"typescript": {
"version": "5.4.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz",
"integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==",
"version": "5.4.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz",
"integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==",
"devOptional": true
},
"ua-parser-js": {
+6 -3
View File
@@ -51,6 +51,7 @@ import { ILibraryRepository } from 'src/interfaces/library.interface';
import { IMachineLearningRepository } from 'src/interfaces/machine-learning.interface';
import { IMediaRepository } from 'src/interfaces/media.interface';
import { IMetadataRepository } from 'src/interfaces/metadata.interface';
import { IMetricRepository } from 'src/interfaces/metric.interface';
import { IMoveRepository } from 'src/interfaces/move.interface';
import { IPartnerRepository } from 'src/interfaces/partner.interface';
import { IPersonRepository } from 'src/interfaces/person.interface';
@@ -77,18 +78,19 @@ import { AuditRepository } from 'src/repositories/audit.repository';
import { CryptoRepository } from 'src/repositories/crypto.repository';
import { DatabaseRepository } from 'src/repositories/database.repository';
import { EventRepository } from 'src/repositories/event.repository';
import { FilesystemProvider } from 'src/repositories/filesystem.provider';
import { JobRepository } from 'src/repositories/job.repository';
import { LibraryRepository } from 'src/repositories/library.repository';
import { MachineLearningRepository } from 'src/repositories/machine-learning.repository';
import { MediaRepository } from 'src/repositories/media.repository';
import { MetadataRepository } from 'src/repositories/metadata.repository';
import { MetricRepository } from 'src/repositories/metric.repository';
import { MoveRepository } from 'src/repositories/move.repository';
import { PartnerRepository } from 'src/repositories/partner.repository';
import { PersonRepository } from 'src/repositories/person.repository';
import { SearchRepository } from 'src/repositories/search.repository';
import { ServerInfoRepository } from 'src/repositories/server-info.repository';
import { SharedLinkRepository } from 'src/repositories/shared-link.repository';
import { StorageRepository } from 'src/repositories/storage.repository';
import { SystemConfigRepository } from 'src/repositories/system-config.repository';
import { SystemMetadataRepository } from 'src/repositories/system-metadata.repository';
import { TagRepository } from 'src/repositories/tag.repository';
@@ -163,7 +165,6 @@ const controllers = [
const services: Provider[] = [
ApiService,
MicroservicesService,
APIKeyService,
ActivityService,
AlbumService,
@@ -208,13 +209,14 @@ const repositories: Provider[] = [
{ provide: IKeyRepository, useClass: ApiKeyRepository },
{ provide: IMachineLearningRepository, useClass: MachineLearningRepository },
{ provide: IMetadataRepository, useClass: MetadataRepository },
{ provide: IMetricRepository, useClass: MetricRepository },
{ provide: IMoveRepository, useClass: MoveRepository },
{ provide: IPartnerRepository, useClass: PartnerRepository },
{ provide: IPersonRepository, useClass: PersonRepository },
{ provide: IServerInfoRepository, useClass: ServerInfoRepository },
{ provide: ISharedLinkRepository, useClass: SharedLinkRepository },
{ provide: ISearchRepository, useClass: SearchRepository },
{ provide: IStorageRepository, useClass: FilesystemProvider },
{ provide: IStorageRepository, useClass: StorageRepository },
{ provide: ISystemConfigRepository, useClass: SystemConfigRepository },
{ provide: ISystemMetadataRepository, useClass: SystemMetadataRepository },
{ provide: ITagRepository, useClass: TagRepository },
@@ -277,6 +279,7 @@ export class ImmichAdminModule {}
EventEmitterModule.forRoot(),
TypeOrmModule.forRoot(databaseConfig),
TypeOrmModule.forFeature(databaseEntities),
OpenTelemetryModule.forRoot(otelConfig),
],
controllers: [...controllers],
providers: [...services, ...repositories, ...middleware, SchedulerRegistry],
+39 -26
View File
@@ -4,7 +4,6 @@ import { SystemConfigCore } from 'src/cores/system-config.core';
import { AssetEntity } from 'src/entities/asset.entity';
import { AssetPathType, PathType, PersonPathType } from 'src/entities/move.entity';
import { PersonEntity } from 'src/entities/person.entity';
import { ImageFormat } from 'src/entities/system-config.entity';
import { IAssetRepository } from 'src/interfaces/asset.interface';
import { ICryptoRepository } from 'src/interfaces/crypto.interface';
import { IMoveRepository } from 'src/interfaces/move.interface';
@@ -35,8 +34,7 @@ export interface MoveRequest {
};
}
export type GeneratedImageType = AssetPathType.PREVIEW | AssetPathType.THUMBNAIL;
export type GeneratedAssetType = GeneratedImageType | AssetPathType.ENCODED_VIDEO;
type GeneratedAssetPath = AssetPathType.JPEG_THUMBNAIL | AssetPathType.WEBP_THUMBNAIL | AssetPathType.ENCODED_VIDEO;
let instance: StorageCore | null;
@@ -96,8 +94,12 @@ export class StorageCore {
return StorageCore.getNestedPath(StorageFolder.THUMBNAILS, person.ownerId, `${person.id}.jpeg`);
}
static getImagePath(asset: AssetEntity, type: GeneratedImageType, format: ImageFormat) {
return StorageCore.getNestedPath(StorageFolder.THUMBNAILS, asset.ownerId, `${asset.id}-${type}.${format}`);
static getLargeThumbnailPath(asset: AssetEntity) {
return StorageCore.getNestedPath(StorageFolder.THUMBNAILS, asset.ownerId, `${asset.id}.jpeg`);
}
static getSmallThumbnailPath(asset: AssetEntity) {
return StorageCore.getNestedPath(StorageFolder.THUMBNAILS, asset.ownerId, `${asset.id}.webp`);
}
static getEncodedVideoPath(asset: AssetEntity) {
@@ -120,23 +122,34 @@ export class StorageCore {
return path.startsWith(THUMBNAIL_DIR) || path.startsWith(ENCODED_VIDEO_DIR);
}
async moveAssetImage(asset: AssetEntity, pathType: GeneratedImageType, format: ImageFormat) {
const { id: entityId, previewPath, thumbnailPath } = asset;
return this.moveFile({
entityId,
pathType,
oldPath: pathType === AssetPathType.PREVIEW ? previewPath : thumbnailPath,
newPath: StorageCore.getImagePath(asset, AssetPathType.THUMBNAIL, format),
});
}
async moveAssetVideo(asset: AssetEntity) {
return this.moveFile({
entityId: asset.id,
pathType: AssetPathType.ENCODED_VIDEO,
oldPath: asset.encodedVideoPath,
newPath: StorageCore.getEncodedVideoPath(asset),
});
async moveAssetFile(asset: AssetEntity, pathType: GeneratedAssetPath) {
const { id: entityId, resizePath, webpPath, encodedVideoPath } = asset;
switch (pathType) {
case AssetPathType.JPEG_THUMBNAIL: {
return this.moveFile({
entityId,
pathType,
oldPath: resizePath,
newPath: StorageCore.getLargeThumbnailPath(asset),
});
}
case AssetPathType.WEBP_THUMBNAIL: {
return this.moveFile({
entityId,
pathType,
oldPath: webpPath,
newPath: StorageCore.getSmallThumbnailPath(asset),
});
}
case AssetPathType.ENCODED_VIDEO: {
return this.moveFile({
entityId,
pathType,
oldPath: encodedVideoPath,
newPath: StorageCore.getEncodedVideoPath(asset),
});
}
}
}
async movePersonFile(person: PersonEntity, pathType: PersonPathType) {
@@ -275,11 +288,11 @@ export class StorageCore {
case AssetPathType.ORIGINAL: {
return this.assetRepository.update({ id, originalPath: newPath });
}
case AssetPathType.PREVIEW: {
return this.assetRepository.update({ id, previewPath: newPath });
case AssetPathType.JPEG_THUMBNAIL: {
return this.assetRepository.update({ id, resizePath: newPath });
}
case AssetPathType.THUMBNAIL: {
return this.assetRepository.update({ id, thumbnailPath: newPath });
case AssetPathType.WEBP_THUMBNAIL: {
return this.assetRepository.update({ id, webpPath: newPath });
}
case AssetPathType.ENCODED_VIDEO: {
return this.assetRepository.update({ id, encodedVideoPath: newPath });
+3 -6
View File
@@ -10,7 +10,6 @@ import {
AudioCodec,
CQMode,
Colorspace,
ImageFormat,
LogLevel,
SystemConfig,
SystemConfigEntity,
@@ -113,11 +112,9 @@ export const defaults = Object.freeze<SystemConfig>({
hashVerificationEnabled: true,
template: '{{y}}/{{y}}-{{MM}}-{{dd}}/{{filename}}',
},
image: {
thumbnailFormat: ImageFormat.WEBP,
thumbnailSize: 250,
previewFormat: ImageFormat.JPEG,
previewSize: 1440,
thumbnail: {
webpSize: 250,
jpegSize: 1440,
quality: 80,
colorspace: Colorspace.P3,
},
+7 -2
View File
@@ -82,7 +82,7 @@ export function mapAsset(entity: AssetEntity, options: AssetMapOptions = {}): As
type: entity.type,
thumbhash: entity.thumbhash?.toString('base64') ?? null,
localDateTime: entity.localDateTime,
resized: !!entity.previewPath,
resized: !!entity.resizePath,
duration: entity.duration ?? '0:00:00.00000',
livePhotoVideoId: entity.livePhotoVideoId,
hasMetadata: false,
@@ -100,7 +100,7 @@ export function mapAsset(entity: AssetEntity, options: AssetMapOptions = {}): As
type: entity.type,
originalPath: entity.originalPath,
originalFileName: entity.originalFileName,
resized: !!entity.previewPath,
resized: !!entity.resizePath,
thumbhash: entity.thumbhash?.toString('base64') ?? null,
fileCreatedAt: entity.fileCreatedAt,
fileModifiedAt: entity.fileModifiedAt,
@@ -131,7 +131,12 @@ export function mapAsset(entity: AssetEntity, options: AssetMapOptions = {}): As
}
export class MemoryLaneResponseDto {
@ApiProperty({ deprecated: true })
title!: string;
@ApiProperty({ type: 'integer' })
yearsAgo!: number;
assets!: AssetResponseDto[];
}
+2 -2
View File
@@ -31,7 +31,7 @@ export class CheckExistingAssetsResponseDto {
export class CuratedLocationsResponseDto {
id!: string;
city!: string;
previewPath!: string;
resizePath!: string;
deviceAssetId!: string;
deviceId!: string;
}
@@ -39,7 +39,7 @@ export class CuratedLocationsResponseDto {
export class CuratedObjectsResponseDto {
id!: string;
object!: string;
previewPath!: string;
resizePath!: string;
deviceAssetId!: string;
deviceId!: string;
}
+9 -5
View File
@@ -2,7 +2,6 @@ import { ApiProperty } from '@nestjs/swagger';
import { Type } from 'class-transformer';
import { ArrayNotEmpty, IsArray, IsEnum, IsInt, IsNotEmpty, IsString, IsUUID, ValidateNested } from 'class-validator';
import { UploadFieldName } from 'src/dtos/asset.dto';
import { ImageFormat } from 'src/entities/system-config.entity';
import { Optional, ValidateBoolean, ValidateDate, ValidateUUID } from 'src/validation';
export class AssetBulkUploadCheckItem {
@@ -113,17 +112,22 @@ export class CreateAssetDto {
[UploadFieldName.SIDECAR_DATA]?: any;
}
export enum GetAssetThumbnailFormatEnum {
JPEG = 'JPEG',
WEBP = 'WEBP',
}
export class GetAssetThumbnailDto {
@Optional()
@IsEnum(ImageFormat)
@IsEnum(GetAssetThumbnailFormatEnum)
@ApiProperty({
type: String,
enum: ImageFormat,
default: ImageFormat.WEBP,
enum: GetAssetThumbnailFormatEnum,
default: GetAssetThumbnailFormatEnum.WEBP,
required: false,
enumName: 'ThumbnailFormat',
})
format: ImageFormat = ImageFormat.WEBP;
format: GetAssetThumbnailFormatEnum = GetAssetThumbnailFormatEnum.WEBP;
}
export class SearchPropertiesDto {
+2 -2
View File
@@ -163,12 +163,12 @@ export class MetadataSearchDto extends BaseSearchDto {
@IsString()
@IsNotEmpty()
@Optional()
previewPath?: string;
resizePath?: string;
@IsString()
@IsNotEmpty()
@Optional()
thumbnailPath?: string;
webpPath?: string;
@IsString()
@IsNotEmpty()
+7 -16
View File
@@ -22,7 +22,6 @@ import {
AudioCodec,
CQMode,
Colorspace,
ImageFormat,
LogLevel,
SystemConfig,
ToneMapping,
@@ -387,25 +386,17 @@ export class SystemConfigThemeDto {
}
class SystemConfigThumbnailDto {
@IsEnum(ImageFormat)
@ApiProperty({ enumName: 'ImageFormat', enum: ImageFormat })
thumbnailFormat!: ImageFormat;
@IsInt()
@Min(1)
@Type(() => Number)
@ApiProperty({ type: 'integer' })
webpSize!: number;
@IsInt()
@Min(1)
@Type(() => Number)
@ApiProperty({ type: 'integer' })
thumbnailSize!: number;
@IsEnum(ImageFormat)
@ApiProperty({ enumName: 'ImageFormat', enum: ImageFormat })
previewFormat!: ImageFormat;
@IsInt()
@Min(1)
@Type(() => Number)
@ApiProperty({ type: 'integer' })
previewSize!: number;
jpegSize!: number;
@IsInt()
@Min(1)
@@ -492,7 +483,7 @@ export class SystemConfigDto implements SystemConfig {
@Type(() => SystemConfigThumbnailDto)
@ValidateNested()
@IsObject()
image!: SystemConfigThumbnailDto;
thumbnail!: SystemConfigThumbnailDto;
@Type(() => SystemConfigTrashDto)
@ValidateNested()
+2 -2
View File
@@ -67,10 +67,10 @@ export class AssetEntity {
originalPath!: string;
@Column({ type: 'varchar', nullable: true })
previewPath!: string | null;
resizePath!: string | null;
@Column({ type: 'varchar', nullable: true, default: '' })
thumbnailPath!: string | null;
webpPath!: string | null;
@Column({ type: 'bytea', nullable: true })
thumbhash!: Buffer | null;
+2 -2
View File
@@ -24,8 +24,8 @@ export class MoveEntity {
export enum AssetPathType {
ORIGINAL = 'original',
PREVIEW = 'preview',
THUMBNAIL = 'thumbnail',
JPEG_THUMBNAIL = 'jpeg_thumbnail',
WEBP_THUMBNAIL = 'webp_thumbnail',
ENCODED_VIDEO = 'encoded_video',
SIDECAR = 'sidecar',
}
+3 -10
View File
@@ -165,11 +165,6 @@ export enum Colorspace {
P3 = 'p3',
}
export enum ImageFormat {
JPEG = 'jpeg',
WEBP = 'webp',
}
export enum LogLevel {
VERBOSE = 'verbose',
DEBUG = 'debug',
@@ -254,11 +249,9 @@ export interface SystemConfig {
hashVerificationEnabled: boolean;
template: string;
};
image: {
thumbnailFormat: ImageFormat;
thumbnailSize: number;
previewFormat: ImageFormat;
previewSize: number;
thumbnail: {
webpSize: number;
jpegSize: number;
quality: number;
colorspace: Colorspace;
};
+4 -4
View File
@@ -33,8 +33,8 @@ export enum JobName {
// thumbnails
QUEUE_GENERATE_THUMBNAILS = 'queue-generate-thumbnails',
GENERATE_THUMBNAIL = 'generate-thumbnail',
GENERATE_PREVIEW = 'generate-preview',
GENERATE_JPEG_THUMBNAIL = 'generate-jpeg-thumbnail',
GENERATE_WEBP_THUMBNAIL = 'generate-webp-thumbnail',
GENERATE_THUMBHASH_THUMBNAIL = 'generate-thumbhash-thumbnail',
GENERATE_PERSON_THUMBNAIL = 'generate-person-thumbnail',
@@ -160,8 +160,8 @@ export type JobItem =
// Thumbnails
| { name: JobName.QUEUE_GENERATE_THUMBNAILS; data: IBaseJob }
| { name: JobName.GENERATE_THUMBNAIL; data: IEntityJob }
| { name: JobName.GENERATE_PREVIEW; data: IEntityJob }
| { name: JobName.GENERATE_JPEG_THUMBNAIL; data: IEntityJob }
| { name: JobName.GENERATE_WEBP_THUMBNAIL; data: IEntityJob }
| { name: JobName.GENERATE_THUMBHASH_THUMBNAIL; data: IEntityJob }
// User
+2 -2
View File
@@ -1,11 +1,11 @@
import { Writable } from 'node:stream';
import { ImageFormat, TranscodeTarget, VideoCodec } from 'src/entities/system-config.entity';
import { TranscodeTarget, VideoCodec } from 'src/entities/system-config.entity';
export const IMediaRepository = 'IMediaRepository';
export interface ResizeOptions {
size: number;
format: ImageFormat;
format: 'webp' | 'jpeg';
colorspace: string;
quality: number;
}
+21
View File
@@ -0,0 +1,21 @@
import { MetricOptions } from '@opentelemetry/api';
export const IMetricRepository = 'IMetricRepository';
export interface MetricGroupOptions {
enabled: boolean;
}
export interface IMetricGroupRepository {
addToCounter(name: string, value: number, options?: MetricOptions): void;
addToGauge(name: string, value: number, options?: MetricOptions): void;
addToHistogram(name: string, value: number, options?: MetricOptions): void;
configure(options: MetricGroupOptions): this;
}
export interface IMetricRepository {
api: IMetricGroupRepository;
host: IMetricGroupRepository;
jobs: IMetricGroupRepository;
repo: IMetricGroupRepository;
}
+2 -2
View File
@@ -117,8 +117,8 @@ export interface SearchPathOptions {
encodedVideoPath?: string;
originalFileName?: string;
originalPath?: string;
previewPath?: string;
thumbnailPath?: string;
resizePath?: string;
webpPath?: string;
}
export interface SearchExifOptions {
@@ -1,31 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class RenameWebpJpegPaths1711257900274 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.renameColumn('assets', 'webpPath', 'thumbnailPath');
await queryRunner.renameColumn('assets', 'resizePath', 'previewPath');
await queryRunner.query(`
UPDATE system_config
SET key = 'image.previewSize'
WHERE key = 'thumbnail.jpegSize'`);
await queryRunner.query(
`UPDATE system_config
SET key = 'image.thumbnailSize'
WHERE key = 'thumbnail.webpSize'`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.renameColumn('assets', 'thumbnailPath', 'webpPath');
await queryRunner.renameColumn('assets', 'previewPath', 'resizePath');
await queryRunner.query(`
UPDATE system_config
SET key = 'thumbnail.jpegSize'
WHERE key = 'image.previewSize'`);
await queryRunner.query(
`UPDATE system_config
SET key = 'thumbnail.webpSize'
WHERE key = 'image.thumbnailSize'`,
);
}
}
+21 -21
View File
@@ -9,8 +9,8 @@ SELECT
"AssetEntity"."deviceId" AS "AssetEntity_deviceId",
"AssetEntity"."type" AS "AssetEntity_type",
"AssetEntity"."originalPath" AS "AssetEntity_originalPath",
"AssetEntity"."previewPath" AS "AssetEntity_previewPath",
"AssetEntity"."thumbnailPath" AS "AssetEntity_thumbnailPath",
"AssetEntity"."resizePath" AS "AssetEntity_resizePath",
"AssetEntity"."webpPath" AS "AssetEntity_webpPath",
"AssetEntity"."thumbhash" AS "AssetEntity_thumbhash",
"AssetEntity"."encodedVideoPath" AS "AssetEntity_encodedVideoPath",
"AssetEntity"."createdAt" AS "AssetEntity_createdAt",
@@ -45,8 +45,8 @@ SELECT
"AssetEntity"."deviceId" AS "AssetEntity_deviceId",
"AssetEntity"."type" AS "AssetEntity_type",
"AssetEntity"."originalPath" AS "AssetEntity_originalPath",
"AssetEntity"."previewPath" AS "AssetEntity_previewPath",
"AssetEntity"."thumbnailPath" AS "AssetEntity_thumbnailPath",
"AssetEntity"."resizePath" AS "AssetEntity_resizePath",
"AssetEntity"."webpPath" AS "AssetEntity_webpPath",
"AssetEntity"."thumbhash" AS "AssetEntity_thumbhash",
"AssetEntity"."encodedVideoPath" AS "AssetEntity_encodedVideoPath",
"AssetEntity"."createdAt" AS "AssetEntity_createdAt",
@@ -130,8 +130,8 @@ SELECT
"bd93d5747511a4dad4923546c51365bf1a803774"."deviceId" AS "bd93d5747511a4dad4923546c51365bf1a803774_deviceId",
"bd93d5747511a4dad4923546c51365bf1a803774"."type" AS "bd93d5747511a4dad4923546c51365bf1a803774_type",
"bd93d5747511a4dad4923546c51365bf1a803774"."originalPath" AS "bd93d5747511a4dad4923546c51365bf1a803774_originalPath",
"bd93d5747511a4dad4923546c51365bf1a803774"."previewPath" AS "bd93d5747511a4dad4923546c51365bf1a803774_previewPath",
"bd93d5747511a4dad4923546c51365bf1a803774"."thumbnailPath" AS "bd93d5747511a4dad4923546c51365bf1a803774_thumbnailPath",
"bd93d5747511a4dad4923546c51365bf1a803774"."resizePath" AS "bd93d5747511a4dad4923546c51365bf1a803774_resizePath",
"bd93d5747511a4dad4923546c51365bf1a803774"."webpPath" AS "bd93d5747511a4dad4923546c51365bf1a803774_webpPath",
"bd93d5747511a4dad4923546c51365bf1a803774"."thumbhash" AS "bd93d5747511a4dad4923546c51365bf1a803774_thumbhash",
"bd93d5747511a4dad4923546c51365bf1a803774"."encodedVideoPath" AS "bd93d5747511a4dad4923546c51365bf1a803774_encodedVideoPath",
"bd93d5747511a4dad4923546c51365bf1a803774"."createdAt" AS "bd93d5747511a4dad4923546c51365bf1a803774_createdAt",
@@ -183,8 +183,8 @@ FROM
"AssetEntity"."deviceId" AS "AssetEntity_deviceId",
"AssetEntity"."type" AS "AssetEntity_type",
"AssetEntity"."originalPath" AS "AssetEntity_originalPath",
"AssetEntity"."previewPath" AS "AssetEntity_previewPath",
"AssetEntity"."thumbnailPath" AS "AssetEntity_thumbnailPath",
"AssetEntity"."resizePath" AS "AssetEntity_resizePath",
"AssetEntity"."webpPath" AS "AssetEntity_webpPath",
"AssetEntity"."thumbhash" AS "AssetEntity_thumbhash",
"AssetEntity"."encodedVideoPath" AS "AssetEntity_encodedVideoPath",
"AssetEntity"."createdAt" AS "AssetEntity_createdAt",
@@ -229,7 +229,7 @@ LIMIT
WITH
paths AS (
SELECT
unnest($2::text []) AS path
unnest($2::text[]) AS path
)
SELECT
path
@@ -280,8 +280,8 @@ SELECT
"AssetEntity"."deviceId" AS "AssetEntity_deviceId",
"AssetEntity"."type" AS "AssetEntity_type",
"AssetEntity"."originalPath" AS "AssetEntity_originalPath",
"AssetEntity"."previewPath" AS "AssetEntity_previewPath",
"AssetEntity"."thumbnailPath" AS "AssetEntity_thumbnailPath",
"AssetEntity"."resizePath" AS "AssetEntity_resizePath",
"AssetEntity"."webpPath" AS "AssetEntity_webpPath",
"AssetEntity"."thumbhash" AS "AssetEntity_thumbhash",
"AssetEntity"."encodedVideoPath" AS "AssetEntity_encodedVideoPath",
"AssetEntity"."createdAt" AS "AssetEntity_createdAt",
@@ -326,8 +326,8 @@ SELECT
"AssetEntity"."deviceId" AS "AssetEntity_deviceId",
"AssetEntity"."type" AS "AssetEntity_type",
"AssetEntity"."originalPath" AS "AssetEntity_originalPath",
"AssetEntity"."previewPath" AS "AssetEntity_previewPath",
"AssetEntity"."thumbnailPath" AS "AssetEntity_thumbnailPath",
"AssetEntity"."resizePath" AS "AssetEntity_resizePath",
"AssetEntity"."webpPath" AS "AssetEntity_webpPath",
"AssetEntity"."thumbhash" AS "AssetEntity_thumbhash",
"AssetEntity"."encodedVideoPath" AS "AssetEntity_encodedVideoPath",
"AssetEntity"."createdAt" AS "AssetEntity_createdAt",
@@ -370,8 +370,8 @@ SELECT
"AssetEntity"."deviceId" AS "AssetEntity_deviceId",
"AssetEntity"."type" AS "AssetEntity_type",
"AssetEntity"."originalPath" AS "AssetEntity_originalPath",
"AssetEntity"."previewPath" AS "AssetEntity_previewPath",
"AssetEntity"."thumbnailPath" AS "AssetEntity_thumbnailPath",
"AssetEntity"."resizePath" AS "AssetEntity_resizePath",
"AssetEntity"."webpPath" AS "AssetEntity_webpPath",
"AssetEntity"."thumbhash" AS "AssetEntity_thumbhash",
"AssetEntity"."encodedVideoPath" AS "AssetEntity_encodedVideoPath",
"AssetEntity"."createdAt" AS "AssetEntity_createdAt",
@@ -459,8 +459,8 @@ SELECT
"asset"."deviceId" AS "asset_deviceId",
"asset"."type" AS "asset_type",
"asset"."originalPath" AS "asset_originalPath",
"asset"."previewPath" AS "asset_previewPath",
"asset"."thumbnailPath" AS "asset_thumbnailPath",
"asset"."resizePath" AS "asset_resizePath",
"asset"."webpPath" AS "asset_webpPath",
"asset"."thumbhash" AS "asset_thumbhash",
"asset"."encodedVideoPath" AS "asset_encodedVideoPath",
"asset"."createdAt" AS "asset_createdAt",
@@ -518,8 +518,8 @@ SELECT
"stackedAssets"."deviceId" AS "stackedAssets_deviceId",
"stackedAssets"."type" AS "stackedAssets_type",
"stackedAssets"."originalPath" AS "stackedAssets_originalPath",
"stackedAssets"."previewPath" AS "stackedAssets_previewPath",
"stackedAssets"."thumbnailPath" AS "stackedAssets_thumbnailPath",
"stackedAssets"."resizePath" AS "stackedAssets_resizePath",
"stackedAssets"."webpPath" AS "stackedAssets_webpPath",
"stackedAssets"."thumbhash" AS "stackedAssets_thumbhash",
"stackedAssets"."encodedVideoPath" AS "stackedAssets_encodedVideoPath",
"stackedAssets"."createdAt" AS "stackedAssets_createdAt",
@@ -630,8 +630,8 @@ LIMIT
SELECT
asset.*,
e.*,
COALESCE("si"."tags", array[]::text []) AS "tags",
COALESCE("si"."objects", array[]::text []) AS "objects"
COALESCE("si"."tags", array[]::text[]) AS "tags",
COALESCE("si"."objects", array[]::text[]) AS "objects"
FROM
"assets" "asset"
INNER JOIN "exif" "e" ON asset."id" = e."assetId"
+6 -6
View File
@@ -152,8 +152,8 @@ FROM
"AssetFaceEntity__AssetFaceEntity_asset"."deviceId" AS "AssetFaceEntity__AssetFaceEntity_asset_deviceId",
"AssetFaceEntity__AssetFaceEntity_asset"."type" AS "AssetFaceEntity__AssetFaceEntity_asset_type",
"AssetFaceEntity__AssetFaceEntity_asset"."originalPath" AS "AssetFaceEntity__AssetFaceEntity_asset_originalPath",
"AssetFaceEntity__AssetFaceEntity_asset"."previewPath" AS "AssetFaceEntity__AssetFaceEntity_asset_previewPath",
"AssetFaceEntity__AssetFaceEntity_asset"."thumbnailPath" AS "AssetFaceEntity__AssetFaceEntity_asset_thumbnailPath",
"AssetFaceEntity__AssetFaceEntity_asset"."resizePath" AS "AssetFaceEntity__AssetFaceEntity_asset_resizePath",
"AssetFaceEntity__AssetFaceEntity_asset"."webpPath" AS "AssetFaceEntity__AssetFaceEntity_asset_webpPath",
"AssetFaceEntity__AssetFaceEntity_asset"."thumbhash" AS "AssetFaceEntity__AssetFaceEntity_asset_thumbhash",
"AssetFaceEntity__AssetFaceEntity_asset"."encodedVideoPath" AS "AssetFaceEntity__AssetFaceEntity_asset_encodedVideoPath",
"AssetFaceEntity__AssetFaceEntity_asset"."createdAt" AS "AssetFaceEntity__AssetFaceEntity_asset_createdAt",
@@ -250,8 +250,8 @@ FROM
"AssetEntity"."deviceId" AS "AssetEntity_deviceId",
"AssetEntity"."type" AS "AssetEntity_type",
"AssetEntity"."originalPath" AS "AssetEntity_originalPath",
"AssetEntity"."previewPath" AS "AssetEntity_previewPath",
"AssetEntity"."thumbnailPath" AS "AssetEntity_thumbnailPath",
"AssetEntity"."resizePath" AS "AssetEntity_resizePath",
"AssetEntity"."webpPath" AS "AssetEntity_webpPath",
"AssetEntity"."thumbhash" AS "AssetEntity_thumbhash",
"AssetEntity"."encodedVideoPath" AS "AssetEntity_encodedVideoPath",
"AssetEntity"."createdAt" AS "AssetEntity_createdAt",
@@ -380,8 +380,8 @@ SELECT
"AssetFaceEntity__AssetFaceEntity_asset"."deviceId" AS "AssetFaceEntity__AssetFaceEntity_asset_deviceId",
"AssetFaceEntity__AssetFaceEntity_asset"."type" AS "AssetFaceEntity__AssetFaceEntity_asset_type",
"AssetFaceEntity__AssetFaceEntity_asset"."originalPath" AS "AssetFaceEntity__AssetFaceEntity_asset_originalPath",
"AssetFaceEntity__AssetFaceEntity_asset"."previewPath" AS "AssetFaceEntity__AssetFaceEntity_asset_previewPath",
"AssetFaceEntity__AssetFaceEntity_asset"."thumbnailPath" AS "AssetFaceEntity__AssetFaceEntity_asset_thumbnailPath",
"AssetFaceEntity__AssetFaceEntity_asset"."resizePath" AS "AssetFaceEntity__AssetFaceEntity_asset_resizePath",
"AssetFaceEntity__AssetFaceEntity_asset"."webpPath" AS "AssetFaceEntity__AssetFaceEntity_asset_webpPath",
"AssetFaceEntity__AssetFaceEntity_asset"."thumbhash" AS "AssetFaceEntity__AssetFaceEntity_asset_thumbhash",
"AssetFaceEntity__AssetFaceEntity_asset"."encodedVideoPath" AS "AssetFaceEntity__AssetFaceEntity_asset_encodedVideoPath",
"AssetFaceEntity__AssetFaceEntity_asset"."createdAt" AS "AssetFaceEntity__AssetFaceEntity_asset_createdAt",
+12 -12
View File
@@ -14,8 +14,8 @@ FROM
"asset"."deviceId" AS "asset_deviceId",
"asset"."type" AS "asset_type",
"asset"."originalPath" AS "asset_originalPath",
"asset"."previewPath" AS "asset_previewPath",
"asset"."thumbnailPath" AS "asset_thumbnailPath",
"asset"."resizePath" AS "asset_resizePath",
"asset"."webpPath" AS "asset_webpPath",
"asset"."thumbhash" AS "asset_thumbhash",
"asset"."encodedVideoPath" AS "asset_encodedVideoPath",
"asset"."createdAt" AS "asset_createdAt",
@@ -45,8 +45,8 @@ FROM
"stackedAssets"."deviceId" AS "stackedAssets_deviceId",
"stackedAssets"."type" AS "stackedAssets_type",
"stackedAssets"."originalPath" AS "stackedAssets_originalPath",
"stackedAssets"."previewPath" AS "stackedAssets_previewPath",
"stackedAssets"."thumbnailPath" AS "stackedAssets_thumbnailPath",
"stackedAssets"."resizePath" AS "stackedAssets_resizePath",
"stackedAssets"."webpPath" AS "stackedAssets_webpPath",
"stackedAssets"."thumbhash" AS "stackedAssets_thumbhash",
"stackedAssets"."encodedVideoPath" AS "stackedAssets_encodedVideoPath",
"stackedAssets"."createdAt" AS "stackedAssets_createdAt",
@@ -110,8 +110,8 @@ SELECT
"asset"."deviceId" AS "asset_deviceId",
"asset"."type" AS "asset_type",
"asset"."originalPath" AS "asset_originalPath",
"asset"."previewPath" AS "asset_previewPath",
"asset"."thumbnailPath" AS "asset_thumbnailPath",
"asset"."resizePath" AS "asset_resizePath",
"asset"."webpPath" AS "asset_webpPath",
"asset"."thumbhash" AS "asset_thumbhash",
"asset"."encodedVideoPath" AS "asset_encodedVideoPath",
"asset"."createdAt" AS "asset_createdAt",
@@ -141,8 +141,8 @@ SELECT
"stackedAssets"."deviceId" AS "stackedAssets_deviceId",
"stackedAssets"."type" AS "stackedAssets_type",
"stackedAssets"."originalPath" AS "stackedAssets_originalPath",
"stackedAssets"."previewPath" AS "stackedAssets_previewPath",
"stackedAssets"."thumbnailPath" AS "stackedAssets_thumbnailPath",
"stackedAssets"."resizePath" AS "stackedAssets_resizePath",
"stackedAssets"."webpPath" AS "stackedAssets_webpPath",
"stackedAssets"."thumbhash" AS "stackedAssets_thumbhash",
"stackedAssets"."encodedVideoPath" AS "stackedAssets_encodedVideoPath",
"stackedAssets"."createdAt" AS "stackedAssets_createdAt",
@@ -278,7 +278,7 @@ WITH RECURSIVE
exif
INNER JOIN assets ON exif."assetId" = assets.id
WHERE
"ownerId" IN ($1)
"ownerId" = ANY ('$1'::uuid [])
AND "isVisible" = $2
AND "isArchived" = $3
AND type = $4
@@ -302,7 +302,7 @@ WITH RECURSIVE
INNER JOIN assets ON exif."assetId" = assets.id
WHERE
city > c.city
AND "ownerId" IN ($1)
AND "ownerId" = ANY ('$1'::uuid [])
AND "isVisible" = $2
AND "isArchived" = $3
AND type = $4
@@ -320,8 +320,8 @@ SELECT
"asset"."deviceId" AS "asset_deviceId",
"asset"."type" AS "asset_type",
"asset"."originalPath" AS "asset_originalPath",
"asset"."previewPath" AS "asset_previewPath",
"asset"."thumbnailPath" AS "asset_thumbnailPath",
"asset"."resizePath" AS "asset_resizePath",
"asset"."webpPath" AS "asset_webpPath",
"asset"."thumbhash" AS "asset_thumbhash",
"asset"."encodedVideoPath" AS "asset_encodedVideoPath",
"asset"."createdAt" AS "asset_createdAt",
@@ -28,8 +28,8 @@ FROM
"SharedLinkEntity__SharedLinkEntity_assets"."deviceId" AS "SharedLinkEntity__SharedLinkEntity_assets_deviceId",
"SharedLinkEntity__SharedLinkEntity_assets"."type" AS "SharedLinkEntity__SharedLinkEntity_assets_type",
"SharedLinkEntity__SharedLinkEntity_assets"."originalPath" AS "SharedLinkEntity__SharedLinkEntity_assets_originalPath",
"SharedLinkEntity__SharedLinkEntity_assets"."previewPath" AS "SharedLinkEntity__SharedLinkEntity_assets_previewPath",
"SharedLinkEntity__SharedLinkEntity_assets"."thumbnailPath" AS "SharedLinkEntity__SharedLinkEntity_assets_thumbnailPath",
"SharedLinkEntity__SharedLinkEntity_assets"."resizePath" AS "SharedLinkEntity__SharedLinkEntity_assets_resizePath",
"SharedLinkEntity__SharedLinkEntity_assets"."webpPath" AS "SharedLinkEntity__SharedLinkEntity_assets_webpPath",
"SharedLinkEntity__SharedLinkEntity_assets"."thumbhash" AS "SharedLinkEntity__SharedLinkEntity_assets_thumbhash",
"SharedLinkEntity__SharedLinkEntity_assets"."encodedVideoPath" AS "SharedLinkEntity__SharedLinkEntity_assets_encodedVideoPath",
"SharedLinkEntity__SharedLinkEntity_assets"."createdAt" AS "SharedLinkEntity__SharedLinkEntity_assets_createdAt",
@@ -95,8 +95,8 @@ FROM
"4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6"."deviceId" AS "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6_deviceId",
"4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6"."type" AS "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6_type",
"4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6"."originalPath" AS "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6_originalPath",
"4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6"."previewPath" AS "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6_previewPath",
"4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6"."thumbnailPath" AS "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6_thumbnailPath",
"4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6"."resizePath" AS "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6_resizePath",
"4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6"."webpPath" AS "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6_webpPath",
"4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6"."thumbhash" AS "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6_thumbhash",
"4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6"."encodedVideoPath" AS "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6_encodedVideoPath",
"4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6"."createdAt" AS "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6_createdAt",
@@ -218,8 +218,8 @@ SELECT
"SharedLinkEntity__SharedLinkEntity_assets"."deviceId" AS "SharedLinkEntity__SharedLinkEntity_assets_deviceId",
"SharedLinkEntity__SharedLinkEntity_assets"."type" AS "SharedLinkEntity__SharedLinkEntity_assets_type",
"SharedLinkEntity__SharedLinkEntity_assets"."originalPath" AS "SharedLinkEntity__SharedLinkEntity_assets_originalPath",
"SharedLinkEntity__SharedLinkEntity_assets"."previewPath" AS "SharedLinkEntity__SharedLinkEntity_assets_previewPath",
"SharedLinkEntity__SharedLinkEntity_assets"."thumbnailPath" AS "SharedLinkEntity__SharedLinkEntity_assets_thumbnailPath",
"SharedLinkEntity__SharedLinkEntity_assets"."resizePath" AS "SharedLinkEntity__SharedLinkEntity_assets_resizePath",
"SharedLinkEntity__SharedLinkEntity_assets"."webpPath" AS "SharedLinkEntity__SharedLinkEntity_assets_webpPath",
"SharedLinkEntity__SharedLinkEntity_assets"."thumbhash" AS "SharedLinkEntity__SharedLinkEntity_assets_thumbhash",
"SharedLinkEntity__SharedLinkEntity_assets"."encodedVideoPath" AS "SharedLinkEntity__SharedLinkEntity_assets_encodedVideoPath",
"SharedLinkEntity__SharedLinkEntity_assets"."createdAt" AS "SharedLinkEntity__SharedLinkEntity_assets_createdAt",
@@ -66,7 +66,7 @@ export class AssetRepositoryV1 implements IAssetRepositoryV1 {
getDetectedObjectsByUserId(userId: string): Promise<CuratedObjectsResponseDto[]> {
return this.assetRepository.query(
`
SELECT DISTINCT ON (unnest(si.objects)) a.id, unnest(si.objects) as "object", a."previewPath", a."deviceAssetId", a."deviceId"
SELECT DISTINCT ON (unnest(si.objects)) a.id, unnest(si.objects) as "object", a."resizePath", a."deviceAssetId", a."deviceId"
FROM assets a
LEFT JOIN smart_info si ON a.id = si."assetId"
WHERE a."ownerId" = $1
@@ -80,7 +80,7 @@ export class AssetRepositoryV1 implements IAssetRepositoryV1 {
getLocationsByUserId(userId: string): Promise<CuratedLocationsResponseDto[]> {
return this.assetRepository.query(
`
SELECT DISTINCT ON (e.city) a.id, e.city, a."previewPath", a."deviceAssetId", a."deviceId"
SELECT DISTINCT ON (e.city) a.id, e.city, a."resizePath", a."deviceAssetId", a."deviceId"
FROM assets a
LEFT JOIN exif e ON a.id = e."assetId"
WHERE a."ownerId" = $1
+10 -10
View File
@@ -83,7 +83,7 @@ export class AssetRepository implements IAssetRepository {
`entity.ownerId IN (:...ownerIds)
AND entity.isVisible = true
AND entity.isArchived = false
AND entity.previewPath IS NOT NULL
AND entity.resizePath IS NOT NULL
AND EXTRACT(DAY FROM entity.localDateTime AT TIME ZONE 'UTC') = :day
AND EXTRACT(MONTH FROM entity.localDateTime AT TIME ZONE 'UTC') = :month`,
{
@@ -93,7 +93,7 @@ export class AssetRepository implements IAssetRepository {
},
)
.leftJoinAndSelect('entity.exifInfo', 'exifInfo')
.orderBy('entity.localDateTime', 'DESC')
.orderBy('entity.localDateTime', 'ASC')
.getMany();
}
@@ -302,10 +302,10 @@ export class AssetRepository implements IAssetRepository {
switch (property) {
case WithoutProperty.THUMBNAIL: {
where = [
{ previewPath: IsNull(), isVisible: true },
{ previewPath: '', isVisible: true },
{ thumbnailPath: IsNull(), isVisible: true },
{ thumbnailPath: '', isVisible: true },
{ resizePath: IsNull(), isVisible: true },
{ resizePath: '', isVisible: true },
{ webpPath: IsNull(), isVisible: true },
{ webpPath: '', isVisible: true },
{ thumbhash: IsNull(), isVisible: true },
];
break;
@@ -339,7 +339,7 @@ export class AssetRepository implements IAssetRepository {
};
where = {
isVisible: true,
previewPath: Not(IsNull()),
resizePath: Not(IsNull()),
smartSearch: {
embedding: IsNull(),
},
@@ -352,7 +352,7 @@ export class AssetRepository implements IAssetRepository {
smartInfo: true,
};
where = {
previewPath: Not(IsNull()),
resizePath: Not(IsNull()),
isVisible: true,
smartInfo: {
tags: IsNull(),
@@ -367,7 +367,7 @@ export class AssetRepository implements IAssetRepository {
jobStatus: true,
};
where = {
previewPath: Not(IsNull()),
resizePath: Not(IsNull()),
isVisible: true,
faces: {
assetId: IsNull(),
@@ -385,7 +385,7 @@ export class AssetRepository implements IAssetRepository {
faces: true,
};
where = {
previewPath: Not(IsNull()),
resizePath: Not(IsNull()),
isVisible: true,
faces: {
assetId: Not(IsNull()),
+2 -2
View File
@@ -35,8 +35,8 @@ export const JOBS_TO_QUEUE: Record<JobName, QueueName> = {
// thumbnails
[JobName.QUEUE_GENERATE_THUMBNAILS]: QueueName.THUMBNAIL_GENERATION,
[JobName.GENERATE_THUMBNAIL]: QueueName.THUMBNAIL_GENERATION,
[JobName.GENERATE_PREVIEW]: QueueName.THUMBNAIL_GENERATION,
[JobName.GENERATE_JPEG_THUMBNAIL]: QueueName.THUMBNAIL_GENERATION,
[JobName.GENERATE_WEBP_THUMBNAIL]: QueueName.THUMBNAIL_GENERATION,
[JobName.GENERATE_THUMBHASH_THUMBNAIL]: QueueName.THUMBNAIL_GENERATION,
[JobName.GENERATE_PERSON_THUMBNAIL]: QueueName.THUMBNAIL_GENERATION,
@@ -0,0 +1,48 @@
import { Injectable } from '@nestjs/common';
import { MetricOptions } from '@opentelemetry/api';
import { MetricService } from 'nestjs-otel';
import { IMetricGroupRepository, IMetricRepository, MetricGroupOptions } from 'src/interfaces/metric.interface';
import { apiMetrics, hostMetrics, jobMetrics, repoMetrics } from 'src/utils/instrumentation';
class MetricGroupRepository implements IMetricGroupRepository {
private enabled = false;
constructor(private readonly metricService: MetricService) {}
addToCounter(name: string, value: number, options?: MetricOptions): void {
if (this.enabled) {
this.metricService.getCounter(name, options).add(value);
}
}
addToGauge(name: string, value: number, options?: MetricOptions): void {
if (this.enabled) {
this.metricService.getUpDownCounter(name, options).add(value);
}
}
addToHistogram(name: string, value: number, options?: MetricOptions): void {
if (this.enabled) {
this.metricService.getHistogram(name, options).record(value);
}
}
configure(options: MetricGroupOptions): this {
this.enabled = options.enabled;
return this;
}
}
@Injectable()
export class MetricRepository implements IMetricRepository {
api: MetricGroupRepository;
host: MetricGroupRepository;
jobs: MetricGroupRepository;
repo: MetricGroupRepository;
constructor(metricService: MetricService) {
this.api = new MetricGroupRepository(metricService).configure({ enabled: apiMetrics });
this.host = new MetricGroupRepository(metricService).configure({ enabled: hostMetrics });
this.jobs = new MetricGroupRepository(metricService).configure({ enabled: jobMetrics });
this.repo = new MetricGroupRepository(metricService).configure({ enabled: repoMetrics });
}
}
+4 -4
View File
@@ -225,9 +225,9 @@ export class SearchRepository implements ISearchRepository {
.getMany();
}
@GenerateSql({ params: [[DummyValue.UUID]] })
@GenerateSql({ params: [[DummyValue.UUID, DummyValue.UUID]] })
async getAssetsByCity(userIds: string[]): Promise<AssetEntity[]> {
const parameters = [userIds.join(', '), true, false, AssetType.IMAGE];
const parameters = [userIds, true, false, AssetType.IMAGE];
const rawRes = await this.repository.query(this.assetsByCityQuery, parameters);
const items: AssetEntity[] = [];
@@ -315,7 +315,7 @@ WITH RECURSIVE cte AS (
SELECT city, "assetId"
FROM exif
INNER JOIN assets ON exif."assetId" = assets.id
WHERE "ownerId" IN ($1) AND "isVisible" = $2 AND "isArchived" = $3 AND type = $4
WHERE "ownerId" = ANY('$1'::uuid[]) AND "isVisible" = $2 AND "isArchived" = $3 AND type = $4
ORDER BY city
LIMIT 1
)
@@ -328,7 +328,7 @@ WITH RECURSIVE cte AS (
SELECT city, "assetId"
FROM exif
INNER JOIN assets ON exif."assetId" = assets.id
WHERE city > c.city AND "ownerId" IN ($1) AND "isVisible" = $2 AND "isArchived" = $3 AND type = $4
WHERE city > c.city AND "ownerId" = ANY('$1'::uuid[]) AND "isVisible" = $2 AND "isArchived" = $3 AND type = $4
ORDER BY city
LIMIT 1
) l
@@ -1,6 +1,6 @@
import mockfs from 'mock-fs';
import { CrawlOptionsDto } from 'src/dtos/library.dto';
import { FilesystemProvider } from 'src/repositories/filesystem.provider';
import { StorageRepository } from 'src/repositories/storage.repository';
interface Test {
test: string;
@@ -179,11 +179,11 @@ const tests: Test[] = [
},
];
describe(FilesystemProvider.name, () => {
let sut: FilesystemProvider;
describe(StorageRepository.name, () => {
let sut: StorageRepository;
beforeEach(() => {
sut = new FilesystemProvider();
sut = new StorageRepository();
});
afterEach(() => {
@@ -18,8 +18,8 @@ import { ImmichLogger } from 'src/utils/logger';
import { mimeTypes } from 'src/utils/mime-types';
@Instrumentation()
export class FilesystemProvider implements IStorageRepository {
private logger = new ImmichLogger(FilesystemProvider.name);
export class StorageRepository implements IStorageRepository {
private logger = new ImmichLogger(StorageRepository.name);
readdir(folder: string): Promise<string[]> {
return fs.readdir(folder);
+2 -2
View File
@@ -44,13 +44,13 @@ const _getAsset_1 = () => {
asset_1.deviceId = 'device_id_1';
asset_1.type = AssetType.VIDEO;
asset_1.originalPath = 'fake_path/asset_1.jpeg';
asset_1.previewPath = '';
asset_1.resizePath = '';
asset_1.fileModifiedAt = new Date('2022-06-19T23:41:36.910Z');
asset_1.fileCreatedAt = new Date('2022-06-19T23:41:36.910Z');
asset_1.updatedAt = new Date('2022-06-19T23:41:36.910Z');
asset_1.isFavorite = false;
asset_1.isArchived = false;
asset_1.thumbnailPath = '';
asset_1.webpPath = '';
asset_1.encodedVideoPath = '';
asset_1.duration = '0:00:00.000000';
asset_1.exifInfo = new ExifEntity();
+15 -15
View File
@@ -22,12 +22,12 @@ import {
CheckExistingAssetsDto,
CreateAssetDto,
GetAssetThumbnailDto,
GetAssetThumbnailFormatEnum,
ServeFileDto,
} from 'src/dtos/asset-v1.dto';
import { AuthDto } from 'src/dtos/auth.dto';
import { ASSET_CHECKSUM_CONSTRAINT, AssetEntity, AssetType } from 'src/entities/asset.entity';
import { LibraryType } from 'src/entities/library.entity';
import { ImageFormat } from 'src/entities/system-config.entity';
import { IAccessRepository } from 'src/interfaces/access.interface';
import { IAssetRepositoryV1 } from 'src/interfaces/asset-v1.interface';
import { IAssetRepository } from 'src/interfaces/asset.interface';
@@ -244,19 +244,19 @@ export class AssetServiceV1 {
};
}
private getThumbnailPath(asset: AssetEntity, format: ImageFormat) {
private getThumbnailPath(asset: AssetEntity, format: GetAssetThumbnailFormatEnum) {
switch (format) {
case ImageFormat.WEBP: {
if (asset.thumbnailPath) {
return asset.thumbnailPath;
case GetAssetThumbnailFormatEnum.WEBP: {
if (asset.webpPath) {
return asset.webpPath;
}
this.logger.warn(`WebP thumbnail requested but not found for asset ${asset.id}, falling back to JPEG`);
}
case ImageFormat.JPEG: {
if (!asset.previewPath) {
case GetAssetThumbnailFormatEnum.JPEG: {
if (!asset.resizePath) {
throw new NotFoundException(`No thumbnail found for asset ${asset.id}`);
}
return asset.previewPath;
return asset.resizePath;
}
}
}
@@ -268,12 +268,12 @@ export class AssetServiceV1 {
* Serve file viewer on the web
*/
if (dto.isWeb && mimeType != 'image/gif') {
if (!asset.previewPath) {
if (!asset.resizePath) {
this.logger.error('Error serving IMAGE asset for web');
throw new InternalServerErrorException(`Failed to serve image asset for web`, 'ServeFile');
}
return asset.previewPath;
return asset.resizePath;
}
/**
@@ -283,15 +283,15 @@ export class AssetServiceV1 {
return asset.originalPath;
}
if (asset.thumbnailPath && asset.thumbnailPath.length > 0) {
return asset.thumbnailPath;
if (asset.webpPath && asset.webpPath.length > 0) {
return asset.webpPath;
}
if (!asset.previewPath) {
throw new Error('previewPath not set');
if (!asset.resizePath) {
throw new Error('resizePath not set');
}
return asset.previewPath;
return asset.resizePath;
}
private async getLibraryId(auth: AuthDto, libraryId?: string) {
+16 -9
View File
@@ -307,13 +307,17 @@ describe(AssetService.name, () => {
jest.useRealTimers();
});
it('should set the title correctly', async () => {
it('should group the assets correctly', async () => {
const image1 = { ...assetStub.image, localDateTime: new Date(2023, 1, 15, 0, 0, 0) };
const image2 = { ...assetStub.image, localDateTime: new Date(2023, 1, 15, 1, 0, 0) };
const image3 = { ...assetStub.image, localDateTime: new Date(2015, 1, 15) };
partnerMock.getAll.mockResolvedValue([]);
assetMock.getByDayOfYear.mockResolvedValue([assetStub.image, assetStub.imageFrom2015]);
assetMock.getByDayOfYear.mockResolvedValue([image1, image2, image3]);
await expect(sut.getMemoryLane(authStub.admin, { day: 15, month: 1 })).resolves.toEqual([
{ title: '1 year since...', assets: [mapAsset(assetStub.image)] },
{ title: '9 years since...', assets: [mapAsset(assetStub.imageFrom2015)] },
{ yearsAgo: 1, title: '1 year since...', assets: [mapAsset(image1), mapAsset(image2)] },
{ yearsAgo: 9, title: '9 years since...', assets: [mapAsset(image3)] },
]);
expect(assetMock.getByDayOfYear.mock.calls).toEqual([[[authStub.admin.user.id], { day: 15, month: 1 }]]);
@@ -321,6 +325,7 @@ describe(AssetService.name, () => {
it('should get memories with partners with inTimeline enabled', async () => {
partnerMock.getAll.mockResolvedValue([partnerStub.user1ToAdmin1]);
assetMock.getByDayOfYear.mockResolvedValue([]);
await sut.getMemoryLane(authStub.admin, { day: 15, month: 1 });
@@ -777,8 +782,8 @@ describe(AssetService.name, () => {
name: JobName.DELETE_FILES,
data: {
files: [
assetWithFace.thumbnailPath,
assetWithFace.previewPath,
assetWithFace.webpPath,
assetWithFace.resizePath,
assetWithFace.encodedVideoPath,
assetWithFace.sidecarPath,
assetWithFace.originalPath,
@@ -861,8 +866,8 @@ describe(AssetService.name, () => {
name: JobName.DELETE_FILES,
data: {
files: [
assetStub.external.thumbnailPath,
assetStub.external.previewPath,
assetStub.external.webpPath,
assetStub.external.resizePath,
assetStub.external.encodedVideoPath,
assetStub.external.sidecarPath,
],
@@ -944,7 +949,9 @@ describe(AssetService.name, () => {
it('should run the refresh thumbnails job', async () => {
accessMock.asset.checkOwnerAccess.mockResolvedValue(new Set(['asset-1']));
await sut.run(authStub.admin, { assetIds: ['asset-1'], name: AssetJobName.REGENERATE_THUMBNAIL }),
expect(jobMock.queueAll).toHaveBeenCalledWith([{ name: JobName.GENERATE_THUMBNAIL, data: { id: 'asset-1' } }]);
expect(jobMock.queueAll).toHaveBeenCalledWith([
{ name: JobName.GENERATE_JPEG_THUMBNAIL, data: { id: 'asset-1' } },
]);
});
it('should run the transcode video', async () => {
+20 -15
View File
@@ -174,20 +174,25 @@ export class AssetService {
userIds.push(...partnersIds);
const assets = await this.assetRepository.getByDayOfYear(userIds, dto);
const groups: Record<number, AssetEntity[]> = {};
for (const asset of assets) {
const yearsAgo = currentYear - asset.localDateTime.getFullYear();
if (!groups[yearsAgo]) {
groups[yearsAgo] = [];
}
groups[yearsAgo].push(asset);
}
return _.chain(assets)
.filter((asset) => asset.localDateTime.getFullYear() < currentYear)
.map((asset) => {
const years = currentYear - asset.localDateTime.getFullYear();
return {
title: `${years} year${years > 1 ? 's' : ''} since...`,
asset: mapAsset(asset, { auth }),
};
})
.groupBy((asset) => asset.title)
.map((items, title) => ({ title, assets: items.map(({ asset }) => asset) }))
.value();
return Object.keys(groups)
.map(Number)
.sort()
.filter((yearsAgo) => yearsAgo > 0)
.map((yearsAgo) => ({
yearsAgo,
// TODO move this to clients
title: `${yearsAgo} year${yearsAgo > 1 ? 's' : ''} since...`,
assets: groups[yearsAgo].map((asset) => mapAsset(asset, { auth })),
}));
}
private async timeBucketChecks(auth: AuthDto, dto: TimeBucketDto) {
@@ -461,7 +466,7 @@ export class AssetService {
await this.jobRepository.queue({ name: JobName.ASSET_DELETION, data: { id: asset.livePhotoVideoId } });
}
const files = [asset.thumbnailPath, asset.previewPath, asset.encodedVideoPath, asset.sidecarPath];
const files = [asset.webpPath, asset.resizePath, asset.encodedVideoPath, asset.sidecarPath];
if (!fromExternal) {
files.push(asset.originalPath);
}
@@ -534,7 +539,7 @@ export class AssetService {
}
case AssetJobName.REGENERATE_THUMBNAIL: {
jobs.push({ name: JobName.GENERATE_THUMBNAIL, data: { id } });
jobs.push({ name: JobName.GENERATE_JPEG_THUMBNAIL, data: { id } });
break;
}
+11 -11
View File
@@ -95,13 +95,13 @@ export class AuditService {
break;
}
case AssetPathType.PREVIEW: {
await this.assetRepository.update({ id, previewPath: pathValue });
case AssetPathType.JPEG_THUMBNAIL: {
await this.assetRepository.update({ id, resizePath: pathValue });
break;
}
case AssetPathType.THUMBNAIL: {
await this.assetRepository.update({ id, thumbnailPath: pathValue });
case AssetPathType.WEBP_THUMBNAIL: {
await this.assetRepository.update({ id, webpPath: pathValue });
break;
}
@@ -174,8 +174,8 @@ export class AuditService {
const orphans: FileReportItemDto[] = [];
for await (const assets of pagination) {
assetCount += assets.length;
for (const { id, originalPath, previewPath, encodedVideoPath, thumbnailPath, isExternal, checksum } of assets) {
for (const file of [originalPath, previewPath, encodedVideoPath, thumbnailPath]) {
for (const { id, originalPath, resizePath, encodedVideoPath, webpPath, isExternal, checksum } of assets) {
for (const file of [originalPath, resizePath, encodedVideoPath, webpPath]) {
track(file);
}
@@ -191,14 +191,14 @@ export class AuditService {
) {
orphans.push({ ...entity, pathType: AssetPathType.ORIGINAL, pathValue: originalPath });
}
if (previewPath && !hasFile(thumbFiles, previewPath)) {
orphans.push({ ...entity, pathType: AssetPathType.PREVIEW, pathValue: previewPath });
if (resizePath && !hasFile(thumbFiles, resizePath)) {
orphans.push({ ...entity, pathType: AssetPathType.JPEG_THUMBNAIL, pathValue: resizePath });
}
if (thumbnailPath && !hasFile(thumbFiles, thumbnailPath)) {
orphans.push({ ...entity, pathType: AssetPathType.THUMBNAIL, pathValue: thumbnailPath });
if (webpPath && !hasFile(thumbFiles, webpPath)) {
orphans.push({ ...entity, pathType: AssetPathType.WEBP_THUMBNAIL, pathValue: webpPath });
}
if (encodedVideoPath && !hasFile(videoFiles, encodedVideoPath)) {
orphans.push({ ...entity, pathType: AssetPathType.THUMBNAIL, pathValue: encodedVideoPath });
orphans.push({ ...entity, pathType: AssetPathType.WEBP_THUMBNAIL, pathValue: encodedVideoPath });
}
}
}
+13 -9
View File
@@ -12,6 +12,7 @@ import {
JobStatus,
QueueName,
} from 'src/interfaces/job.interface';
import { IMetricRepository } from 'src/interfaces/metric.interface';
import { IPersonRepository } from 'src/interfaces/person.interface';
import { ISystemConfigRepository } from 'src/interfaces/system-config.interface';
import { JobService } from 'src/services/job.service';
@@ -19,6 +20,7 @@ import { assetStub } from 'test/fixtures/asset.stub';
import { newAssetRepositoryMock } from 'test/repositories/asset.repository.mock';
import { newEventRepositoryMock } from 'test/repositories/event.repository.mock';
import { newJobRepositoryMock } from 'test/repositories/job.repository.mock';
import { newMetricRepositoryMock } from 'test/repositories/metric.repository.mock';
import { newPersonRepositoryMock } from 'test/repositories/person.repository.mock';
import { newSystemConfigRepositoryMock } from 'test/repositories/system-config.repository.mock';
@@ -37,6 +39,7 @@ describe(JobService.name, () => {
let eventMock: jest.Mocked<IEventRepository>;
let jobMock: jest.Mocked<IJobRepository>;
let personMock: jest.Mocked<IPersonRepository>;
let metricMock: jest.Mocked<IMetricRepository>;
beforeEach(() => {
assetMock = newAssetRepositoryMock();
@@ -44,7 +47,8 @@ describe(JobService.name, () => {
eventMock = newEventRepositoryMock();
jobMock = newJobRepositoryMock();
personMock = newPersonRepositoryMock();
sut = new JobService(assetMock, eventMock, jobMock, configMock, personMock);
metricMock = newMetricRepositoryMock();
sut = new JobService(assetMock, eventMock, jobMock, configMock, personMock, metricMock);
});
it('should work', () => {
@@ -275,7 +279,7 @@ describe(JobService.name, () => {
},
{
item: { name: JobName.STORAGE_TEMPLATE_MIGRATION_SINGLE, data: { id: 'asset-1', source: 'upload' } },
jobs: [JobName.GENERATE_THUMBNAIL],
jobs: [JobName.GENERATE_JPEG_THUMBNAIL],
},
{
item: { name: JobName.STORAGE_TEMPLATE_MIGRATION_SINGLE, data: { id: 'asset-1' } },
@@ -286,13 +290,13 @@ describe(JobService.name, () => {
jobs: [],
},
{
item: { name: JobName.GENERATE_THUMBNAIL, data: { id: 'asset-1' } },
jobs: [JobName.GENERATE_PREVIEW, JobName.GENERATE_THUMBHASH_THUMBNAIL],
item: { name: JobName.GENERATE_JPEG_THUMBNAIL, data: { id: 'asset-1' } },
jobs: [JobName.GENERATE_WEBP_THUMBNAIL, JobName.GENERATE_THUMBHASH_THUMBNAIL],
},
{
item: { name: JobName.GENERATE_THUMBNAIL, data: { id: 'asset-1', source: 'upload' } },
item: { name: JobName.GENERATE_JPEG_THUMBNAIL, data: { id: 'asset-1', source: 'upload' } },
jobs: [
JobName.GENERATE_PREVIEW,
JobName.GENERATE_WEBP_THUMBNAIL,
JobName.GENERATE_THUMBHASH_THUMBNAIL,
JobName.SMART_SEARCH,
JobName.FACE_DETECTION,
@@ -300,9 +304,9 @@ describe(JobService.name, () => {
],
},
{
item: { name: JobName.GENERATE_THUMBNAIL, data: { id: 'asset-live-image', source: 'upload' } },
item: { name: JobName.GENERATE_JPEG_THUMBNAIL, data: { id: 'asset-live-image', source: 'upload' } },
jobs: [
JobName.GENERATE_PREVIEW,
JobName.GENERATE_WEBP_THUMBNAIL,
JobName.GENERATE_THUMBHASH_THUMBNAIL,
JobName.SMART_SEARCH,
JobName.FACE_DETECTION,
@@ -325,7 +329,7 @@ describe(JobService.name, () => {
for (const { item, jobs } of tests) {
it(`should queue ${jobs.length} jobs when a ${item.name} job finishes successfully`, async () => {
if (item.name === JobName.GENERATE_THUMBNAIL && item.data.source === 'upload') {
if (item.name === JobName.GENERATE_JPEG_THUMBNAIL && item.data.source === 'upload') {
if (item.data.id === 'asset-live-image') {
assetMock.getByIds.mockResolvedValue([assetStub.livePhotoStillAsset]);
} else {
+16 -4
View File
@@ -1,4 +1,5 @@
import { BadRequestException, Inject, Injectable } from '@nestjs/common';
import { snakeCase } from 'lodash';
import { FeatureFlag, SystemConfigCore } from 'src/cores/system-config.core';
import { mapAsset } from 'src/dtos/asset-response.dto';
import { AllJobStatusResponseDto, JobCommandDto, JobStatusDto } from 'src/dtos/job.dto';
@@ -16,6 +17,7 @@ import {
QueueCleanType,
QueueName,
} from 'src/interfaces/job.interface';
import { IMetricRepository } from 'src/interfaces/metric.interface';
import { IPersonRepository } from 'src/interfaces/person.interface';
import { ISystemConfigRepository } from 'src/interfaces/system-config.interface';
import { ImmichLogger } from 'src/utils/logger';
@@ -31,6 +33,7 @@ export class JobService {
@Inject(IJobRepository) private jobRepository: IJobRepository,
@Inject(ISystemConfigRepository) configRepository: ISystemConfigRepository,
@Inject(IPersonRepository) private personRepository: IPersonRepository,
@Inject(IMetricRepository) private metricRepository: IMetricRepository,
) {
this.configCore = SystemConfigCore.create(configRepository);
}
@@ -92,6 +95,8 @@ export class JobService {
throw new BadRequestException(`Job is already running`);
}
this.metricRepository.jobs.addToCounter(`immich.queues.${snakeCase(name)}.started`, 1);
switch (name) {
case QueueName.VIDEO_CONVERSION: {
return this.jobRepository.queue({ name: JobName.QUEUE_VIDEO_CONVERSION, data: { force } });
@@ -156,14 +161,21 @@ export class JobService {
this.jobRepository.addHandler(queueName, concurrency, async (item: JobItem): Promise<void> => {
const { name, data } = item;
const queueMetric = `immich.queues.${snakeCase(queueName)}.active`;
this.metricRepository.jobs.addToGauge(queueMetric, 1);
try {
const handler = jobHandlers[name];
const status = await handler(data);
const jobMetric = `immich.jobs.${name.replaceAll('-', '_')}.${status}`;
this.metricRepository.jobs.addToCounter(jobMetric, 1);
if (status === JobStatus.SUCCESS || status == JobStatus.SKIPPED) {
await this.onDone(item);
}
} catch (error: Error | any) {
this.logger.error(`Unable to run job handler (${queueName}/${name}): ${error}`, error?.stack, data);
} finally {
this.metricRepository.jobs.addToGauge(queueMetric, -1);
}
});
}
@@ -233,7 +245,7 @@ export class JobService {
case JobName.STORAGE_TEMPLATE_MIGRATION_SINGLE: {
if (item.data.source === 'upload') {
await this.jobRepository.queue({ name: JobName.GENERATE_THUMBNAIL, data: item.data });
await this.jobRepository.queue({ name: JobName.GENERATE_JPEG_THUMBNAIL, data: item.data });
}
break;
}
@@ -247,9 +259,9 @@ export class JobService {
break;
}
case JobName.GENERATE_THUMBNAIL: {
case JobName.GENERATE_JPEG_THUMBNAIL: {
const jobs: JobItem[] = [
{ name: JobName.GENERATE_PREVIEW, data: item.data },
{ name: JobName.GENERATE_WEBP_THUMBNAIL, data: item.data },
{ name: JobName.GENERATE_THUMBHASH_THUMBNAIL, data: item.data },
];
@@ -270,7 +282,7 @@ export class JobService {
break;
}
case JobName.GENERATE_PREVIEW: {
case JobName.GENERATE_WEBP_THUMBNAIL: {
if (item.data.source !== 'upload') {
break;
}
+52 -69
View File
@@ -4,7 +4,6 @@ import { ExifEntity } from 'src/entities/exif.entity';
import {
AudioCodec,
Colorspace,
ImageFormat,
SystemConfigKey,
ToneMapping,
TranscodeHWAccel,
@@ -79,7 +78,7 @@ describe(MediaService.name, () => {
expect(assetMock.getWithout).not.toHaveBeenCalled();
expect(jobMock.queueAll).toHaveBeenCalledWith([
{
name: JobName.GENERATE_THUMBNAIL,
name: JobName.GENERATE_JPEG_THUMBNAIL,
data: { id: assetStub.image.id },
},
]);
@@ -137,7 +136,7 @@ describe(MediaService.name, () => {
expect(assetMock.getWithout).toHaveBeenCalledWith({ skip: 0, take: 1000 }, WithoutProperty.THUMBNAIL);
expect(jobMock.queueAll).toHaveBeenCalledWith([
{
name: JobName.GENERATE_THUMBNAIL,
name: JobName.GENERATE_JPEG_THUMBNAIL,
data: { id: assetStub.image.id },
},
]);
@@ -161,7 +160,7 @@ describe(MediaService.name, () => {
expect(assetMock.getWithout).toHaveBeenCalledWith({ skip: 0, take: 1000 }, WithoutProperty.THUMBNAIL);
expect(jobMock.queueAll).toHaveBeenCalledWith([
{
name: JobName.GENERATE_PREVIEW,
name: JobName.GENERATE_WEBP_THUMBNAIL,
data: { id: assetStub.image.id },
},
]);
@@ -194,10 +193,10 @@ describe(MediaService.name, () => {
});
});
describe('handleGeneratePreview', () => {
describe('handleGenerateJpegThumbnail', () => {
it('should skip thumbnail generation if asset not found', async () => {
assetMock.getByIds.mockResolvedValue([]);
await sut.handleGeneratePreview({ id: assetStub.image.id });
await sut.handleGenerateJpegThumbnail({ id: assetStub.image.id });
expect(mediaMock.resize).not.toHaveBeenCalled();
expect(assetMock.update).not.toHaveBeenCalledWith();
});
@@ -205,29 +204,25 @@ describe(MediaService.name, () => {
it('should skip video thumbnail generation if no video stream', async () => {
mediaMock.probe.mockResolvedValue(probeStub.noVideoStreams);
assetMock.getByIds.mockResolvedValue([assetStub.video]);
await sut.handleGeneratePreview({ id: assetStub.image.id });
await sut.handleGenerateJpegThumbnail({ id: assetStub.image.id });
expect(mediaMock.resize).not.toHaveBeenCalled();
expect(assetMock.update).not.toHaveBeenCalledWith();
});
it('should generate a thumbnail for an image', async () => {
assetMock.getByIds.mockResolvedValue([assetStub.image]);
await sut.handleGeneratePreview({ id: assetStub.image.id });
await sut.handleGenerateJpegThumbnail({ id: assetStub.image.id });
expect(storageMock.mkdirSync).toHaveBeenCalledWith('upload/thumbs/user-id/as/se');
expect(mediaMock.resize).toHaveBeenCalledWith(
'/original/path.jpg',
'upload/thumbs/user-id/as/se/asset-id-preview.jpeg',
{
size: 1440,
format: ImageFormat.JPEG,
quality: 80,
colorspace: Colorspace.SRGB,
},
);
expect(mediaMock.resize).toHaveBeenCalledWith('/original/path.jpg', 'upload/thumbs/user-id/as/se/asset-id.jpeg', {
size: 1440,
format: 'jpeg',
quality: 80,
colorspace: Colorspace.SRGB,
});
expect(assetMock.update).toHaveBeenCalledWith({
id: 'asset-id',
previewPath: 'upload/thumbs/user-id/as/se/asset-id-preview.jpeg',
resizePath: 'upload/thumbs/user-id/as/se/asset-id.jpeg',
});
});
@@ -235,34 +230,30 @@ describe(MediaService.name, () => {
assetMock.getByIds.mockResolvedValue([
{ ...assetStub.image, exifInfo: { profileDescription: 'Adobe RGB', bitsPerSample: 14 } as ExifEntity },
]);
await sut.handleGeneratePreview({ id: assetStub.image.id });
await sut.handleGenerateJpegThumbnail({ id: assetStub.image.id });
expect(storageMock.mkdirSync).toHaveBeenCalledWith('upload/thumbs/user-id/as/se');
expect(mediaMock.resize).toHaveBeenCalledWith(
'/original/path.jpg',
'upload/thumbs/user-id/as/se/asset-id-preview.jpeg',
{
size: 1440,
format: ImageFormat.JPEG,
quality: 80,
colorspace: Colorspace.P3,
},
);
expect(mediaMock.resize).toHaveBeenCalledWith('/original/path.jpg', 'upload/thumbs/user-id/as/se/asset-id.jpeg', {
size: 1440,
format: 'jpeg',
quality: 80,
colorspace: Colorspace.P3,
});
expect(assetMock.update).toHaveBeenCalledWith({
id: 'asset-id',
previewPath: 'upload/thumbs/user-id/as/se/asset-id-preview.jpeg',
resizePath: 'upload/thumbs/user-id/as/se/asset-id.jpeg',
});
});
it('should generate a thumbnail for a video', async () => {
mediaMock.probe.mockResolvedValue(probeStub.videoStream2160p);
assetMock.getByIds.mockResolvedValue([assetStub.video]);
await sut.handleGeneratePreview({ id: assetStub.video.id });
await sut.handleGenerateJpegThumbnail({ id: assetStub.video.id });
expect(storageMock.mkdirSync).toHaveBeenCalledWith('upload/thumbs/user-id/as/se');
expect(mediaMock.transcode).toHaveBeenCalledWith(
'/original/path.ext',
'upload/thumbs/user-id/as/se/asset-id-preview.jpeg',
'upload/thumbs/user-id/as/se/asset-id.jpeg',
{
inputOptions: ['-ss 00:00:00', '-sws_flags accurate_rnd+bitexact+full_chroma_int'],
outputOptions: [
@@ -275,19 +266,19 @@ describe(MediaService.name, () => {
);
expect(assetMock.update).toHaveBeenCalledWith({
id: 'asset-id',
previewPath: 'upload/thumbs/user-id/as/se/asset-id-preview.jpeg',
resizePath: 'upload/thumbs/user-id/as/se/asset-id.jpeg',
});
});
it('should tonemap thumbnail for hdr video', async () => {
mediaMock.probe.mockResolvedValue(probeStub.videoStreamHDR);
assetMock.getByIds.mockResolvedValue([assetStub.video]);
await sut.handleGeneratePreview({ id: assetStub.video.id });
await sut.handleGenerateJpegThumbnail({ id: assetStub.video.id });
expect(storageMock.mkdirSync).toHaveBeenCalledWith('upload/thumbs/user-id/as/se');
expect(mediaMock.transcode).toHaveBeenCalledWith(
'/original/path.ext',
'upload/thumbs/user-id/as/se/asset-id-preview.jpeg',
'upload/thumbs/user-id/as/se/asset-id.jpeg',
{
inputOptions: ['-ss 00:00:00', '-sws_flags accurate_rnd+bitexact+full_chroma_int'],
outputOptions: [
@@ -300,7 +291,7 @@ describe(MediaService.name, () => {
);
expect(assetMock.update).toHaveBeenCalledWith({
id: 'asset-id',
previewPath: 'upload/thumbs/user-id/as/se/asset-id-preview.jpeg',
resizePath: 'upload/thumbs/user-id/as/se/asset-id.jpeg',
});
});
@@ -311,11 +302,11 @@ describe(MediaService.name, () => {
{ key: SystemConfigKey.FFMPEG_MAX_BITRATE, value: '5000k' },
]);
assetMock.getByIds.mockResolvedValue([assetStub.video]);
await sut.handleGeneratePreview({ id: assetStub.video.id });
await sut.handleGenerateJpegThumbnail({ id: assetStub.video.id });
expect(mediaMock.transcode).toHaveBeenCalledWith(
'/original/path.ext',
'upload/thumbs/user-id/as/se/asset-id-preview.jpeg',
'upload/thumbs/user-id/as/se/asset-id.jpeg',
{
inputOptions: ['-ss 00:00:00', '-sws_flags accurate_rnd+bitexact+full_chroma_int'],
outputOptions: [
@@ -330,35 +321,31 @@ describe(MediaService.name, () => {
it('should run successfully', async () => {
assetMock.getByIds.mockResolvedValue([assetStub.image]);
await sut.handleGeneratePreview({ id: assetStub.image.id });
await sut.handleGenerateJpegThumbnail({ id: assetStub.image.id });
});
});
describe('handleGenerateThumbnail', () => {
describe('handleGenerateWebpThumbnail', () => {
it('should skip thumbnail generation if asset not found', async () => {
assetMock.getByIds.mockResolvedValue([]);
await sut.handleGenerateThumbnail({ id: assetStub.image.id });
await sut.handleGenerateWebpThumbnail({ id: assetStub.image.id });
expect(mediaMock.resize).not.toHaveBeenCalled();
expect(assetMock.update).not.toHaveBeenCalledWith();
});
it('should generate a thumbnail', async () => {
assetMock.getByIds.mockResolvedValue([assetStub.image]);
await sut.handleGenerateThumbnail({ id: assetStub.image.id });
await sut.handleGenerateWebpThumbnail({ id: assetStub.image.id });
expect(mediaMock.resize).toHaveBeenCalledWith(
'/original/path.jpg',
'upload/thumbs/user-id/as/se/asset-id-thumbnail.webp',
{
format: ImageFormat.WEBP,
size: 250,
quality: 80,
colorspace: Colorspace.SRGB,
},
);
expect(mediaMock.resize).toHaveBeenCalledWith('/original/path.jpg', 'upload/thumbs/user-id/as/se/asset-id.webp', {
format: 'webp',
size: 250,
quality: 80,
colorspace: Colorspace.SRGB,
});
expect(assetMock.update).toHaveBeenCalledWith({
id: 'asset-id',
thumbnailPath: 'upload/thumbs/user-id/as/se/asset-id-thumbnail.webp',
webpPath: 'upload/thumbs/user-id/as/se/asset-id.webp',
});
});
});
@@ -367,35 +354,31 @@ describe(MediaService.name, () => {
assetMock.getByIds.mockResolvedValue([
{ ...assetStub.image, exifInfo: { profileDescription: 'Adobe RGB', bitsPerSample: 14 } as ExifEntity },
]);
await sut.handleGenerateThumbnail({ id: assetStub.image.id });
await sut.handleGenerateWebpThumbnail({ id: assetStub.image.id });
expect(storageMock.mkdirSync).toHaveBeenCalledWith('upload/thumbs/user-id/as/se');
expect(mediaMock.resize).toHaveBeenCalledWith(
'/original/path.jpg',
'upload/thumbs/user-id/as/se/asset-id-thumbnail.webp',
{
format: ImageFormat.WEBP,
size: 250,
quality: 80,
colorspace: Colorspace.P3,
},
);
expect(mediaMock.resize).toHaveBeenCalledWith('/original/path.jpg', 'upload/thumbs/user-id/as/se/asset-id.webp', {
format: 'webp',
size: 250,
quality: 80,
colorspace: Colorspace.P3,
});
expect(assetMock.update).toHaveBeenCalledWith({
id: 'asset-id',
thumbnailPath: 'upload/thumbs/user-id/as/se/asset-id-thumbnail.webp',
webpPath: 'upload/thumbs/user-id/as/se/asset-id.webp',
});
});
describe('handleGenerateThumbhashThumbnail', () => {
it('should skip thumbhash generation if asset not found', async () => {
assetMock.getByIds.mockResolvedValue([]);
await sut.handleGenerateThumbhash({ id: assetStub.image.id });
await sut.handleGenerateThumbhashThumbnail({ id: assetStub.image.id });
expect(mediaMock.generateThumbhash).not.toHaveBeenCalled();
});
it('should skip thumbhash generation if resize path is missing', async () => {
assetMock.getByIds.mockResolvedValue([assetStub.noResizePath]);
await sut.handleGenerateThumbhash({ id: assetStub.noResizePath.id });
await sut.handleGenerateThumbhashThumbnail({ id: assetStub.noResizePath.id });
expect(mediaMock.generateThumbhash).not.toHaveBeenCalled();
});
@@ -404,7 +387,7 @@ describe(MediaService.name, () => {
assetMock.getByIds.mockResolvedValue([assetStub.image]);
mediaMock.generateThumbhash.mockResolvedValue(thumbhashBuffer);
await sut.handleGenerateThumbhash({ id: assetStub.image.id });
await sut.handleGenerateThumbhashThumbnail({ id: assetStub.image.id });
expect(mediaMock.generateThumbhash).toHaveBeenCalledWith('/uploads/user-id/thumbs/path.jpg');
expect(assetMock.update).toHaveBeenCalledWith({ id: 'asset-id', thumbhash: thumbhashBuffer });
+25 -26
View File
@@ -1,5 +1,5 @@
import { Inject, Injectable, UnsupportedMediaTypeException } from '@nestjs/common';
import { GeneratedImageType, StorageCore, StorageFolder } from 'src/cores/storage.core';
import { StorageCore, StorageFolder } from 'src/cores/storage.core';
import { SystemConfigCore } from 'src/cores/system-config.core';
import { SystemConfigFFmpegDto } from 'src/dtos/system-config.dto';
import { AssetEntity, AssetType } from 'src/entities/asset.entity';
@@ -7,7 +7,6 @@ import { AssetPathType } from 'src/entities/move.entity';
import {
AudioCodec,
Colorspace,
ImageFormat,
TranscodeHWAccel,
TranscodePolicy,
TranscodeTarget,
@@ -82,12 +81,12 @@ export class MediaService {
const jobs: JobItem[] = [];
for (const asset of assets) {
if (!asset.previewPath || force) {
jobs.push({ name: JobName.GENERATE_THUMBNAIL, data: { id: asset.id } });
if (!asset.resizePath || force) {
jobs.push({ name: JobName.GENERATE_JPEG_THUMBNAIL, data: { id: asset.id } });
continue;
}
if (!asset.thumbnailPath) {
jobs.push({ name: JobName.GENERATE_PREVIEW, data: { id: asset.id } });
if (!asset.webpPath) {
jobs.push({ name: JobName.GENERATE_WEBP_THUMBNAIL, data: { id: asset.id } });
}
if (!asset.thumbhash) {
jobs.push({ name: JobName.GENERATE_THUMBHASH_THUMBNAIL, data: { id: asset.id } });
@@ -153,41 +152,41 @@ export class MediaService {
}
async handleAssetMigration({ id }: IEntityJob): Promise<JobStatus> {
const { image } = await this.configCore.getConfig();
const [asset] = await this.assetRepository.getByIds([id]);
if (!asset) {
return JobStatus.FAILED;
}
await this.storageCore.moveAssetImage(asset, AssetPathType.PREVIEW, image.previewFormat);
await this.storageCore.moveAssetImage(asset, AssetPathType.THUMBNAIL, image.thumbnailFormat);
await this.storageCore.moveAssetVideo(asset);
await this.storageCore.moveAssetFile(asset, AssetPathType.JPEG_THUMBNAIL);
await this.storageCore.moveAssetFile(asset, AssetPathType.WEBP_THUMBNAIL);
await this.storageCore.moveAssetFile(asset, AssetPathType.ENCODED_VIDEO);
return JobStatus.SUCCESS;
}
async handleGeneratePreview({ id }: IEntityJob): Promise<JobStatus> {
async handleGenerateJpegThumbnail({ id }: IEntityJob): Promise<JobStatus> {
const [asset] = await this.assetRepository.getByIds([id], { exifInfo: true });
if (!asset) {
return JobStatus.FAILED;
}
const previewPath = await this.generateThumbnail(asset, AssetPathType.PREVIEW, ImageFormat.JPEG);
await this.assetRepository.update({ id: asset.id, previewPath });
const resizePath = await this.generateThumbnail(asset, 'jpeg');
await this.assetRepository.update({ id: asset.id, resizePath });
return JobStatus.SUCCESS;
}
private async generateThumbnail(asset: AssetEntity, type: GeneratedImageType, format: ImageFormat) {
const { image, ffmpeg } = await this.configCore.getConfig();
const size = type === AssetPathType.PREVIEW ? image.previewSize : image.thumbnailSize;
const path = StorageCore.getImagePath(asset, type, format);
private async generateThumbnail(asset: AssetEntity, format: 'jpeg' | 'webp') {
const { thumbnail, ffmpeg } = await this.configCore.getConfig();
const size = format === 'jpeg' ? thumbnail.jpegSize : thumbnail.webpSize;
const path =
format === 'jpeg' ? StorageCore.getLargeThumbnailPath(asset) : StorageCore.getSmallThumbnailPath(asset);
this.storageCore.ensureFolders(path);
switch (asset.type) {
case AssetType.IMAGE: {
const colorspace = this.isSRGB(asset) ? Colorspace.SRGB : image.colorspace;
const imageOptions = { format, size, colorspace, quality: image.quality };
await this.mediaRepository.resize(asset.originalPath, path, imageOptions);
const colorspace = this.isSRGB(asset) ? Colorspace.SRGB : thumbnail.colorspace;
const thumbnailOptions = { format, size, colorspace, quality: thumbnail.quality };
await this.mediaRepository.resize(asset.originalPath, path, thumbnailOptions);
break;
}
@@ -215,24 +214,24 @@ export class MediaService {
return path;
}
async handleGenerateThumbnail({ id }: IEntityJob): Promise<JobStatus> {
async handleGenerateWebpThumbnail({ id }: IEntityJob): Promise<JobStatus> {
const [asset] = await this.assetRepository.getByIds([id], { exifInfo: true });
if (!asset) {
return JobStatus.FAILED;
}
const thumbnailPath = await this.generateThumbnail(asset, AssetPathType.THUMBNAIL, ImageFormat.WEBP);
await this.assetRepository.update({ id: asset.id, thumbnailPath });
const webpPath = await this.generateThumbnail(asset, 'webp');
await this.assetRepository.update({ id: asset.id, webpPath });
return JobStatus.SUCCESS;
}
async handleGenerateThumbhash({ id }: IEntityJob): Promise<JobStatus> {
async handleGenerateThumbhashThumbnail({ id }: IEntityJob): Promise<JobStatus> {
const [asset] = await this.assetRepository.getByIds([id]);
if (!asset?.previewPath) {
if (!asset?.resizePath) {
return JobStatus.FAILED;
}
const thumbhash = await this.mediaRepository.generateThumbhash(asset.previewPath);
const thumbhash = await this.mediaRepository.generateThumbhash(asset.resizePath);
await this.assetRepository.update({ id: asset.id, thumbhash });
return JobStatus.SUCCESS;
+3 -3
View File
@@ -53,9 +53,9 @@ export class MicroservicesService {
[JobName.MIGRATE_ASSET]: (data) => this.mediaService.handleAssetMigration(data),
[JobName.MIGRATE_PERSON]: (data) => this.personService.handlePersonMigration(data),
[JobName.QUEUE_GENERATE_THUMBNAILS]: (data) => this.mediaService.handleQueueGenerateThumbnails(data),
[JobName.GENERATE_THUMBNAIL]: (data) => this.mediaService.handleGeneratePreview(data),
[JobName.GENERATE_PREVIEW]: (data) => this.mediaService.handleGenerateThumbnail(data),
[JobName.GENERATE_THUMBHASH_THUMBNAIL]: (data) => this.mediaService.handleGenerateThumbhash(data),
[JobName.GENERATE_JPEG_THUMBNAIL]: (data) => this.mediaService.handleGenerateJpegThumbnail(data),
[JobName.GENERATE_WEBP_THUMBNAIL]: (data) => this.mediaService.handleGenerateWebpThumbnail(data),
[JobName.GENERATE_THUMBHASH_THUMBNAIL]: (data) => this.mediaService.handleGenerateThumbhashThumbnail(data),
[JobName.QUEUE_VIDEO_CONVERSION]: (data) => this.mediaService.handleQueueVideoConversion(data),
[JobName.VIDEO_CONVERSION]: (data) => this.mediaService.handleVideoConversion(data),
[JobName.QUEUE_METADATA_EXTRACTION]: (data) => this.metadataService.handleQueueMetadataExtraction(data),
+1 -1
View File
@@ -645,7 +645,7 @@ describe(PersonService.name, () => {
expect(machineLearningMock.detectFaces).toHaveBeenCalledWith(
'http://immich-machine-learning:3003',
{
imagePath: assetStub.image.previewPath,
imagePath: assetStub.image.resizePath,
},
{
enabled: true,

Some files were not shown because too many files have changed in this diff Show More