You've already forked immich
mirror of
https://github.com/immich-app/immich.git
synced 2025-07-17 09:47:44 +02:00
Merge branch 'main' into feat/notification-email-templates
This commit is contained in:
@ -102,6 +102,8 @@ For the mobile app, you can use `https://demo.immich.app/api` for the `Server En
|
||||
| Offline support | Yes | No |
|
||||
| Read-only gallery | Yes | Yes |
|
||||
| Stacked Photos | Yes | Yes |
|
||||
| Tags | No | Yes |
|
||||
| Folder View | No | Yes |
|
||||
|
||||
## Translations
|
||||
|
||||
|
@ -1 +1 @@
|
||||
20.18.0
|
||||
22.11.0
|
||||
|
@ -1,4 +1,4 @@
|
||||
FROM node:20.18.0-alpine3.20@sha256:c13b26e7e602ef2f1074aef304ce6e9b7dd284c419b35d89fcf3cc8e44a8def9 AS core
|
||||
FROM node:22.11.0-alpine3.20@sha256:f265794478aa0b1a23d85a492c8311ed795bc527c3fe7e43453b3c872dcd71a3 AS core
|
||||
|
||||
WORKDIR /usr/src/open-api/typescript-sdk
|
||||
COPY open-api/typescript-sdk/package*.json open-api/typescript-sdk/tsconfig*.json ./
|
||||
|
195
cli/package-lock.json
generated
195
cli/package-lock.json
generated
@ -24,7 +24,7 @@
|
||||
"@types/cli-progress": "^3.11.0",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/mock-fs": "^4.13.1",
|
||||
"@types/node": "^20.17.0",
|
||||
"@types/node": "^22.8.6",
|
||||
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
||||
"@typescript-eslint/parser": "^8.0.0",
|
||||
"@vitest/coverage-v8": "^2.0.5",
|
||||
@ -43,7 +43,7 @@
|
||||
"vite": "^5.0.12",
|
||||
"vite-tsconfig-paths": "^5.0.0",
|
||||
"vitest": "^2.0.5",
|
||||
"vitest-fetch-mock": "^0.3.0",
|
||||
"vitest-fetch-mock": "^0.4.0",
|
||||
"yaml": "^2.3.1"
|
||||
},
|
||||
"engines": {
|
||||
@ -59,7 +59,7 @@
|
||||
"@oazapfts/runtime": "^1.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.17.0",
|
||||
"@types/node": "^22.8.6",
|
||||
"typescript": "^5.3.3"
|
||||
}
|
||||
},
|
||||
@ -766,9 +766,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/core": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.6.0.tgz",
|
||||
"integrity": "sha512-8I2Q8ykA4J0x0o7cg67FPVnehcqWTBehu/lmY+bolPFHGjh49YzGBMXTvpqVgEbBdvNCSxj6iFgiIyHzf03lzg==",
|
||||
"version": "0.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.7.0.tgz",
|
||||
"integrity": "sha512-xp5Jirz5DyPYlPiKat8jaq0EmYvDXKKpzTbxXMpT9eqlRJkRKIz9AGMdlvYjih+im+QlhWrpvVjl8IPC/lHlUw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
@ -835,9 +835,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/js": {
|
||||
"version": "9.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.12.0.tgz",
|
||||
"integrity": "sha512-eohesHH8WFRUprDNyEREgqP6beG6htMeUYeCpkEgBCieCMme5r9zFWjzAJp//9S+Kub4rqE+jXe9Cp1a7IYIIA==",
|
||||
"version": "9.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.13.0.tgz",
|
||||
"integrity": "sha512-IFLyoY4d72Z5y/6o/BazFBezupzI/taV8sGumxTAVw3lXG9A6md1Dc34T9s1FoD/an9pJH8RHbAxsaEbBed9lA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@ -1378,13 +1378,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "20.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.2.tgz",
|
||||
"integrity": "sha512-OOHK4sjXqkL7yQ7VEEHcf6+0jSvKjWqwnaCtY7AKD/VLEvRHMsxxu7eI8ErnjxHS8VwmekD4PeVCpu4qZEZSxg==",
|
||||
"version": "22.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz",
|
||||
"integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~6.19.2"
|
||||
"undici-types": "~6.19.8"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/normalize-package-data": {
|
||||
@ -1394,17 +1394,17 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "8.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.10.0.tgz",
|
||||
"integrity": "sha512-phuB3hoP7FFKbRXxjl+DRlQDuJqhpOnm5MmtROXyWi3uS/Xg2ZXqiQfcG2BJHiN4QKyzdOJi3NEn/qTnjUlkmQ==",
|
||||
"version": "8.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.11.0.tgz",
|
||||
"integrity": "sha512-KhGn2LjW1PJT2A/GfDpiyOfS4a8xHQv2myUagTM5+zsormOmBlYsnQ6pobJ8XxJmh6hnHwa2Mbe3fPrDJoDhbA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/regexpp": "^4.10.0",
|
||||
"@typescript-eslint/scope-manager": "8.10.0",
|
||||
"@typescript-eslint/type-utils": "8.10.0",
|
||||
"@typescript-eslint/utils": "8.10.0",
|
||||
"@typescript-eslint/visitor-keys": "8.10.0",
|
||||
"@typescript-eslint/scope-manager": "8.11.0",
|
||||
"@typescript-eslint/type-utils": "8.11.0",
|
||||
"@typescript-eslint/utils": "8.11.0",
|
||||
"@typescript-eslint/visitor-keys": "8.11.0",
|
||||
"graphemer": "^1.4.0",
|
||||
"ignore": "^5.3.1",
|
||||
"natural-compare": "^1.4.0",
|
||||
@ -1428,16 +1428,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser": {
|
||||
"version": "8.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.10.0.tgz",
|
||||
"integrity": "sha512-E24l90SxuJhytWJ0pTQydFT46Nk0Z+bsLKo/L8rtQSL93rQ6byd1V/QbDpHUTdLPOMsBCcYXZweADNCfOCmOAg==",
|
||||
"version": "8.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.11.0.tgz",
|
||||
"integrity": "sha512-lmt73NeHdy1Q/2ul295Qy3uninSqi6wQI18XwSpm8w0ZbQXUpjCAWP1Vlv/obudoBiIjJVjlztjQ+d/Md98Yxg==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.10.0",
|
||||
"@typescript-eslint/types": "8.10.0",
|
||||
"@typescript-eslint/typescript-estree": "8.10.0",
|
||||
"@typescript-eslint/visitor-keys": "8.10.0",
|
||||
"@typescript-eslint/scope-manager": "8.11.0",
|
||||
"@typescript-eslint/types": "8.11.0",
|
||||
"@typescript-eslint/typescript-estree": "8.11.0",
|
||||
"@typescript-eslint/visitor-keys": "8.11.0",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
@ -1457,14 +1457,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "8.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.10.0.tgz",
|
||||
"integrity": "sha512-AgCaEjhfql9MDKjMUxWvH7HjLeBqMCBfIaBbzzIcBbQPZE7CPh1m6FF+L75NUMJFMLYhCywJXIDEMa3//1A0dw==",
|
||||
"version": "8.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.11.0.tgz",
|
||||
"integrity": "sha512-Uholz7tWhXmA4r6epo+vaeV7yjdKy5QFCERMjs1kMVsLRKIrSdM6o21W2He9ftp5PP6aWOVpD5zvrvuHZC0bMQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.10.0",
|
||||
"@typescript-eslint/visitor-keys": "8.10.0"
|
||||
"@typescript-eslint/types": "8.11.0",
|
||||
"@typescript-eslint/visitor-keys": "8.11.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@ -1475,14 +1475,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/type-utils": {
|
||||
"version": "8.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.10.0.tgz",
|
||||
"integrity": "sha512-PCpUOpyQSpxBn230yIcK+LeCQaXuxrgCm2Zk1S+PTIRJsEfU6nJ0TtwyH8pIwPK/vJoA+7TZtzyAJSGBz+s/dg==",
|
||||
"version": "8.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.11.0.tgz",
|
||||
"integrity": "sha512-ItiMfJS6pQU0NIKAaybBKkuVzo6IdnAhPFZA/2Mba/uBjuPQPet/8+zh5GtLHwmuFRShZx+8lhIs7/QeDHflOg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/typescript-estree": "8.10.0",
|
||||
"@typescript-eslint/utils": "8.10.0",
|
||||
"@typescript-eslint/typescript-estree": "8.11.0",
|
||||
"@typescript-eslint/utils": "8.11.0",
|
||||
"debug": "^4.3.4",
|
||||
"ts-api-utils": "^1.3.0"
|
||||
},
|
||||
@ -1500,9 +1500,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/types": {
|
||||
"version": "8.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.10.0.tgz",
|
||||
"integrity": "sha512-k/E48uzsfJCRRbGLapdZgrX52csmWJ2rcowwPvOZ8lwPUv3xW6CcFeJAXgx4uJm+Ge4+a4tFOkdYvSpxhRhg1w==",
|
||||
"version": "8.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.11.0.tgz",
|
||||
"integrity": "sha512-tn6sNMHf6EBAYMvmPUaKaVeYvhUsrE6x+bXQTxjQRp360h1giATU0WvgeEys1spbvb5R+VpNOZ+XJmjD8wOUHw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@ -1514,14 +1514,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "8.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.10.0.tgz",
|
||||
"integrity": "sha512-3OE0nlcOHaMvQ8Xu5gAfME3/tWVDpb/HxtpUZ1WeOAksZ/h/gwrBzCklaGzwZT97/lBbbxJ16dMA98JMEngW4w==",
|
||||
"version": "8.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.11.0.tgz",
|
||||
"integrity": "sha512-yHC3s1z1RCHoCz5t06gf7jH24rr3vns08XXhfEqzYpd6Hll3z/3g23JRi0jM8A47UFKNc3u/y5KIMx8Ynbjohg==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.10.0",
|
||||
"@typescript-eslint/visitor-keys": "8.10.0",
|
||||
"@typescript-eslint/types": "8.11.0",
|
||||
"@typescript-eslint/visitor-keys": "8.11.0",
|
||||
"debug": "^4.3.4",
|
||||
"fast-glob": "^3.3.2",
|
||||
"is-glob": "^4.0.3",
|
||||
@ -1543,16 +1543,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils": {
|
||||
"version": "8.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.10.0.tgz",
|
||||
"integrity": "sha512-Oq4uZ7JFr9d1ZunE/QKy5egcDRXT/FrS2z/nlxzPua2VHFtmMvFNDvpq1m/hq0ra+T52aUezfcjGRIB7vNJF9w==",
|
||||
"version": "8.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.11.0.tgz",
|
||||
"integrity": "sha512-CYiX6WZcbXNJV7UNB4PLDIBtSdRmRI/nb0FMyqHPTQD1rMjA0foPLaPUV39C/MxkTd/QKSeX+Gb34PPsDVC35g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.4.0",
|
||||
"@typescript-eslint/scope-manager": "8.10.0",
|
||||
"@typescript-eslint/types": "8.10.0",
|
||||
"@typescript-eslint/typescript-estree": "8.10.0"
|
||||
"@typescript-eslint/scope-manager": "8.11.0",
|
||||
"@typescript-eslint/types": "8.11.0",
|
||||
"@typescript-eslint/typescript-estree": "8.11.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@ -1566,13 +1566,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "8.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.10.0.tgz",
|
||||
"integrity": "sha512-k8nekgqwr7FadWk548Lfph6V3r9OVqjzAIVskE7orMZR23cGJjAOVazsZSJW+ElyjfTM4wx/1g88Mi70DDtG9A==",
|
||||
"version": "8.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.11.0.tgz",
|
||||
"integrity": "sha512-EaewX6lxSjRJnc+99+dqzTeoDZUfyrA52d2/HRrkI830kgovWsmIiTfmr0NZorzqic7ga+1bS60lRBUgR3n/Bw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.10.0",
|
||||
"@typescript-eslint/types": "8.11.0",
|
||||
"eslint-visitor-keys": "^3.4.3"
|
||||
},
|
||||
"engines": {
|
||||
@ -2067,15 +2067,6 @@
|
||||
"url": "https://opencollective.com/core-js"
|
||||
}
|
||||
},
|
||||
"node_modules/cross-fetch": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz",
|
||||
"integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"node-fetch": "^2.6.12"
|
||||
}
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
||||
@ -2211,18 +2202,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint": {
|
||||
"version": "9.12.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.12.0.tgz",
|
||||
"integrity": "sha512-UVIOlTEWxwIopRL1wgSQYdnVDcEvs2wyaO6DGo5mXqe3r16IoCNWkR29iHhyaP4cICWjbgbmFUGAhh0GJRuGZw==",
|
||||
"version": "9.13.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.13.0.tgz",
|
||||
"integrity": "sha512-EYZK6SX6zjFHST/HRytOdA/zE72Cq/bfw45LSyuwrdvcclb/gqV8RRQxywOBEWO2+WDpva6UZa4CcDeJKzUCFA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.2.0",
|
||||
"@eslint-community/regexpp": "^4.11.0",
|
||||
"@eslint/config-array": "^0.18.0",
|
||||
"@eslint/core": "^0.6.0",
|
||||
"@eslint/core": "^0.7.0",
|
||||
"@eslint/eslintrc": "^3.1.0",
|
||||
"@eslint/js": "9.12.0",
|
||||
"@eslint/js": "9.13.0",
|
||||
"@eslint/plugin-kit": "^0.2.0",
|
||||
"@humanfs/node": "^0.16.5",
|
||||
"@humanwhocodes/module-importer": "^1.0.1",
|
||||
@ -2396,9 +2387,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint/node_modules/eslint-visitor-keys": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.1.0.tgz",
|
||||
"integrity": "sha512-Q7lok0mqMUSf5a/AdAZkA5a/gHcO6snwQClVNNvFKCAVlxXucdU8pKydU5ZVZjBx5xr37vGbFFWtLQYreLzrZg==",
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz",
|
||||
"integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
@ -3189,26 +3180,6 @@
|
||||
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/node-fetch": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
|
||||
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "4.x || >=6.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"encoding": "^0.1.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"encoding": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/node-releases": {
|
||||
"version": "2.0.14",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz",
|
||||
@ -4054,12 +4025,6 @@
|
||||
"node": ">=8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tr46": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/ts-api-utils": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz",
|
||||
@ -4181,9 +4146,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "5.4.9",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.9.tgz",
|
||||
"integrity": "sha512-20OVpJHh0PAM0oSOELa5GaZNWeDjcAvQjGXy2Uyr+Tp+/D2/Hdz6NLgpJLsarPTA2QJ6v8mX2P1ZfbsSKvdMkg==",
|
||||
"version": "5.4.10",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.10.tgz",
|
||||
"integrity": "sha512-1hvaPshuPUtxeQ0hsVH3Mud0ZanOLwVTneA1EgbAM5LhaZEqyPWGRQ7BtaMvUrTDeEaC8pxtj6a6jku3x4z6SQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -4348,36 +4313,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vitest-fetch-mock": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/vitest-fetch-mock/-/vitest-fetch-mock-0.3.0.tgz",
|
||||
"integrity": "sha512-g6upWcL8/32fXL43/5f4VHcocuwQIi9Fj5othcK9gPO8XqSEGtnIZdenr2IaipDr61ReRFt+vaOEgo8jiUUX5w==",
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/vitest-fetch-mock/-/vitest-fetch-mock-0.4.1.tgz",
|
||||
"integrity": "sha512-Y6VEV2AgJps1t9NUdhID/vUwarAuhOkPHShfoEruIlQr5+O31hgJ4YmZpU8kVWD3KQjEyZqPeMibWehd7rMq+A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"cross-fetch": "^4.0.0"
|
||||
},
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14.14.0"
|
||||
"node": ">=18.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vitest": ">=2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/webidl-conversions": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/whatwg-url": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"tr46": "~0.0.3",
|
||||
"webidl-conversions": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
|
@ -20,7 +20,7 @@
|
||||
"@types/cli-progress": "^3.11.0",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/mock-fs": "^4.13.1",
|
||||
"@types/node": "^20.17.0",
|
||||
"@types/node": "^22.8.6",
|
||||
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
||||
"@typescript-eslint/parser": "^8.0.0",
|
||||
"@vitest/coverage-v8": "^2.0.5",
|
||||
@ -39,7 +39,7 @@
|
||||
"vite": "^5.0.12",
|
||||
"vite-tsconfig-paths": "^5.0.0",
|
||||
"vitest": "^2.0.5",
|
||||
"vitest-fetch-mock": "^0.3.0",
|
||||
"vitest-fetch-mock": "^0.4.0",
|
||||
"yaml": "^2.3.1"
|
||||
},
|
||||
"scripts": {
|
||||
@ -67,6 +67,6 @@
|
||||
"lodash-es": "^4.17.21"
|
||||
},
|
||||
"volta": {
|
||||
"node": "20.18.0"
|
||||
"node": "22.11.0"
|
||||
}
|
||||
}
|
||||
|
@ -2,37 +2,37 @@
|
||||
# Manual edits may be lost in future updates.
|
||||
|
||||
provider "registry.opentofu.org/cloudflare/cloudflare" {
|
||||
version = "4.44.0"
|
||||
constraints = "4.44.0"
|
||||
version = "4.45.0"
|
||||
constraints = "4.45.0"
|
||||
hashes = [
|
||||
"h1:2kgcFIKdPgw/22FoLI+54G2+Np3L0np8ost6e8VwAhU=",
|
||||
"h1:5FQNWKXde8kkfEJHeu5yBaC0NxCzeOKs2K0QMGTicAQ=",
|
||||
"h1:8rq9U72Ieus2zn1M1FVKgEnEO2f+ZFx/ISMefIEt6kc=",
|
||||
"h1:MeMGdavako/OPTU/qAgKRIQKD49x9tn4PGrOTWi9tFE=",
|
||||
"h1:PIP4W0AHpcV+jnKMlZmNd6MtfXMJlKjBdR2DYOX8rTs=",
|
||||
"h1:TSWZWvxliZCajkWOq5wOSsB6TJdu3nhhaaAcFL6aBdM=",
|
||||
"h1:VKuCmJyi+i9UlIHl2ezQkmGZgZMm8E+WHkxTheDGcFc=",
|
||||
"h1:WL/GOSfwJuTfyGnl17hXxBA24Pe+pG5o6ONKfGdAesM=",
|
||||
"h1:XicUpPrAY/QJvE3qncz/LtAfeS43fXgI5lihOBmWzpI=",
|
||||
"h1:YftzVnHzmOxfK9an5wzHnB7kLAEMzoNnEgRffntMIFI=",
|
||||
"h1:ZQrgBiB//NLYh3K6gOT1nJp5ezBqUQJ1Qx5eTpMpBB4=",
|
||||
"h1:cRV923M29H/H3NfvlVGsGkvyn8P7ovsMU5BNb9U1d+o=",
|
||||
"h1:qivroEJiR4Vycmc4E4DherPkBCAm4xfet2Sr87uIXhY=",
|
||||
"h1:z2119TMCuS7zEYg2oDYff5EnHWtYSVv0mcOJ25wl3i8=",
|
||||
"zh:0cae95e8c9d2d979669712745251dcf5720cee2a59bb81d8ad2c2dcf0e6e0c7a",
|
||||
"zh:1220aee9549e7938648f6a36237929ead0de8244c6a00f8e8cded559f4b65a2f",
|
||||
"zh:23ae1862e5fe5b583b8ec2c96f80a5ba0e3883be8e1169a0484a45106cc238ce",
|
||||
"zh:3034654c6f34e419c53dcd6ea558b715e1150fbcc70c93209c5ee88a03025072",
|
||||
"zh:3b64a66f3ddeb04345511262ad9376eb3c26e0683a78f47a3fd7f5e71f3f7e27",
|
||||
"zh:4b29435e1e8f970b92bb38eca52820f7a8362c16235334aef9a83be32bd00094",
|
||||
"zh:4f8fe69db7f54bce0e78a4c671aa5db20515114626035051f387d9833f4a5a91",
|
||||
"zh:86776bfbdabd2095975be9b3ca999c2f47ca5194ece6c58c69130ccfa2e3c97d",
|
||||
"h1:/CGpnYMkLRDmqn4iAsh/jg7ELZ6QExUw03VdjKZyK5M=",
|
||||
"h1:82C/ryqwQvxhBINYOOyF5ZzPW/k4zJ/RYT13eCdPgEc=",
|
||||
"h1:8Wu1D7ZwbLGdHakLRAzoAJ5VqZ8I14qzkPv1OGNfIlg=",
|
||||
"h1:CVq0CAibeueOuiNk0UQtwZvMLMof33n1BgskFPOymrk=",
|
||||
"h1:FSS5Kq+L+CX1zARy8PhaF8edBFNgsLtds4Uo8MwJiK8=",
|
||||
"h1:L4qsorLII7f8xSFmv6JOoWfLWDunWQEpK964Bxk7mtM=",
|
||||
"h1:StO3PV5PDskSCnhoHhWHOPxu6hbzJUQggfLgOSkvhwg=",
|
||||
"h1:Tjo+Er9ets5YrTRIdP9LBmi4p89nL/W+A7r8a1MM9nI=",
|
||||
"h1:XIwT+AWvks1LTytePM9zls+O8ItxoqCfPOgHwuH9ivQ=",
|
||||
"h1:aOXn/zuM1+5GGy/SSRx8q4EYCSTFE9Tr0twHPIf5/KE=",
|
||||
"h1:lb+YcuZ4guYd8zE51vgSnDsRAD9IV00Z15l1i1X52s8=",
|
||||
"h1:pYwNXGjfXA2rUEmotGMLWgmavT9D2rdHnV3TpuIK3ko=",
|
||||
"h1:q1qrnPq6KkljwBrugCwzb7f0SVP4Lzkfh+EOLARY9V8=",
|
||||
"h1:v9sL4cZLTV5Gu2004DDyy7209gT0JmudBCAD0WCr/JE=",
|
||||
"zh:00be2a6adc76615a368491c7a026098103b6286deb31e3cfb037365dd39f095f",
|
||||
"zh:05bd072e6119f7a5abff05c6064001f745473119a956586cf77ae843cf55d666",
|
||||
"zh:228bbe61345c4e8e0bc6b698b4b9652abff65662ee72ede2aecb4c3efb91b243",
|
||||
"zh:2948aeefe71ba041c94082cf931ecc95510d93af0a61d0a287880f5b9d24b11a",
|
||||
"zh:5dfc2c5e95843ca54957212ee3ecb7ff06f2cf60bfd6ca278b5249fd70ac18f5",
|
||||
"zh:69922cb45559b0b0544b9c2d31ed2d0fac9121faa75bc2f523484785b45d8e2b",
|
||||
"zh:890df766e9b839623b1f0437355032a3c006226a6c200cd911e15ee1a9014e9f",
|
||||
"zh:9d50271a09ee01a7105c06e582c52743a3baaf46f298d52bfc9e64cd7bfaa712",
|
||||
"zh:a1e12d1c3472d457140de0e8b77a4b09e5cdcd3e2f6c0be0fe0dae0526d368db",
|
||||
"zh:ad638e2c91490367d55ec6fe46ee34a9f7c151ca6e3cc52e5bad9f358e77f1aa",
|
||||
"zh:cc4c496f2c594994a9d966f7ebe00a797eca9b924ac1bbe5aef26ec83ec7f833",
|
||||
"zh:e74d5a3695deb38c2858d1c99c424495900e9b298ca8961c0a2fc1b3714c4c77",
|
||||
"zh:f4b6efa4c2b4c85c92171dc0824dbf42af9dde5250131494de803e0b9fe1ea3c",
|
||||
"zh:9d83a0cbf72327286f7dbd63cd4af89059c648163fe6ed21b1df768e0518d445",
|
||||
"zh:a8e1982945822c7d7aaa6ba8602c7247d1a3fad15d612f30eb323491a637bf8d",
|
||||
"zh:c6d41ebd69ddb23e3dad49a0ebf1da5a9c7d8706a4f55d953115d371f407928b",
|
||||
"zh:d03e5442b12846c2737f099d30cd23d9f85a0c6d65437ccb44819f9a6c4e1d7f",
|
||||
"zh:d446f2e1186b35037aea03b0e27d8b032d2f069f194f84b3f0e2907b3a79a955",
|
||||
"zh:e4d7549a4c856524e01f3dd4d69f57119ea205f7a0fa38dcfe154475b4ae9258",
|
||||
"zh:e64b8915cb9686f85e77115bd674f2faf4f29880688067d7d0f1376566fdb3b0",
|
||||
"zh:f046efdc55e6385cdd69baaa06a929bef9fe6809d373b0d2d6c7df8f8c23eddc",
|
||||
]
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ terraform {
|
||||
required_providers {
|
||||
cloudflare = {
|
||||
source = "cloudflare/cloudflare"
|
||||
version = "4.44.0"
|
||||
version = "4.45.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,37 +2,37 @@
|
||||
# Manual edits may be lost in future updates.
|
||||
|
||||
provider "registry.opentofu.org/cloudflare/cloudflare" {
|
||||
version = "4.44.0"
|
||||
constraints = "4.44.0"
|
||||
version = "4.45.0"
|
||||
constraints = "4.45.0"
|
||||
hashes = [
|
||||
"h1:2kgcFIKdPgw/22FoLI+54G2+Np3L0np8ost6e8VwAhU=",
|
||||
"h1:5FQNWKXde8kkfEJHeu5yBaC0NxCzeOKs2K0QMGTicAQ=",
|
||||
"h1:8rq9U72Ieus2zn1M1FVKgEnEO2f+ZFx/ISMefIEt6kc=",
|
||||
"h1:MeMGdavako/OPTU/qAgKRIQKD49x9tn4PGrOTWi9tFE=",
|
||||
"h1:PIP4W0AHpcV+jnKMlZmNd6MtfXMJlKjBdR2DYOX8rTs=",
|
||||
"h1:TSWZWvxliZCajkWOq5wOSsB6TJdu3nhhaaAcFL6aBdM=",
|
||||
"h1:VKuCmJyi+i9UlIHl2ezQkmGZgZMm8E+WHkxTheDGcFc=",
|
||||
"h1:WL/GOSfwJuTfyGnl17hXxBA24Pe+pG5o6ONKfGdAesM=",
|
||||
"h1:XicUpPrAY/QJvE3qncz/LtAfeS43fXgI5lihOBmWzpI=",
|
||||
"h1:YftzVnHzmOxfK9an5wzHnB7kLAEMzoNnEgRffntMIFI=",
|
||||
"h1:ZQrgBiB//NLYh3K6gOT1nJp5ezBqUQJ1Qx5eTpMpBB4=",
|
||||
"h1:cRV923M29H/H3NfvlVGsGkvyn8P7ovsMU5BNb9U1d+o=",
|
||||
"h1:qivroEJiR4Vycmc4E4DherPkBCAm4xfet2Sr87uIXhY=",
|
||||
"h1:z2119TMCuS7zEYg2oDYff5EnHWtYSVv0mcOJ25wl3i8=",
|
||||
"zh:0cae95e8c9d2d979669712745251dcf5720cee2a59bb81d8ad2c2dcf0e6e0c7a",
|
||||
"zh:1220aee9549e7938648f6a36237929ead0de8244c6a00f8e8cded559f4b65a2f",
|
||||
"zh:23ae1862e5fe5b583b8ec2c96f80a5ba0e3883be8e1169a0484a45106cc238ce",
|
||||
"zh:3034654c6f34e419c53dcd6ea558b715e1150fbcc70c93209c5ee88a03025072",
|
||||
"zh:3b64a66f3ddeb04345511262ad9376eb3c26e0683a78f47a3fd7f5e71f3f7e27",
|
||||
"zh:4b29435e1e8f970b92bb38eca52820f7a8362c16235334aef9a83be32bd00094",
|
||||
"zh:4f8fe69db7f54bce0e78a4c671aa5db20515114626035051f387d9833f4a5a91",
|
||||
"zh:86776bfbdabd2095975be9b3ca999c2f47ca5194ece6c58c69130ccfa2e3c97d",
|
||||
"h1:/CGpnYMkLRDmqn4iAsh/jg7ELZ6QExUw03VdjKZyK5M=",
|
||||
"h1:82C/ryqwQvxhBINYOOyF5ZzPW/k4zJ/RYT13eCdPgEc=",
|
||||
"h1:8Wu1D7ZwbLGdHakLRAzoAJ5VqZ8I14qzkPv1OGNfIlg=",
|
||||
"h1:CVq0CAibeueOuiNk0UQtwZvMLMof33n1BgskFPOymrk=",
|
||||
"h1:FSS5Kq+L+CX1zARy8PhaF8edBFNgsLtds4Uo8MwJiK8=",
|
||||
"h1:L4qsorLII7f8xSFmv6JOoWfLWDunWQEpK964Bxk7mtM=",
|
||||
"h1:StO3PV5PDskSCnhoHhWHOPxu6hbzJUQggfLgOSkvhwg=",
|
||||
"h1:Tjo+Er9ets5YrTRIdP9LBmi4p89nL/W+A7r8a1MM9nI=",
|
||||
"h1:XIwT+AWvks1LTytePM9zls+O8ItxoqCfPOgHwuH9ivQ=",
|
||||
"h1:aOXn/zuM1+5GGy/SSRx8q4EYCSTFE9Tr0twHPIf5/KE=",
|
||||
"h1:lb+YcuZ4guYd8zE51vgSnDsRAD9IV00Z15l1i1X52s8=",
|
||||
"h1:pYwNXGjfXA2rUEmotGMLWgmavT9D2rdHnV3TpuIK3ko=",
|
||||
"h1:q1qrnPq6KkljwBrugCwzb7f0SVP4Lzkfh+EOLARY9V8=",
|
||||
"h1:v9sL4cZLTV5Gu2004DDyy7209gT0JmudBCAD0WCr/JE=",
|
||||
"zh:00be2a6adc76615a368491c7a026098103b6286deb31e3cfb037365dd39f095f",
|
||||
"zh:05bd072e6119f7a5abff05c6064001f745473119a956586cf77ae843cf55d666",
|
||||
"zh:228bbe61345c4e8e0bc6b698b4b9652abff65662ee72ede2aecb4c3efb91b243",
|
||||
"zh:2948aeefe71ba041c94082cf931ecc95510d93af0a61d0a287880f5b9d24b11a",
|
||||
"zh:5dfc2c5e95843ca54957212ee3ecb7ff06f2cf60bfd6ca278b5249fd70ac18f5",
|
||||
"zh:69922cb45559b0b0544b9c2d31ed2d0fac9121faa75bc2f523484785b45d8e2b",
|
||||
"zh:890df766e9b839623b1f0437355032a3c006226a6c200cd911e15ee1a9014e9f",
|
||||
"zh:9d50271a09ee01a7105c06e582c52743a3baaf46f298d52bfc9e64cd7bfaa712",
|
||||
"zh:a1e12d1c3472d457140de0e8b77a4b09e5cdcd3e2f6c0be0fe0dae0526d368db",
|
||||
"zh:ad638e2c91490367d55ec6fe46ee34a9f7c151ca6e3cc52e5bad9f358e77f1aa",
|
||||
"zh:cc4c496f2c594994a9d966f7ebe00a797eca9b924ac1bbe5aef26ec83ec7f833",
|
||||
"zh:e74d5a3695deb38c2858d1c99c424495900e9b298ca8961c0a2fc1b3714c4c77",
|
||||
"zh:f4b6efa4c2b4c85c92171dc0824dbf42af9dde5250131494de803e0b9fe1ea3c",
|
||||
"zh:9d83a0cbf72327286f7dbd63cd4af89059c648163fe6ed21b1df768e0518d445",
|
||||
"zh:a8e1982945822c7d7aaa6ba8602c7247d1a3fad15d612f30eb323491a637bf8d",
|
||||
"zh:c6d41ebd69ddb23e3dad49a0ebf1da5a9c7d8706a4f55d953115d371f407928b",
|
||||
"zh:d03e5442b12846c2737f099d30cd23d9f85a0c6d65437ccb44819f9a6c4e1d7f",
|
||||
"zh:d446f2e1186b35037aea03b0e27d8b032d2f069f194f84b3f0e2907b3a79a955",
|
||||
"zh:e4d7549a4c856524e01f3dd4d69f57119ea205f7a0fa38dcfe154475b4ae9258",
|
||||
"zh:e64b8915cb9686f85e77115bd674f2faf4f29880688067d7d0f1376566fdb3b0",
|
||||
"zh:f046efdc55e6385cdd69baaa06a929bef9fe6809d373b0d2d6c7df8f8c23eddc",
|
||||
]
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ terraform {
|
||||
required_providers {
|
||||
cloudflare = {
|
||||
source = "cloudflare/cloudflare"
|
||||
version = "4.44.0"
|
||||
version = "4.45.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -143,7 +143,7 @@ services:
|
||||
'wal_compression=on',
|
||||
]
|
||||
|
||||
# set IMMICH_METRICS=true in .env to enable metrics
|
||||
# set IMMICH_TELEMETRY_INCLUDE=all in .env to enable metrics
|
||||
# immich-prometheus:
|
||||
# container_name: immich_prometheus
|
||||
# ports:
|
||||
|
@ -89,7 +89,7 @@ services:
|
||||
]
|
||||
restart: always
|
||||
|
||||
# set IMMICH_METRICS=true in .env to enable metrics
|
||||
# set IMMICH_TELEMETRY_INCLUDE=all in .env to enable metrics
|
||||
immich-prometheus:
|
||||
container_name: immich_prometheus
|
||||
ports:
|
||||
@ -106,7 +106,7 @@ services:
|
||||
command: ['./run.sh', '-disable-reporting']
|
||||
ports:
|
||||
- 3000:3000
|
||||
image: grafana/grafana:11.2.2-ubuntu@sha256:2bef00403c18d27919ff19d64fd6253fa713b3880304e92f69109e14221ac843
|
||||
image: grafana/grafana:11.3.0-ubuntu@sha256:51587e148ac0214d7938e7f3fe8512182e4eb6141892a3ffb88bba1901b49285
|
||||
volumes:
|
||||
- grafana-data:/var/lib/grafana
|
||||
|
||||
|
@ -1 +1 @@
|
||||
20.18.0
|
||||
22.11.0
|
||||
|
@ -21,6 +21,17 @@ The recommended way to backup and restore the Immich database is to use the `pg_
|
||||
It is not recommended to directly backup the `DB_DATA_LOCATION` folder. Doing so while the database is running can lead to a corrupted backup that cannot be restored.
|
||||
:::
|
||||
|
||||
### Automatic Database Backups
|
||||
|
||||
Immich will automatically create database backups by default. The backups are stored in `UPLOAD_LOCATION/backups`.
|
||||
You can adjust the schedule and amount of kept backups in the [admin settings](http://my.immich.app/admin/system-settings?isOpen=backup).
|
||||
By default, Immich will keep the last 14 backups and create a new backup every day at 2:00 AM.
|
||||
|
||||
#### Restoring
|
||||
|
||||
We hope to make restoring simpler in future versions, for now you can find the backups in the `UPLOAD_LOCATION/backups` folder on your host.
|
||||
Then please follow the steps in the following section for restoring the database.
|
||||
|
||||
### Manual Backup and Restore
|
||||
|
||||
<Tabs>
|
||||
|
@ -26,7 +26,6 @@ The default configuration looks like this:
|
||||
"bframes": -1,
|
||||
"refs": 0,
|
||||
"gopSize": 0,
|
||||
"npl": 0,
|
||||
"temporalAQ": false,
|
||||
"cqMode": "auto",
|
||||
"twoPass": false,
|
||||
|
7
docs/package-lock.json
generated
7
docs/package-lock.json
generated
@ -3006,9 +3006,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@mdx-js/react": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.0.1.tgz",
|
||||
"integrity": "sha512-9ZrPIU4MGf6et1m1ov3zKf+q9+deetI51zprKB1D/z3NOb+rUxxtEl3mCjW5wTGh6VhRdwPueh1oRzi6ezkA8A==",
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.1.0.tgz",
|
||||
"integrity": "sha512-QjHtSaoameoalGnKDT3FoIl4+9RwyTmo9ZJGBdLOks/YOiWHoRDI3PUwEzOE7kEmGcV3AFcp9K6dYu9rEuKLAQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/mdx": "^2.0.0"
|
||||
},
|
||||
|
@ -56,6 +56,6 @@
|
||||
"node": ">=20"
|
||||
},
|
||||
"volta": {
|
||||
"node": "20.18.0"
|
||||
"node": "22.11.0"
|
||||
}
|
||||
}
|
||||
|
@ -83,6 +83,12 @@ const projects: CommunityProjectProps[] = [
|
||||
description: 'Power tools for organizing your immich library.',
|
||||
url: 'https://github.com/varun-raj/immich-power-tools',
|
||||
},
|
||||
{
|
||||
title: 'Immich Public Proxy',
|
||||
description:
|
||||
'Share your Immich photos and albums in a safe way without exposing your Immich instance to the public.',
|
||||
url: 'https://github.com/alangrainger/immich-public-proxy',
|
||||
},
|
||||
];
|
||||
|
||||
function CommunityProject({ title, description, url }: CommunityProjectProps): JSX.Element {
|
||||
|
@ -1 +1 @@
|
||||
20.18.0
|
||||
22.11.0
|
||||
|
@ -19,7 +19,7 @@ services:
|
||||
- DB_PASSWORD=postgres
|
||||
- DB_DATABASE_NAME=immich
|
||||
- IMMICH_MACHINE_LEARNING_ENABLED=false
|
||||
- IMMICH_METRICS=true
|
||||
- IMMICH_TELEMETRY_INCLUDE=all
|
||||
- IMMICH_ENV=testing
|
||||
- IMMICH_PORT=2285
|
||||
- IMMICH_IGNORE_MOUNT_CHECK_ERRORS=true
|
||||
|
233
e2e/package-lock.json
generated
233
e2e/package-lock.json
generated
@ -15,7 +15,7 @@
|
||||
"@immich/sdk": "file:../open-api/typescript-sdk",
|
||||
"@playwright/test": "^1.44.1",
|
||||
"@types/luxon": "^3.4.2",
|
||||
"@types/node": "^20.17.0",
|
||||
"@types/node": "^22.8.6",
|
||||
"@types/oidc-provider": "^8.5.1",
|
||||
"@types/pg": "^8.11.0",
|
||||
"@types/pngjs": "^6.0.4",
|
||||
@ -64,7 +64,7 @@
|
||||
"@types/cli-progress": "^3.11.0",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/mock-fs": "^4.13.1",
|
||||
"@types/node": "^20.17.0",
|
||||
"@types/node": "^22.8.6",
|
||||
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
||||
"@typescript-eslint/parser": "^8.0.0",
|
||||
"@vitest/coverage-v8": "^2.0.5",
|
||||
@ -83,7 +83,7 @@
|
||||
"vite": "^5.0.12",
|
||||
"vite-tsconfig-paths": "^5.0.0",
|
||||
"vitest": "^2.0.5",
|
||||
"vitest-fetch-mock": "^0.3.0",
|
||||
"vitest-fetch-mock": "^0.4.0",
|
||||
"yaml": "^2.3.1"
|
||||
},
|
||||
"engines": {
|
||||
@ -99,7 +99,7 @@
|
||||
"@oazapfts/runtime": "^1.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.17.0",
|
||||
"@types/node": "^22.8.6",
|
||||
"typescript": "^5.3.3"
|
||||
}
|
||||
},
|
||||
@ -785,9 +785,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/core": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.6.0.tgz",
|
||||
"integrity": "sha512-8I2Q8ykA4J0x0o7cg67FPVnehcqWTBehu/lmY+bolPFHGjh49YzGBMXTvpqVgEbBdvNCSxj6iFgiIyHzf03lzg==",
|
||||
"version": "0.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.7.0.tgz",
|
||||
"integrity": "sha512-xp5Jirz5DyPYlPiKat8jaq0EmYvDXKKpzTbxXMpT9eqlRJkRKIz9AGMdlvYjih+im+QlhWrpvVjl8IPC/lHlUw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
@ -832,9 +832,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/js": {
|
||||
"version": "9.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.12.0.tgz",
|
||||
"integrity": "sha512-eohesHH8WFRUprDNyEREgqP6beG6htMeUYeCpkEgBCieCMme5r9zFWjzAJp//9S+Kub4rqE+jXe9Cp1a7IYIIA==",
|
||||
"version": "9.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.13.0.tgz",
|
||||
"integrity": "sha512-IFLyoY4d72Z5y/6o/BazFBezupzI/taV8sGumxTAVw3lXG9A6md1Dc34T9s1FoD/an9pJH8RHbAxsaEbBed9lA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@ -1060,19 +1060,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@koa/router": {
|
||||
"version": "12.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@koa/router/-/router-12.0.1.tgz",
|
||||
"integrity": "sha512-ribfPYfHb+Uw3b27Eiw6NPqjhIhTpVFzEWLwyc/1Xp+DCdwRRyIlAUODX+9bPARF6aQtUu1+/PHzdNvRzcs/+Q==",
|
||||
"version": "13.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@koa/router/-/router-13.1.0.tgz",
|
||||
"integrity": "sha512-mNVu1nvkpSd8Q8gMebGbCkDWJ51ODetrFvLKYusej+V0ByD4btqHYnPIzTBLXnQMVUlm/oxVwqmWBY3zQfZilw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "^4.3.4",
|
||||
"http-errors": "^2.0.0",
|
||||
"koa-compose": "^4.1.0",
|
||||
"methods": "^1.1.2",
|
||||
"path-to-regexp": "^6.2.1"
|
||||
"path-to-regexp": "^6.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 12"
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@mapbox/node-pre-gyp": {
|
||||
@ -1614,13 +1613,13 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "20.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.2.tgz",
|
||||
"integrity": "sha512-OOHK4sjXqkL7yQ7VEEHcf6+0jSvKjWqwnaCtY7AKD/VLEvRHMsxxu7eI8ErnjxHS8VwmekD4PeVCpu4qZEZSxg==",
|
||||
"version": "22.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz",
|
||||
"integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~6.19.2"
|
||||
"undici-types": "~6.19.8"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/normalize-package-data": {
|
||||
@ -1778,17 +1777,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "8.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.10.0.tgz",
|
||||
"integrity": "sha512-phuB3hoP7FFKbRXxjl+DRlQDuJqhpOnm5MmtROXyWi3uS/Xg2ZXqiQfcG2BJHiN4QKyzdOJi3NEn/qTnjUlkmQ==",
|
||||
"version": "8.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.11.0.tgz",
|
||||
"integrity": "sha512-KhGn2LjW1PJT2A/GfDpiyOfS4a8xHQv2myUagTM5+zsormOmBlYsnQ6pobJ8XxJmh6hnHwa2Mbe3fPrDJoDhbA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/regexpp": "^4.10.0",
|
||||
"@typescript-eslint/scope-manager": "8.10.0",
|
||||
"@typescript-eslint/type-utils": "8.10.0",
|
||||
"@typescript-eslint/utils": "8.10.0",
|
||||
"@typescript-eslint/visitor-keys": "8.10.0",
|
||||
"@typescript-eslint/scope-manager": "8.11.0",
|
||||
"@typescript-eslint/type-utils": "8.11.0",
|
||||
"@typescript-eslint/utils": "8.11.0",
|
||||
"@typescript-eslint/visitor-keys": "8.11.0",
|
||||
"graphemer": "^1.4.0",
|
||||
"ignore": "^5.3.1",
|
||||
"natural-compare": "^1.4.0",
|
||||
@ -1812,16 +1811,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser": {
|
||||
"version": "8.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.10.0.tgz",
|
||||
"integrity": "sha512-E24l90SxuJhytWJ0pTQydFT46Nk0Z+bsLKo/L8rtQSL93rQ6byd1V/QbDpHUTdLPOMsBCcYXZweADNCfOCmOAg==",
|
||||
"version": "8.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.11.0.tgz",
|
||||
"integrity": "sha512-lmt73NeHdy1Q/2ul295Qy3uninSqi6wQI18XwSpm8w0ZbQXUpjCAWP1Vlv/obudoBiIjJVjlztjQ+d/Md98Yxg==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.10.0",
|
||||
"@typescript-eslint/types": "8.10.0",
|
||||
"@typescript-eslint/typescript-estree": "8.10.0",
|
||||
"@typescript-eslint/visitor-keys": "8.10.0",
|
||||
"@typescript-eslint/scope-manager": "8.11.0",
|
||||
"@typescript-eslint/types": "8.11.0",
|
||||
"@typescript-eslint/typescript-estree": "8.11.0",
|
||||
"@typescript-eslint/visitor-keys": "8.11.0",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
@ -1841,14 +1840,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "8.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.10.0.tgz",
|
||||
"integrity": "sha512-AgCaEjhfql9MDKjMUxWvH7HjLeBqMCBfIaBbzzIcBbQPZE7CPh1m6FF+L75NUMJFMLYhCywJXIDEMa3//1A0dw==",
|
||||
"version": "8.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.11.0.tgz",
|
||||
"integrity": "sha512-Uholz7tWhXmA4r6epo+vaeV7yjdKy5QFCERMjs1kMVsLRKIrSdM6o21W2He9ftp5PP6aWOVpD5zvrvuHZC0bMQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.10.0",
|
||||
"@typescript-eslint/visitor-keys": "8.10.0"
|
||||
"@typescript-eslint/types": "8.11.0",
|
||||
"@typescript-eslint/visitor-keys": "8.11.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@ -1859,14 +1858,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/type-utils": {
|
||||
"version": "8.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.10.0.tgz",
|
||||
"integrity": "sha512-PCpUOpyQSpxBn230yIcK+LeCQaXuxrgCm2Zk1S+PTIRJsEfU6nJ0TtwyH8pIwPK/vJoA+7TZtzyAJSGBz+s/dg==",
|
||||
"version": "8.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.11.0.tgz",
|
||||
"integrity": "sha512-ItiMfJS6pQU0NIKAaybBKkuVzo6IdnAhPFZA/2Mba/uBjuPQPet/8+zh5GtLHwmuFRShZx+8lhIs7/QeDHflOg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/typescript-estree": "8.10.0",
|
||||
"@typescript-eslint/utils": "8.10.0",
|
||||
"@typescript-eslint/typescript-estree": "8.11.0",
|
||||
"@typescript-eslint/utils": "8.11.0",
|
||||
"debug": "^4.3.4",
|
||||
"ts-api-utils": "^1.3.0"
|
||||
},
|
||||
@ -1884,9 +1883,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/types": {
|
||||
"version": "8.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.10.0.tgz",
|
||||
"integrity": "sha512-k/E48uzsfJCRRbGLapdZgrX52csmWJ2rcowwPvOZ8lwPUv3xW6CcFeJAXgx4uJm+Ge4+a4tFOkdYvSpxhRhg1w==",
|
||||
"version": "8.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.11.0.tgz",
|
||||
"integrity": "sha512-tn6sNMHf6EBAYMvmPUaKaVeYvhUsrE6x+bXQTxjQRp360h1giATU0WvgeEys1spbvb5R+VpNOZ+XJmjD8wOUHw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@ -1898,14 +1897,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "8.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.10.0.tgz",
|
||||
"integrity": "sha512-3OE0nlcOHaMvQ8Xu5gAfME3/tWVDpb/HxtpUZ1WeOAksZ/h/gwrBzCklaGzwZT97/lBbbxJ16dMA98JMEngW4w==",
|
||||
"version": "8.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.11.0.tgz",
|
||||
"integrity": "sha512-yHC3s1z1RCHoCz5t06gf7jH24rr3vns08XXhfEqzYpd6Hll3z/3g23JRi0jM8A47UFKNc3u/y5KIMx8Ynbjohg==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.10.0",
|
||||
"@typescript-eslint/visitor-keys": "8.10.0",
|
||||
"@typescript-eslint/types": "8.11.0",
|
||||
"@typescript-eslint/visitor-keys": "8.11.0",
|
||||
"debug": "^4.3.4",
|
||||
"fast-glob": "^3.3.2",
|
||||
"is-glob": "^4.0.3",
|
||||
@ -1953,16 +1952,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils": {
|
||||
"version": "8.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.10.0.tgz",
|
||||
"integrity": "sha512-Oq4uZ7JFr9d1ZunE/QKy5egcDRXT/FrS2z/nlxzPua2VHFtmMvFNDvpq1m/hq0ra+T52aUezfcjGRIB7vNJF9w==",
|
||||
"version": "8.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.11.0.tgz",
|
||||
"integrity": "sha512-CYiX6WZcbXNJV7UNB4PLDIBtSdRmRI/nb0FMyqHPTQD1rMjA0foPLaPUV39C/MxkTd/QKSeX+Gb34PPsDVC35g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.4.0",
|
||||
"@typescript-eslint/scope-manager": "8.10.0",
|
||||
"@typescript-eslint/types": "8.10.0",
|
||||
"@typescript-eslint/typescript-estree": "8.10.0"
|
||||
"@typescript-eslint/scope-manager": "8.11.0",
|
||||
"@typescript-eslint/types": "8.11.0",
|
||||
"@typescript-eslint/typescript-estree": "8.11.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@ -1976,13 +1975,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "8.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.10.0.tgz",
|
||||
"integrity": "sha512-k8nekgqwr7FadWk548Lfph6V3r9OVqjzAIVskE7orMZR23cGJjAOVazsZSJW+ElyjfTM4wx/1g88Mi70DDtG9A==",
|
||||
"version": "8.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.11.0.tgz",
|
||||
"integrity": "sha512-EaewX6lxSjRJnc+99+dqzTeoDZUfyrA52d2/HRrkI830kgovWsmIiTfmr0NZorzqic7ga+1bS60lRBUgR3n/Bw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.10.0",
|
||||
"@typescript-eslint/types": "8.11.0",
|
||||
"eslint-visitor-keys": "^3.4.3"
|
||||
},
|
||||
"engines": {
|
||||
@ -3018,18 +3017,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint": {
|
||||
"version": "9.12.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.12.0.tgz",
|
||||
"integrity": "sha512-UVIOlTEWxwIopRL1wgSQYdnVDcEvs2wyaO6DGo5mXqe3r16IoCNWkR29iHhyaP4cICWjbgbmFUGAhh0GJRuGZw==",
|
||||
"version": "9.13.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.13.0.tgz",
|
||||
"integrity": "sha512-EYZK6SX6zjFHST/HRytOdA/zE72Cq/bfw45LSyuwrdvcclb/gqV8RRQxywOBEWO2+WDpva6UZa4CcDeJKzUCFA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.2.0",
|
||||
"@eslint-community/regexpp": "^4.11.0",
|
||||
"@eslint/config-array": "^0.18.0",
|
||||
"@eslint/core": "^0.6.0",
|
||||
"@eslint/core": "^0.7.0",
|
||||
"@eslint/eslintrc": "^3.1.0",
|
||||
"@eslint/js": "9.12.0",
|
||||
"@eslint/js": "9.13.0",
|
||||
"@eslint/plugin-kit": "^0.2.0",
|
||||
"@humanfs/node": "^0.16.5",
|
||||
"@humanwhocodes/module-importer": "^1.0.1",
|
||||
@ -3185,9 +3184,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint/node_modules/eslint-visitor-keys": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.1.0.tgz",
|
||||
"integrity": "sha512-Q7lok0mqMUSf5a/AdAZkA5a/gHcO6snwQClVNNvFKCAVlxXucdU8pKydU5ZVZjBx5xr37vGbFFWtLQYreLzrZg==",
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz",
|
||||
"integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
@ -3282,10 +3281,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eta": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/eta/-/eta-3.4.0.tgz",
|
||||
"integrity": "sha512-tCsc7WXTjrTx4ZjYLplcqrI3o4mYJ+Z6YspeuGL8tbt/hHoMchwBwtKfwM09svEY86iRapY93vUqQttcNuIO5Q==",
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/eta/-/eta-3.5.0.tgz",
|
||||
"integrity": "sha512-e3x3FBvGzeCIHhF+zhK8FZA2vC5uFn6b4HJjegUbIWrDb4mJ7JjTGMJY9VGIbRVpmSwHopNiaJibhjIr+HfLug==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
},
|
||||
@ -3294,9 +3294,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/exiftool-vendored": {
|
||||
"version": "28.6.0",
|
||||
"resolved": "https://registry.npmjs.org/exiftool-vendored/-/exiftool-vendored-28.6.0.tgz",
|
||||
"integrity": "sha512-Cx8/8ov1tKEacHhsi7FNYdisIhKq/SeQfprYSpYzwBuJwkPmCV8w7tTIvUJRQX9rvopXhBA4eBf1FPXqTZW5vA==",
|
||||
"version": "28.7.0",
|
||||
"resolved": "https://registry.npmjs.org/exiftool-vendored/-/exiftool-vendored-28.7.0.tgz",
|
||||
"integrity": "sha512-0zoq6kBS1yPjzJs+p0qZDinWEA72PTKoRk5ETYKfmeRcZAkhv83Y3HCpbb/LdgJJywfm8BcIJGezrBHvL7dVnQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -3307,14 +3307,14 @@
|
||||
"luxon": "^3.5.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"exiftool-vendored.exe": "12.97.0",
|
||||
"exiftool-vendored.pl": "12.97.0"
|
||||
"exiftool-vendored.exe": "12.99.0",
|
||||
"exiftool-vendored.pl": "12.99.0"
|
||||
}
|
||||
},
|
||||
"node_modules/exiftool-vendored.exe": {
|
||||
"version": "12.97.0",
|
||||
"resolved": "https://registry.npmjs.org/exiftool-vendored.exe/-/exiftool-vendored.exe-12.97.0.tgz",
|
||||
"integrity": "sha512-+HxyFigEJOtwRjP7PhEslhZKuVW2V0hvmHPHtbVtNKGfAUGcfc95xNTjASQfKJvc+9ZuvzdEBPkEQmyA/ZYdIw==",
|
||||
"version": "12.99.0",
|
||||
"resolved": "https://registry.npmjs.org/exiftool-vendored.exe/-/exiftool-vendored.exe-12.99.0.tgz",
|
||||
"integrity": "sha512-ffpJHCzC9OYJqw4JlPNtCwRy02jwhmnSJEF/QqEjpuIWDEnlRBQP/yWRh1Nw21K1R4FB4yG5PlCgEDu09VQz/w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
@ -3323,9 +3323,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/exiftool-vendored.pl": {
|
||||
"version": "12.97.0",
|
||||
"resolved": "https://registry.npmjs.org/exiftool-vendored.pl/-/exiftool-vendored.pl-12.97.0.tgz",
|
||||
"integrity": "sha512-mXe9JEH3csfyPWcC7+H6IpfaokDMMr4S45n7MtiobGPdeeh+kFnf1SQ9cxg4sF403P6IKVeYYPbzgKMlpro9eQ==",
|
||||
"version": "12.99.0",
|
||||
"resolved": "https://registry.npmjs.org/exiftool-vendored.pl/-/exiftool-vendored.pl-12.99.0.tgz",
|
||||
"integrity": "sha512-qRVEPQxtoerXF+izJ0O7jGAr5o0Uyvnyu7ao5DTKzF+V7Fv3SurE0l43oCeZPFKo/Ld4V7vEylhFCm4IHVZKWA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
@ -3942,12 +3942,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/iconv-lite": {
|
||||
"version": "0.4.24",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3"
|
||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
@ -4172,9 +4173,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/jose": {
|
||||
"version": "5.9.4",
|
||||
"resolved": "https://registry.npmjs.org/jose/-/jose-5.9.4.tgz",
|
||||
"integrity": "sha512-WBBl6au1qg6OHj67yCffCgFR3BADJBXN8MdRvCgJDuMv3driV2nHr7jdGvaKX9IolosAsn+M0XRArqLXUhyJHQ==",
|
||||
"version": "5.9.6",
|
||||
"resolved": "https://registry.npmjs.org/jose/-/jose-5.9.6.tgz",
|
||||
"integrity": "sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
@ -4768,33 +4769,34 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/oidc-provider": {
|
||||
"version": "8.5.1",
|
||||
"resolved": "https://registry.npmjs.org/oidc-provider/-/oidc-provider-8.5.1.tgz",
|
||||
"integrity": "sha512-Bm3EyxN68/KS76IlciJ3+4pnVtfdRWL+NghWpIF0XQbiRT1gzc6Qf/cyFmpL9yieko/jXYZ/uLHUv77jD00qww==",
|
||||
"version": "8.5.2",
|
||||
"resolved": "https://registry.npmjs.org/oidc-provider/-/oidc-provider-8.5.2.tgz",
|
||||
"integrity": "sha512-WhMIQ61KMgABvrYZJDuefOWFpX34DWgg+U4juKARplGhNUSNfHgJh6i6Mp+PTO08ZDk/oj1Ci7ScU6CAI/wgcg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@koa/cors": "^5.0.0",
|
||||
"@koa/router": "^12.0.1",
|
||||
"debug": "^4.3.5",
|
||||
"eta": "^3.4.0",
|
||||
"@koa/router": "^13.1.0",
|
||||
"debug": "^4.3.7",
|
||||
"eta": "^3.5.0",
|
||||
"got": "^13.0.0",
|
||||
"jose": "^5.6.2",
|
||||
"jose": "^5.9.4",
|
||||
"jsesc": "^3.0.2",
|
||||
"koa": "^2.15.3",
|
||||
"nanoid": "^5.0.7",
|
||||
"object-hash": "^3.0.0",
|
||||
"oidc-token-hash": "^5.0.3",
|
||||
"quick-lru": "^7.0.0",
|
||||
"raw-body": "^2.5.2"
|
||||
"raw-body": "^3.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/panva"
|
||||
}
|
||||
},
|
||||
"node_modules/oidc-provider/node_modules/nanoid": {
|
||||
"version": "5.0.7",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.0.7.tgz",
|
||||
"integrity": "sha512-oLxFY2gd2IqnjcYyOXD8XGCftpGtZP2AbHbOkthDkvRywH5ayNtPVy9YlOPcHckXzbLTCHpkb7FB+yuxKV13pQ==",
|
||||
"version": "5.0.8",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.0.8.tgz",
|
||||
"integrity": "sha512-TcJPw+9RV9dibz1hHUzlLVy8N4X9TnwirAjrU08Juo6BNKggzVfP2ZJ/3ZUSq15Xl5i85i+Z89XBO90pB2PghQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@ -4802,6 +4804,7 @@
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"nanoid": "bin/nanoid.js"
|
||||
},
|
||||
@ -5040,9 +5043,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/pg": {
|
||||
"version": "8.13.0",
|
||||
"resolved": "https://registry.npmjs.org/pg/-/pg-8.13.0.tgz",
|
||||
"integrity": "sha512-34wkUTh3SxTClfoHB3pQ7bIMvw9dpFU1audQQeZG837fmHfHpr14n/AELVDoOYVDW2h5RDWU78tFjkD+erSBsw==",
|
||||
"version": "8.13.1",
|
||||
"resolved": "https://registry.npmjs.org/pg/-/pg-8.13.1.tgz",
|
||||
"integrity": "sha512-OUir1A0rPNZlX//c7ksiu7crsGZTKSOXJPgtNiHGIlC9H0lO+NC6ZDYksSgBYY/thSWhnSRBv8w1lieNNGATNQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -5397,14 +5400,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/raw-body": {
|
||||
"version": "2.5.2",
|
||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
|
||||
"integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz",
|
||||
"integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bytes": "3.1.2",
|
||||
"http-errors": "2.0.0",
|
||||
"iconv-lite": "0.4.24",
|
||||
"iconv-lite": "0.6.3",
|
||||
"unpipe": "1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
@ -5714,7 +5718,8 @@
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.6.2",
|
||||
@ -5815,9 +5820,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io-client": {
|
||||
"version": "4.8.0",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.0.tgz",
|
||||
"integrity": "sha512-C0jdhD5yQahMws9alf/yvtsMGTaIDBnZ8Rb5HU56svyq0l5LIrGzIDZZD5pHQlmzxLuU91Gz+VpQMKgCTNYtkw==",
|
||||
"version": "4.8.1",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz",
|
||||
"integrity": "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
@ -25,7 +25,7 @@
|
||||
"@immich/sdk": "file:../open-api/typescript-sdk",
|
||||
"@playwright/test": "^1.44.1",
|
||||
"@types/luxon": "^3.4.2",
|
||||
"@types/node": "^20.17.0",
|
||||
"@types/node": "^22.8.6",
|
||||
"@types/oidc-provider": "^8.5.1",
|
||||
"@types/pg": "^8.11.0",
|
||||
"@types/pngjs": "^6.0.4",
|
||||
@ -53,6 +53,6 @@
|
||||
"vitest": "^2.0.5"
|
||||
},
|
||||
"volta": {
|
||||
"node": "20.18.0"
|
||||
"node": "22.11.0"
|
||||
}
|
||||
}
|
||||
|
@ -1148,6 +1148,78 @@ describe('/asset', () => {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
input: 'formats/raw/Canon/PowerShot_G12.CR2',
|
||||
expected: {
|
||||
type: AssetTypeEnum.Image,
|
||||
originalFileName: 'PowerShot_G12.CR2',
|
||||
fileCreatedAt: '2015-12-27T09:55:40.000Z',
|
||||
exifInfo: {
|
||||
make: 'Canon',
|
||||
model: 'Canon PowerShot G12',
|
||||
exifImageHeight: 2736,
|
||||
exifImageWidth: 3648,
|
||||
exposureTime: '1/1000',
|
||||
fNumber: 4,
|
||||
focalLength: 18.098,
|
||||
iso: 80,
|
||||
lensModel: null,
|
||||
fileSizeInByte: 11_113_617,
|
||||
dateTimeOriginal: '2015-12-27T09:55:40.000Z',
|
||||
latitude: null,
|
||||
longitude: null,
|
||||
orientation: '1',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
input: 'formats/raw/Fujifilm/X100V_compressed.RAF',
|
||||
expected: {
|
||||
type: AssetTypeEnum.Image,
|
||||
originalFileName: 'X100V_compressed.RAF',
|
||||
fileCreatedAt: '2024-10-12T21:01:01.000Z',
|
||||
exifInfo: {
|
||||
make: 'FUJIFILM',
|
||||
model: 'X100V',
|
||||
exifImageHeight: 4160,
|
||||
exifImageWidth: 6240,
|
||||
exposureTime: '1/4000',
|
||||
fNumber: 16,
|
||||
focalLength: 23,
|
||||
iso: 160,
|
||||
lensModel: null,
|
||||
fileSizeInByte: 13_551_312,
|
||||
dateTimeOriginal: '2024-10-12T21:01:01.000Z',
|
||||
latitude: null,
|
||||
longitude: null,
|
||||
orientation: '6',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
input: 'formats/raw/Ricoh/GR3/Ricoh_GR3-450.DNG',
|
||||
expected: {
|
||||
type: AssetTypeEnum.Image,
|
||||
originalFileName: 'Ricoh_GR3-450.DNG',
|
||||
fileCreatedAt: '2024-06-08T13:48:39.000Z',
|
||||
exifInfo: {
|
||||
dateTimeOriginal: '2024-06-08T13:48:39.000Z',
|
||||
exifImageHeight: 4064,
|
||||
exifImageWidth: 6112,
|
||||
exposureTime: '1/400',
|
||||
fNumber: 5,
|
||||
fileSizeInByte: 31_175_472,
|
||||
focalLength: 18.3,
|
||||
iso: 100,
|
||||
latitude: 36.613_24,
|
||||
lensModel: 'GR LENS 18.3mm F2.8',
|
||||
longitude: -121.897_85,
|
||||
make: 'RICOH IMAGING COMPANY, LTD.',
|
||||
model: 'RICOH GR III',
|
||||
orientation: '1',
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
it(`should upload and generate a thumbnail for different file types`, async () => {
|
||||
|
Submodule e2e/test-assets updated: 3e057d2f58...99544a2004
13
i18n/en.json
13
i18n/en.json
@ -34,6 +34,11 @@
|
||||
"authentication_settings_disable_all": "Are you sure you want to disable all login methods? Login will be completely disabled.",
|
||||
"authentication_settings_reenable": "To re-enable, use a <link>Server Command</link>.",
|
||||
"background_task_job": "Background Tasks",
|
||||
"backup_database": "Backup Database",
|
||||
"backup_database_enable_description": "Enable database backups",
|
||||
"backup_keep_last_amount": "Amount of previous backups to keep",
|
||||
"backup_settings": "Backup Settings",
|
||||
"backup_settings_description": "Manage database backup settings",
|
||||
"check_all": "Check All",
|
||||
"cleared_jobs": "Cleared jobs for: {job}",
|
||||
"config_set_by_file": "Config is currently set by a config file",
|
||||
@ -43,6 +48,9 @@
|
||||
"confirm_reprocess_all_faces": "Are you sure you want to reprocess all faces? This will also clear named people.",
|
||||
"confirm_user_password_reset": "Are you sure you want to reset {user}'s password?",
|
||||
"create_job": "Create job",
|
||||
"cron_expression": "Cron expression",
|
||||
"cron_expression_description": "Set the scanning interval using the cron format. For more information please refer to e.g. <link>Crontab Guru</link>",
|
||||
"cron_expression_presets": "Cron expression presets",
|
||||
"disable_login": "Disable login",
|
||||
"duplicate_detection_job_description": "Run machine learning on assets to detect similar images. Relies on Smart Search",
|
||||
"exclusion_pattern_description": "Exclusion patterns lets you ignore files and folders when scanning your library. This is useful if you have folders that contain files you don't want to import, such as RAW files.",
|
||||
@ -80,9 +88,6 @@
|
||||
"jobs_delayed": "{jobCount, plural, other {# delayed}}",
|
||||
"jobs_failed": "{jobCount, plural, other {# failed}}",
|
||||
"library_created": "Created library: {library}",
|
||||
"library_cron_expression": "Cron expression",
|
||||
"library_cron_expression_description": "Set the scanning interval using the cron format. For more information please refer to e.g. <link>Crontab Guru</link>",
|
||||
"library_cron_expression_presets": "Cron expression presets",
|
||||
"library_deleted": "Library deleted",
|
||||
"library_import_path_description": "Specify a folder to import. This folder, including subfolders, will be scanned for images and videos.",
|
||||
"library_scanning": "Periodic Scanning",
|
||||
@ -309,8 +314,6 @@
|
||||
"transcoding_threads_description": "Higher values lead to faster encoding, but leave less room for the server to process other tasks while active. This value should not be more than the number of CPU cores. Maximizes utilization if set to 0.",
|
||||
"transcoding_tone_mapping": "Tone-mapping",
|
||||
"transcoding_tone_mapping_description": "Attempts to preserve the appearance of HDR videos when converted to SDR. Each algorithm makes different tradeoffs for color, detail and brightness. Hable preserves detail, Mobius preserves color, and Reinhard preserves brightness.",
|
||||
"transcoding_tone_mapping_npl": "Tone-mapping NPL",
|
||||
"transcoding_tone_mapping_npl_description": "Colors will be adjusted to look normal for a display of this brightness. Counter-intuitively, lower values increase the brightness of the video and vice versa since it compensates for the brightness of the display. 0 sets this value automatically.",
|
||||
"transcoding_transcode_policy": "Transcode policy",
|
||||
"transcoding_transcode_policy_description": "Policy for when a video should be transcoded. HDR videos will always be transcoded (except if transcoding is disabled).",
|
||||
"transcoding_two_pass_encoding": "Two-pass encoding",
|
||||
|
300
machine-learning/poetry.lock
generated
300
machine-learning/poetry.lock
generated
@ -748,18 +748,18 @@ test = ["pytest (>=6)"]
|
||||
|
||||
[[package]]
|
||||
name = "fastapi-slim"
|
||||
version = "0.115.2"
|
||||
version = "0.115.4"
|
||||
description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "fastapi_slim-0.115.2-py3-none-any.whl", hash = "sha256:7dd65a58e9b2a1558ebd479d35e621417d4572188d902a3e5564739914ddc81d"},
|
||||
{file = "fastapi_slim-0.115.2.tar.gz", hash = "sha256:83a1ce1e385029cad305b49dcc03622d2be6a905b2ab65d2e7096285605cca8d"},
|
||||
{file = "fastapi_slim-0.115.4-py3-none-any.whl", hash = "sha256:8947515618c21665590a1673a0bfe4c721db4267999c149d5301c3c0f7b3d9ce"},
|
||||
{file = "fastapi_slim-0.115.4.tar.gz", hash = "sha256:6d37987e4d1f6adefb8c7119c9b804e59c9b3f1a488be5425994d52308e2f958"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0"
|
||||
starlette = ">=0.37.2,<0.41.0"
|
||||
starlette = ">=0.40.0,<0.42.0"
|
||||
typing-extensions = ">=4.8.0"
|
||||
|
||||
[package.extras]
|
||||
@ -946,13 +946,13 @@ tqdm = ["tqdm"]
|
||||
|
||||
[[package]]
|
||||
name = "ftfy"
|
||||
version = "6.3.0"
|
||||
version = "6.3.1"
|
||||
description = "Fixes mojibake and other problems with Unicode, after the fact"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "ftfy-6.3.0-py3-none-any.whl", hash = "sha256:17aca296801f44142e3ff2c16f93fbf6a87609ebb3704a9a41dd5d4903396caf"},
|
||||
{file = "ftfy-6.3.0.tar.gz", hash = "sha256:1c7d6418e72b25a7760feb150acf574b86924dbb2e95b32c0b3abbd1ba3d7ad6"},
|
||||
{file = "ftfy-6.3.1-py3-none-any.whl", hash = "sha256:7c70eb532015cd2f9adb53f101fb6c7945988d023a085d127d1573dc49dd0083"},
|
||||
{file = "ftfy-6.3.1.tar.gz", hash = "sha256:9b3c3d90f84fb267fe64d375a07b7f8912d817cf86009ae134aa03e1819506ec"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -1315,13 +1315,13 @@ zstd = ["zstandard (>=0.18.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "huggingface-hub"
|
||||
version = "0.25.2"
|
||||
version = "0.26.2"
|
||||
description = "Client library to download and publish models, datasets and other repos on the huggingface.co hub"
|
||||
optional = false
|
||||
python-versions = ">=3.8.0"
|
||||
files = [
|
||||
{file = "huggingface_hub-0.25.2-py3-none-any.whl", hash = "sha256:1897caf88ce7f97fe0110603d8f66ac264e3ba6accdf30cd66cc0fed5282ad25"},
|
||||
{file = "huggingface_hub-0.25.2.tar.gz", hash = "sha256:a1014ea111a5f40ccd23f7f7ba8ac46e20fa3b658ced1f86a00c75c06ec6423c"},
|
||||
{file = "huggingface_hub-0.26.2-py3-none-any.whl", hash = "sha256:98c2a5a8e786c7b2cb6fdeb2740893cba4d53e312572ed3d8afafda65b128c46"},
|
||||
{file = "huggingface_hub-0.26.2.tar.gz", hash = "sha256:b100d853465d965733964d123939ba287da60a547087783ddff8a323f340332b"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -1334,16 +1334,16 @@ tqdm = ">=4.42.1"
|
||||
typing-extensions = ">=3.7.4.3"
|
||||
|
||||
[package.extras]
|
||||
all = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio", "jedi", "minijinja (>=1.0)", "mypy (==1.5.1)", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.5.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"]
|
||||
all = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio (>=4.0.0)", "jedi", "libcst (==1.4.0)", "mypy (==1.5.1)", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.5.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"]
|
||||
cli = ["InquirerPy (==0.3.4)"]
|
||||
dev = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio", "jedi", "minijinja (>=1.0)", "mypy (==1.5.1)", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.5.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"]
|
||||
dev = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio (>=4.0.0)", "jedi", "libcst (==1.4.0)", "mypy (==1.5.1)", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.5.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"]
|
||||
fastai = ["fastai (>=2.4)", "fastcore (>=1.3.27)", "toml"]
|
||||
hf-transfer = ["hf-transfer (>=0.1.4)"]
|
||||
inference = ["aiohttp", "minijinja (>=1.0)"]
|
||||
quality = ["mypy (==1.5.1)", "ruff (>=0.5.0)"]
|
||||
inference = ["aiohttp"]
|
||||
quality = ["libcst (==1.4.0)", "mypy (==1.5.1)", "ruff (>=0.5.0)"]
|
||||
tensorflow = ["graphviz", "pydot", "tensorflow"]
|
||||
tensorflow-testing = ["keras (<3.0)", "tensorflow"]
|
||||
testing = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio", "jedi", "minijinja (>=1.0)", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "soundfile", "urllib3 (<2.0)"]
|
||||
testing = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio (>=4.0.0)", "jedi", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "soundfile", "urllib3 (<2.0)"]
|
||||
torch = ["safetensors[torch]", "torch"]
|
||||
typing = ["types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)"]
|
||||
|
||||
@ -1609,13 +1609,13 @@ test = ["pytest (>=7.4)", "pytest-cov (>=4.1)"]
|
||||
|
||||
[[package]]
|
||||
name = "locust"
|
||||
version = "2.32.0"
|
||||
version = "2.32.1"
|
||||
description = "Developer-friendly load testing framework"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "locust-2.32.0-py3-none-any.whl", hash = "sha256:e004514332b8631ca91382d11d224baee4ced040c5f5c8b2233800ebcbc73c0e"},
|
||||
{file = "locust-2.32.0.tar.gz", hash = "sha256:d8f7f5d9d4e801b2e7b0ee3f31109333673da744ccedf85e7da0151f2d263dd9"},
|
||||
{file = "locust-2.32.1-py3-none-any.whl", hash = "sha256:3fb5548b4f2b6477fa5229ee55ac3dddbae56e86c3430bf2ba3fee358eb7e7bb"},
|
||||
{file = "locust-2.32.1.tar.gz", hash = "sha256:8c3b1094dbf20860fd2f6e26b68f0c6064dc28054f4462664389d102fce1448b"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -1876,43 +1876,43 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "mypy"
|
||||
version = "1.12.1"
|
||||
version = "1.13.0"
|
||||
description = "Optional static typing for Python"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "mypy-1.12.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3d7d4371829184e22fda4015278fbfdef0327a4b955a483012bd2d423a788801"},
|
||||
{file = "mypy-1.12.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f59f1dfbf497d473201356966e353ef09d4daec48caeacc0254db8ef633a28a5"},
|
||||
{file = "mypy-1.12.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b947097fae68004b8328c55161ac9db7d3566abfef72d9d41b47a021c2fba6b1"},
|
||||
{file = "mypy-1.12.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:96af62050971c5241afb4701c15189ea9507db89ad07794a4ee7b4e092dc0627"},
|
||||
{file = "mypy-1.12.1-cp310-cp310-win_amd64.whl", hash = "sha256:d90da248f4c2dba6c44ddcfea94bb361e491962f05f41990ff24dbd09969ce20"},
|
||||
{file = "mypy-1.12.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1230048fec1380faf240be6385e709c8570604d2d27ec6ca7e573e3bc09c3735"},
|
||||
{file = "mypy-1.12.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:02dcfe270c6ea13338210908f8cadc8d31af0f04cee8ca996438fe6a97b4ec66"},
|
||||
{file = "mypy-1.12.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a5a437c9102a6a252d9e3a63edc191a3aed5f2fcb786d614722ee3f4472e33f6"},
|
||||
{file = "mypy-1.12.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:186e0c8346efc027ee1f9acf5ca734425fc4f7dc2b60144f0fbe27cc19dc7931"},
|
||||
{file = "mypy-1.12.1-cp311-cp311-win_amd64.whl", hash = "sha256:673ba1140a478b50e6d265c03391702fa11a5c5aff3f54d69a62a48da32cb811"},
|
||||
{file = "mypy-1.12.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9fb83a7be97c498176fb7486cafbb81decccaef1ac339d837c377b0ce3743a7f"},
|
||||
{file = "mypy-1.12.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:389e307e333879c571029d5b93932cf838b811d3f5395ed1ad05086b52148fb0"},
|
||||
{file = "mypy-1.12.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:94b2048a95a21f7a9ebc9fbd075a4fcd310410d078aa0228dbbad7f71335e042"},
|
||||
{file = "mypy-1.12.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ee5932370ccf7ebf83f79d1c157a5929d7ea36313027b0d70a488493dc1b179"},
|
||||
{file = "mypy-1.12.1-cp312-cp312-win_amd64.whl", hash = "sha256:19bf51f87a295e7ab2894f1d8167622b063492d754e69c3c2fed6563268cb42a"},
|
||||
{file = "mypy-1.12.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d34167d43613ffb1d6c6cdc0cc043bb106cac0aa5d6a4171f77ab92a3c758bcc"},
|
||||
{file = "mypy-1.12.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:427878aa54f2e2c5d8db31fa9010c599ed9f994b3b49e64ae9cd9990c40bd635"},
|
||||
{file = "mypy-1.12.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5fcde63ea2c9f69d6be859a1e6dd35955e87fa81de95bc240143cf00de1f7f81"},
|
||||
{file = "mypy-1.12.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:d54d840f6c052929f4a3d2aab2066af0f45a020b085fe0e40d4583db52aab4e4"},
|
||||
{file = "mypy-1.12.1-cp313-cp313-win_amd64.whl", hash = "sha256:20db6eb1ca3d1de8ece00033b12f793f1ea9da767334b7e8c626a4872090cf02"},
|
||||
{file = "mypy-1.12.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b16fe09f9c741d85a2e3b14a5257a27a4f4886c171d562bc5a5e90d8591906b8"},
|
||||
{file = "mypy-1.12.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0dcc1e843d58f444fce19da4cce5bd35c282d4bde232acdeca8279523087088a"},
|
||||
{file = "mypy-1.12.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e10ba7de5c616e44ad21005fa13450cd0de7caaa303a626147d45307492e4f2d"},
|
||||
{file = "mypy-1.12.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0e6fe449223fa59fbee351db32283838a8fee8059e0028e9e6494a03802b4004"},
|
||||
{file = "mypy-1.12.1-cp38-cp38-win_amd64.whl", hash = "sha256:dc6e2a2195a290a7fd5bac3e60b586d77fc88e986eba7feced8b778c373f9afe"},
|
||||
{file = "mypy-1.12.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:de5b2a8988b4e1269a98beaf0e7cc71b510d050dce80c343b53b4955fff45f19"},
|
||||
{file = "mypy-1.12.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:843826966f1d65925e8b50d2b483065c51fc16dc5d72647e0236aae51dc8d77e"},
|
||||
{file = "mypy-1.12.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9fe20f89da41a95e14c34b1ddb09c80262edcc295ad891f22cc4b60013e8f78d"},
|
||||
{file = "mypy-1.12.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8135ffec02121a75f75dc97c81af7c14aa4ae0dda277132cfcd6abcd21551bfd"},
|
||||
{file = "mypy-1.12.1-cp39-cp39-win_amd64.whl", hash = "sha256:a7b76fa83260824300cc4834a3ab93180db19876bce59af921467fd03e692810"},
|
||||
{file = "mypy-1.12.1-py3-none-any.whl", hash = "sha256:ce561a09e3bb9863ab77edf29ae3a50e65685ad74bba1431278185b7e5d5486e"},
|
||||
{file = "mypy-1.12.1.tar.gz", hash = "sha256:f5b3936f7a6d0e8280c9bdef94c7ce4847f5cdfc258fbb2c29a8c1711e8bb96d"},
|
||||
{file = "mypy-1.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6607e0f1dd1fb7f0aca14d936d13fd19eba5e17e1cd2a14f808fa5f8f6d8f60a"},
|
||||
{file = "mypy-1.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8a21be69bd26fa81b1f80a61ee7ab05b076c674d9b18fb56239d72e21d9f4c80"},
|
||||
{file = "mypy-1.13.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b2353a44d2179846a096e25691d54d59904559f4232519d420d64da6828a3a7"},
|
||||
{file = "mypy-1.13.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0730d1c6a2739d4511dc4253f8274cdd140c55c32dfb0a4cf8b7a43f40abfa6f"},
|
||||
{file = "mypy-1.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:c5fc54dbb712ff5e5a0fca797e6e0aa25726c7e72c6a5850cfd2adbc1eb0a372"},
|
||||
{file = "mypy-1.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:581665e6f3a8a9078f28d5502f4c334c0c8d802ef55ea0e7276a6e409bc0d82d"},
|
||||
{file = "mypy-1.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3ddb5b9bf82e05cc9a627e84707b528e5c7caaa1c55c69e175abb15a761cec2d"},
|
||||
{file = "mypy-1.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:20c7ee0bc0d5a9595c46f38beb04201f2620065a93755704e141fcac9f59db2b"},
|
||||
{file = "mypy-1.13.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3790ded76f0b34bc9c8ba4def8f919dd6a46db0f5a6610fb994fe8efdd447f73"},
|
||||
{file = "mypy-1.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:51f869f4b6b538229c1d1bcc1dd7d119817206e2bc54e8e374b3dfa202defcca"},
|
||||
{file = "mypy-1.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5c7051a3461ae84dfb5dd15eff5094640c61c5f22257c8b766794e6dd85e72d5"},
|
||||
{file = "mypy-1.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:39bb21c69a5d6342f4ce526e4584bc5c197fd20a60d14a8624d8743fffb9472e"},
|
||||
{file = "mypy-1.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:164f28cb9d6367439031f4c81e84d3ccaa1e19232d9d05d37cb0bd880d3f93c2"},
|
||||
{file = "mypy-1.13.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a4c1bfcdbce96ff5d96fc9b08e3831acb30dc44ab02671eca5953eadad07d6d0"},
|
||||
{file = "mypy-1.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:a0affb3a79a256b4183ba09811e3577c5163ed06685e4d4b46429a271ba174d2"},
|
||||
{file = "mypy-1.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a7b44178c9760ce1a43f544e595d35ed61ac2c3de306599fa59b38a6048e1aa7"},
|
||||
{file = "mypy-1.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5d5092efb8516d08440e36626f0153b5006d4088c1d663d88bf79625af3d1d62"},
|
||||
{file = "mypy-1.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2904956dac40ced10931ac967ae63c5089bd498542194b436eb097a9f77bc8"},
|
||||
{file = "mypy-1.13.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:7bfd8836970d33c2105562650656b6846149374dc8ed77d98424b40b09340ba7"},
|
||||
{file = "mypy-1.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:9f73dba9ec77acb86457a8fc04b5239822df0c14a082564737833d2963677dbc"},
|
||||
{file = "mypy-1.13.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:100fac22ce82925f676a734af0db922ecfea991e1d7ec0ceb1e115ebe501301a"},
|
||||
{file = "mypy-1.13.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7bcb0bb7f42a978bb323a7c88f1081d1b5dee77ca86f4100735a6f541299d8fb"},
|
||||
{file = "mypy-1.13.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bde31fc887c213e223bbfc34328070996061b0833b0a4cfec53745ed61f3519b"},
|
||||
{file = "mypy-1.13.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:07de989f89786f62b937851295ed62e51774722e5444a27cecca993fc3f9cd74"},
|
||||
{file = "mypy-1.13.0-cp38-cp38-win_amd64.whl", hash = "sha256:4bde84334fbe19bad704b3f5b78c4abd35ff1026f8ba72b29de70dda0916beb6"},
|
||||
{file = "mypy-1.13.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0246bcb1b5de7f08f2826451abd947bf656945209b140d16ed317f65a17dc7dc"},
|
||||
{file = "mypy-1.13.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7f5b7deae912cf8b77e990b9280f170381fdfbddf61b4ef80927edd813163732"},
|
||||
{file = "mypy-1.13.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7029881ec6ffb8bc233a4fa364736789582c738217b133f1b55967115288a2bc"},
|
||||
{file = "mypy-1.13.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3e38b980e5681f28f033f3be86b099a247b13c491f14bb8b1e1e134d23bb599d"},
|
||||
{file = "mypy-1.13.0-cp39-cp39-win_amd64.whl", hash = "sha256:a6789be98a2017c912ae6ccb77ea553bbaf13d27605d2ca20a76dfbced631b24"},
|
||||
{file = "mypy-1.13.0-py3-none-any.whl", hash = "sha256:9c250883f9fd81d212e0952c92dbfcc96fc237f4b7c92f56ac81fd48460b3e5a"},
|
||||
{file = "mypy-1.13.0.tar.gz", hash = "sha256:0291a61b6fbf3e6673e3405cfcc0e7650bebc7939659fdca2702958038bd835e"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -1922,6 +1922,7 @@ typing-extensions = ">=4.6.0"
|
||||
|
||||
[package.extras]
|
||||
dmypy = ["psutil (>=4.0)"]
|
||||
faster-cache = ["orjson"]
|
||||
install-types = ["pip"]
|
||||
mypyc = ["setuptools (>=50)"]
|
||||
reports = ["lxml"]
|
||||
@ -2091,27 +2092,27 @@ sympy = "*"
|
||||
|
||||
[[package]]
|
||||
name = "onnxruntime-gpu"
|
||||
version = "1.18.1"
|
||||
version = "1.19.2"
|
||||
description = "ONNX Runtime is a runtime accelerator for Machine Learning models"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "onnxruntime_gpu-1.18.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4e9a52f5d43a84fe29e135da6bf10daa18836c81bed9060a5924efd6afc0d259"},
|
||||
{file = "onnxruntime_gpu-1.18.1-cp310-cp310-win_amd64.whl", hash = "sha256:e7c1c665e8a11a5cf15369948b04288dc0a6812ad2e6beaff93a3d157c864d9a"},
|
||||
{file = "onnxruntime_gpu-1.18.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1334f802cb1e4e2eb6ceebc4ef71ba44f3ef444d34216baafb940368a7a5d2f5"},
|
||||
{file = "onnxruntime_gpu-1.18.1-cp311-cp311-win_amd64.whl", hash = "sha256:0ffcc711e89b80c935d5172544f8a605b11525fc1e6f0e78ee79e2c28956e2d9"},
|
||||
{file = "onnxruntime_gpu-1.18.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bbb1a6c986b2392eebaebc43e198a1614e3f7d2c191725002dbfa0dceb24454b"},
|
||||
{file = "onnxruntime_gpu-1.18.1-cp312-cp312-win_amd64.whl", hash = "sha256:bee352929e6eec2ff4e11e323a025ed8bd5eac24795005bc502ac740971fa7bd"},
|
||||
{file = "onnxruntime_gpu-1.18.1-cp38-cp38-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:76d307a849a863d0457869febe4b2fd2fc07c7f26385c7339d17066312fa6be0"},
|
||||
{file = "onnxruntime_gpu-1.18.1-cp38-cp38-win_amd64.whl", hash = "sha256:b7498d6c64a03558308ce6d7d14dab306ea90d1204b563890c4d2d26c1b520f0"},
|
||||
{file = "onnxruntime_gpu-1.18.1-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8a1d8113cb4b8a51b195fae91cfeb6849728462a4b46aaf51b6764c44e54f81f"},
|
||||
{file = "onnxruntime_gpu-1.18.1-cp39-cp39-win_amd64.whl", hash = "sha256:fc1d2544a39f5db64c5b8a0c24d0b934d7d64682e6d70763eb2cc726b1fd6c3f"},
|
||||
{file = "onnxruntime_gpu-1.19.2-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a49740e079e7c5215830d30cde3df792e903df007aa0b0fd7aa797937061b27a"},
|
||||
{file = "onnxruntime_gpu-1.19.2-cp310-cp310-win_amd64.whl", hash = "sha256:b895920bb5e4241299f68874e0becdc2635ea0142939c11e7ff5ae5b28993613"},
|
||||
{file = "onnxruntime_gpu-1.19.2-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:562fc7c755393eaad9751e56149339dd201ffbfdb3ef5f43ff21d0619ba9045f"},
|
||||
{file = "onnxruntime_gpu-1.19.2-cp311-cp311-win_amd64.whl", hash = "sha256:522f7495918176cb8c1a3c78bde7152d984f7096acc786c73a27643af8af87c9"},
|
||||
{file = "onnxruntime_gpu-1.19.2-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:554a02a3fac0119707eb87327908afd21c4e6f0fa5bf9a034398f098adc316c5"},
|
||||
{file = "onnxruntime_gpu-1.19.2-cp312-cp312-win_amd64.whl", hash = "sha256:e7c6165a405027e3c0f11d189ae7013b5d66919b3381f9bfb3405c0c0cf07968"},
|
||||
{file = "onnxruntime_gpu-1.19.2-cp38-cp38-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b4a8562e1e6f1912870c60bfaf8233c82b86e5b93ae39f211b650ac0f2015430"},
|
||||
{file = "onnxruntime_gpu-1.19.2-cp38-cp38-win_amd64.whl", hash = "sha256:55505c99e18688a7c68fdc811ed6e7a315aa36f543b33920c77d03a627d2c3f5"},
|
||||
{file = "onnxruntime_gpu-1.19.2-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9e369f01f55ea726ae5d28f18236426e52e97c433f0b7682054e61c478a06c9"},
|
||||
{file = "onnxruntime_gpu-1.19.2-cp39-cp39-win_amd64.whl", hash = "sha256:c8b8128174b0470537e9f4983aeecc002a435d13914970c2af2f41d244ef2781"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
coloredlogs = "*"
|
||||
flatbuffers = "*"
|
||||
numpy = ">=1.21.6,<2.0"
|
||||
numpy = ">=1.21.6"
|
||||
packaging = "*"
|
||||
protobuf = "*"
|
||||
sympy = "*"
|
||||
@ -2169,68 +2170,69 @@ numpy = [
|
||||
|
||||
[[package]]
|
||||
name = "orjson"
|
||||
version = "3.10.7"
|
||||
version = "3.10.10"
|
||||
description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "orjson-3.10.7-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:74f4544f5a6405b90da8ea724d15ac9c36da4d72a738c64685003337401f5c12"},
|
||||
{file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34a566f22c28222b08875b18b0dfbf8a947e69df21a9ed5c51a6bf91cfb944ac"},
|
||||
{file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bf6ba8ebc8ef5792e2337fb0419f8009729335bb400ece005606336b7fd7bab7"},
|
||||
{file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac7cf6222b29fbda9e3a472b41e6a5538b48f2c8f99261eecd60aafbdb60690c"},
|
||||
{file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:de817e2f5fc75a9e7dd350c4b0f54617b280e26d1631811a43e7e968fa71e3e9"},
|
||||
{file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:348bdd16b32556cf8d7257b17cf2bdb7ab7976af4af41ebe79f9796c218f7e91"},
|
||||
{file = "orjson-3.10.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:479fd0844ddc3ca77e0fd99644c7fe2de8e8be1efcd57705b5c92e5186e8a250"},
|
||||
{file = "orjson-3.10.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:fdf5197a21dd660cf19dfd2a3ce79574588f8f5e2dbf21bda9ee2d2b46924d84"},
|
||||
{file = "orjson-3.10.7-cp310-none-win32.whl", hash = "sha256:d374d36726746c81a49f3ff8daa2898dccab6596864ebe43d50733275c629175"},
|
||||
{file = "orjson-3.10.7-cp310-none-win_amd64.whl", hash = "sha256:cb61938aec8b0ffb6eef484d480188a1777e67b05d58e41b435c74b9d84e0b9c"},
|
||||
{file = "orjson-3.10.7-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:7db8539039698ddfb9a524b4dd19508256107568cdad24f3682d5773e60504a2"},
|
||||
{file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:480f455222cb7a1dea35c57a67578848537d2602b46c464472c995297117fa09"},
|
||||
{file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8a9c9b168b3a19e37fe2778c0003359f07822c90fdff8f98d9d2a91b3144d8e0"},
|
||||
{file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8de062de550f63185e4c1c54151bdddfc5625e37daf0aa1e75d2a1293e3b7d9a"},
|
||||
{file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6b0dd04483499d1de9c8f6203f8975caf17a6000b9c0c54630cef02e44ee624e"},
|
||||
{file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b58d3795dafa334fc8fd46f7c5dc013e6ad06fd5b9a4cc98cb1456e7d3558bd6"},
|
||||
{file = "orjson-3.10.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:33cfb96c24034a878d83d1a9415799a73dc77480e6c40417e5dda0710d559ee6"},
|
||||
{file = "orjson-3.10.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e724cebe1fadc2b23c6f7415bad5ee6239e00a69f30ee423f319c6af70e2a5c0"},
|
||||
{file = "orjson-3.10.7-cp311-none-win32.whl", hash = "sha256:82763b46053727a7168d29c772ed5c870fdae2f61aa8a25994c7984a19b1021f"},
|
||||
{file = "orjson-3.10.7-cp311-none-win_amd64.whl", hash = "sha256:eb8d384a24778abf29afb8e41d68fdd9a156cf6e5390c04cc07bbc24b89e98b5"},
|
||||
{file = "orjson-3.10.7-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:44a96f2d4c3af51bfac6bc4ef7b182aa33f2f054fd7f34cc0ee9a320d051d41f"},
|
||||
{file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76ac14cd57df0572453543f8f2575e2d01ae9e790c21f57627803f5e79b0d3c3"},
|
||||
{file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bdbb61dcc365dd9be94e8f7df91975edc9364d6a78c8f7adb69c1cdff318ec93"},
|
||||
{file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b48b3db6bb6e0a08fa8c83b47bc169623f801e5cc4f24442ab2b6617da3b5313"},
|
||||
{file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:23820a1563a1d386414fef15c249040042b8e5d07b40ab3fe3efbfbbcbcb8864"},
|
||||
{file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0c6a008e91d10a2564edbb6ee5069a9e66df3fbe11c9a005cb411f441fd2c09"},
|
||||
{file = "orjson-3.10.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d352ee8ac1926d6193f602cbe36b1643bbd1bbcb25e3c1a657a4390f3000c9a5"},
|
||||
{file = "orjson-3.10.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d2d9f990623f15c0ae7ac608103c33dfe1486d2ed974ac3f40b693bad1a22a7b"},
|
||||
{file = "orjson-3.10.7-cp312-none-win32.whl", hash = "sha256:7c4c17f8157bd520cdb7195f75ddbd31671997cbe10aee559c2d613592e7d7eb"},
|
||||
{file = "orjson-3.10.7-cp312-none-win_amd64.whl", hash = "sha256:1d9c0e733e02ada3ed6098a10a8ee0052dd55774de3d9110d29868d24b17faa1"},
|
||||
{file = "orjson-3.10.7-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:77d325ed866876c0fa6492598ec01fe30e803272a6e8b10e992288b009cbe149"},
|
||||
{file = "orjson-3.10.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ea2c232deedcb605e853ae1db2cc94f7390ac776743b699b50b071b02bea6fe"},
|
||||
{file = "orjson-3.10.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3dcfbede6737fdbef3ce9c37af3fb6142e8e1ebc10336daa05872bfb1d87839c"},
|
||||
{file = "orjson-3.10.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:11748c135f281203f4ee695b7f80bb1358a82a63905f9f0b794769483ea854ad"},
|
||||
{file = "orjson-3.10.7-cp313-none-win32.whl", hash = "sha256:a7e19150d215c7a13f39eb787d84db274298d3f83d85463e61d277bbd7f401d2"},
|
||||
{file = "orjson-3.10.7-cp313-none-win_amd64.whl", hash = "sha256:eef44224729e9525d5261cc8d28d6b11cafc90e6bd0be2157bde69a52ec83024"},
|
||||
{file = "orjson-3.10.7-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:6ea2b2258eff652c82652d5e0f02bd5e0463a6a52abb78e49ac288827aaa1469"},
|
||||
{file = "orjson-3.10.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:430ee4d85841e1483d487e7b81401785a5dfd69db5de01314538f31f8fbf7ee1"},
|
||||
{file = "orjson-3.10.7-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4b6146e439af4c2472c56f8540d799a67a81226e11992008cb47e1267a9b3225"},
|
||||
{file = "orjson-3.10.7-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:084e537806b458911137f76097e53ce7bf5806dda33ddf6aaa66a028f8d43a23"},
|
||||
{file = "orjson-3.10.7-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4829cf2195838e3f93b70fd3b4292156fc5e097aac3739859ac0dcc722b27ac0"},
|
||||
{file = "orjson-3.10.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1193b2416cbad1a769f868b1749535d5da47626ac29445803dae7cc64b3f5c98"},
|
||||
{file = "orjson-3.10.7-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:4e6c3da13e5a57e4b3dca2de059f243ebec705857522f188f0180ae88badd354"},
|
||||
{file = "orjson-3.10.7-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:c31008598424dfbe52ce8c5b47e0752dca918a4fdc4a2a32004efd9fab41d866"},
|
||||
{file = "orjson-3.10.7-cp38-none-win32.whl", hash = "sha256:7122a99831f9e7fe977dc45784d3b2edc821c172d545e6420c375e5a935f5a1c"},
|
||||
{file = "orjson-3.10.7-cp38-none-win_amd64.whl", hash = "sha256:a763bc0e58504cc803739e7df040685816145a6f3c8a589787084b54ebc9f16e"},
|
||||
{file = "orjson-3.10.7-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:e76be12658a6fa376fcd331b1ea4e58f5a06fd0220653450f0d415b8fd0fbe20"},
|
||||
{file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed350d6978d28b92939bfeb1a0570c523f6170efc3f0a0ef1f1df287cd4f4960"},
|
||||
{file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:144888c76f8520e39bfa121b31fd637e18d4cc2f115727865fdf9fa325b10412"},
|
||||
{file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09b2d92fd95ad2402188cf51573acde57eb269eddabaa60f69ea0d733e789fe9"},
|
||||
{file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5b24a579123fa884f3a3caadaed7b75eb5715ee2b17ab5c66ac97d29b18fe57f"},
|
||||
{file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591bcfe7512353bd609875ab38050efe3d55e18934e2f18950c108334b4ff"},
|
||||
{file = "orjson-3.10.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f4db56635b58cd1a200b0a23744ff44206ee6aa428185e2b6c4a65b3197abdcd"},
|
||||
{file = "orjson-3.10.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0fa5886854673222618638c6df7718ea7fe2f3f2384c452c9ccedc70b4a510a5"},
|
||||
{file = "orjson-3.10.7-cp39-none-win32.whl", hash = "sha256:8272527d08450ab16eb405f47e0f4ef0e5ff5981c3d82afe0efd25dcbef2bcd2"},
|
||||
{file = "orjson-3.10.7-cp39-none-win_amd64.whl", hash = "sha256:974683d4618c0c7dbf4f69c95a979734bf183d0658611760017f6e70a145af58"},
|
||||
{file = "orjson-3.10.7.tar.gz", hash = "sha256:75ef0640403f945f3a1f9f6400686560dbfb0fb5b16589ad62cd477043c4eee3"},
|
||||
{file = "orjson-3.10.10-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:b788a579b113acf1c57e0a68e558be71d5d09aa67f62ca1f68e01117e550a998"},
|
||||
{file = "orjson-3.10.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:804b18e2b88022c8905bb79bd2cbe59c0cd014b9328f43da8d3b28441995cda4"},
|
||||
{file = "orjson-3.10.10-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9972572a1d042ec9ee421b6da69f7cc823da5962237563fa548ab17f152f0b9b"},
|
||||
{file = "orjson-3.10.10-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc6993ab1c2ae7dd0711161e303f1db69062955ac2668181bfdf2dd410e65258"},
|
||||
{file = "orjson-3.10.10-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d78e4cacced5781b01d9bc0f0cd8b70b906a0e109825cb41c1b03f9c41e4ce86"},
|
||||
{file = "orjson-3.10.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e6eb2598df518281ba0cbc30d24c5b06124ccf7e19169e883c14e0831217a0bc"},
|
||||
{file = "orjson-3.10.10-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:23776265c5215ec532de6238a52707048401a568f0fa0d938008e92a147fe2c7"},
|
||||
{file = "orjson-3.10.10-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8cc2a654c08755cef90b468ff17c102e2def0edd62898b2486767204a7f5cc9c"},
|
||||
{file = "orjson-3.10.10-cp310-none-win32.whl", hash = "sha256:081b3fc6a86d72efeb67c13d0ea7c030017bd95f9868b1e329a376edc456153b"},
|
||||
{file = "orjson-3.10.10-cp310-none-win_amd64.whl", hash = "sha256:ff38c5fb749347768a603be1fb8a31856458af839f31f064c5aa74aca5be9efe"},
|
||||
{file = "orjson-3.10.10-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:879e99486c0fbb256266c7c6a67ff84f46035e4f8749ac6317cc83dacd7f993a"},
|
||||
{file = "orjson-3.10.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:019481fa9ea5ff13b5d5d95e6fd5ab25ded0810c80b150c2c7b1cc8660b662a7"},
|
||||
{file = "orjson-3.10.10-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0dd57eff09894938b4c86d4b871a479260f9e156fa7f12f8cad4b39ea8028bb5"},
|
||||
{file = "orjson-3.10.10-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dbde6d70cd95ab4d11ea8ac5e738e30764e510fc54d777336eec09bb93b8576c"},
|
||||
{file = "orjson-3.10.10-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b2625cb37b8fb42e2147404e5ff7ef08712099197a9cd38895006d7053e69d6"},
|
||||
{file = "orjson-3.10.10-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dbf3c20c6a7db69df58672a0d5815647ecf78c8e62a4d9bd284e8621c1fe5ccb"},
|
||||
{file = "orjson-3.10.10-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:75c38f5647e02d423807d252ce4528bf6a95bd776af999cb1fb48867ed01d1f6"},
|
||||
{file = "orjson-3.10.10-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:23458d31fa50ec18e0ec4b0b4343730928296b11111df5f547c75913714116b2"},
|
||||
{file = "orjson-3.10.10-cp311-none-win32.whl", hash = "sha256:2787cd9dedc591c989f3facd7e3e86508eafdc9536a26ec277699c0aa63c685b"},
|
||||
{file = "orjson-3.10.10-cp311-none-win_amd64.whl", hash = "sha256:6514449d2c202a75183f807bc755167713297c69f1db57a89a1ef4a0170ee269"},
|
||||
{file = "orjson-3.10.10-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:8564f48f3620861f5ef1e080ce7cd122ee89d7d6dacf25fcae675ff63b4d6e05"},
|
||||
{file = "orjson-3.10.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5bf161a32b479034098c5b81f2608f09167ad2fa1c06abd4e527ea6bf4837a9"},
|
||||
{file = "orjson-3.10.10-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:68b65c93617bcafa7f04b74ae8bc2cc214bd5cb45168a953256ff83015c6747d"},
|
||||
{file = "orjson-3.10.10-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e8e28406f97fc2ea0c6150f4c1b6e8261453318930b334abc419214c82314f85"},
|
||||
{file = "orjson-3.10.10-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e4d0d9fe174cc7a5bdce2e6c378bcdb4c49b2bf522a8f996aa586020e1b96cee"},
|
||||
{file = "orjson-3.10.10-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3be81c42f1242cbed03cbb3973501fcaa2675a0af638f8be494eaf37143d999"},
|
||||
{file = "orjson-3.10.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:65f9886d3bae65be026219c0a5f32dbbe91a9e6272f56d092ab22561ad0ea33b"},
|
||||
{file = "orjson-3.10.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:730ed5350147db7beb23ddaf072f490329e90a1d059711d364b49fe352ec987b"},
|
||||
{file = "orjson-3.10.10-cp312-none-win32.whl", hash = "sha256:a8f4bf5f1c85bea2170800020d53a8877812892697f9c2de73d576c9307a8a5f"},
|
||||
{file = "orjson-3.10.10-cp312-none-win_amd64.whl", hash = "sha256:384cd13579a1b4cd689d218e329f459eb9ddc504fa48c5a83ef4889db7fd7a4f"},
|
||||
{file = "orjson-3.10.10-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:44bffae68c291f94ff5a9b4149fe9d1bdd4cd0ff0fb575bcea8351d48db629a1"},
|
||||
{file = "orjson-3.10.10-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e27b4c6437315df3024f0835887127dac2a0a3ff643500ec27088d2588fa5ae1"},
|
||||
{file = "orjson-3.10.10-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bca84df16d6b49325a4084fd8b2fe2229cb415e15c46c529f868c3387bb1339d"},
|
||||
{file = "orjson-3.10.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c14ce70e8f39bd71f9f80423801b5d10bf93d1dceffdecd04df0f64d2c69bc01"},
|
||||
{file = "orjson-3.10.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:24ac62336da9bda1bd93c0491eff0613003b48d3cb5d01470842e7b52a40d5b4"},
|
||||
{file = "orjson-3.10.10-cp313-none-win32.whl", hash = "sha256:eb0a42831372ec2b05acc9ee45af77bcaccbd91257345f93780a8e654efc75db"},
|
||||
{file = "orjson-3.10.10-cp313-none-win_amd64.whl", hash = "sha256:f0c4f37f8bf3f1075c6cc8dd8a9f843689a4b618628f8812d0a71e6968b95ffd"},
|
||||
{file = "orjson-3.10.10-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:829700cc18503efc0cf502d630f612884258020d98a317679cd2054af0259568"},
|
||||
{file = "orjson-3.10.10-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e0ceb5e0e8c4f010ac787d29ae6299846935044686509e2f0f06ed441c1ca949"},
|
||||
{file = "orjson-3.10.10-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0c25908eb86968613216f3db4d3003f1c45d78eb9046b71056ca327ff92bdbd4"},
|
||||
{file = "orjson-3.10.10-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:218cb0bc03340144b6328a9ff78f0932e642199ac184dd74b01ad691f42f93ff"},
|
||||
{file = "orjson-3.10.10-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e2277ec2cea3775640dc81ab5195bb5b2ada2fe0ea6eee4677474edc75ea6785"},
|
||||
{file = "orjson-3.10.10-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:848ea3b55ab5ccc9d7bbd420d69432628b691fba3ca8ae3148c35156cbd282aa"},
|
||||
{file = "orjson-3.10.10-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:e3e67b537ac0c835b25b5f7d40d83816abd2d3f4c0b0866ee981a045287a54f3"},
|
||||
{file = "orjson-3.10.10-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:7948cfb909353fce2135dcdbe4521a5e7e1159484e0bb024c1722f272488f2b8"},
|
||||
{file = "orjson-3.10.10-cp38-none-win32.whl", hash = "sha256:78bee66a988f1a333dc0b6257503d63553b1957889c17b2c4ed72385cd1b96ae"},
|
||||
{file = "orjson-3.10.10-cp38-none-win_amd64.whl", hash = "sha256:f1d647ca8d62afeb774340a343c7fc023efacfd3a39f70c798991063f0c681dd"},
|
||||
{file = "orjson-3.10.10-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:5a059afddbaa6dd733b5a2d76a90dbc8af790b993b1b5cb97a1176ca713b5df8"},
|
||||
{file = "orjson-3.10.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f9b5c59f7e2a1a410f971c5ebc68f1995822837cd10905ee255f96074537ee6"},
|
||||
{file = "orjson-3.10.10-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d5ef198bafdef4aa9d49a4165ba53ffdc0a9e1c7b6f76178572ab33118afea25"},
|
||||
{file = "orjson-3.10.10-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aaf29ce0bb5d3320824ec3d1508652421000ba466abd63bdd52c64bcce9eb1fa"},
|
||||
{file = "orjson-3.10.10-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dddd5516bcc93e723d029c1633ae79c4417477b4f57dad9bfeeb6bc0315e654a"},
|
||||
{file = "orjson-3.10.10-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a12f2003695b10817f0fa8b8fca982ed7f5761dcb0d93cff4f2f9f6709903fd7"},
|
||||
{file = "orjson-3.10.10-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:672f9874a8a8fb9bb1b771331d31ba27f57702c8106cdbadad8bda5d10bc1019"},
|
||||
{file = "orjson-3.10.10-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1dcbb0ca5fafb2b378b2c74419480ab2486326974826bbf6588f4dc62137570a"},
|
||||
{file = "orjson-3.10.10-cp39-none-win32.whl", hash = "sha256:d9bbd3a4b92256875cb058c3381b782649b9a3c68a4aa9a2fff020c2f9cfc1be"},
|
||||
{file = "orjson-3.10.10-cp39-none-win_amd64.whl", hash = "sha256:766f21487a53aee8524b97ca9582d5c6541b03ab6210fbaf10142ae2f3ced2aa"},
|
||||
{file = "orjson-3.10.10.tar.gz", hash = "sha256:37949383c4df7b4337ce82ee35b6d7471e55195efa7dcb45ab8226ceadb0fe3b"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2747,13 +2749,13 @@ cli = ["click (>=5.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "python-multipart"
|
||||
version = "0.0.12"
|
||||
version = "0.0.17"
|
||||
description = "A streaming multipart parser for Python"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "python_multipart-0.0.12-py3-none-any.whl", hash = "sha256:43dcf96cf65888a9cd3423544dd0d75ac10f7aa0c3c28a175bbcd00c9ce1aebf"},
|
||||
{file = "python_multipart-0.0.12.tar.gz", hash = "sha256:045e1f98d719c1ce085ed7f7e1ef9d8ccc8c02ba02b5566d5f7521410ced58cb"},
|
||||
{file = "python_multipart-0.0.17-py3-none-any.whl", hash = "sha256:15dc4f487e0a9476cc1201261188ee0940165cffc94429b6fc565c4d3045cb5d"},
|
||||
{file = "python_multipart-0.0.17.tar.gz", hash = "sha256:41330d831cae6e2f22902704ead2826ea038d0419530eadff3ea80175aec5538"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2984,13 +2986,13 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
|
||||
|
||||
[[package]]
|
||||
name = "rich"
|
||||
version = "13.9.2"
|
||||
version = "13.9.3"
|
||||
description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
|
||||
optional = false
|
||||
python-versions = ">=3.8.0"
|
||||
files = [
|
||||
{file = "rich-13.9.2-py3-none-any.whl", hash = "sha256:8c82a3d3f8dcfe9e734771313e606b39d8247bb6b826e196f4914b333b743cf1"},
|
||||
{file = "rich-13.9.2.tar.gz", hash = "sha256:51a2c62057461aaf7152b4d611168f93a9fc73068f8ded2790f29fe2b5366d0c"},
|
||||
{file = "rich-13.9.3-py3-none-any.whl", hash = "sha256:9836f5096eb2172c9e77df411c1b009bace4193d6a481d534fea75ebba758283"},
|
||||
{file = "rich-13.9.3.tar.gz", hash = "sha256:bc1e01b899537598cf02579d2b9f4a415104d3fc439313a7a2c165d76557a08e"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -3003,29 +3005,29 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"]
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.7.0"
|
||||
version = "0.7.1"
|
||||
description = "An extremely fast Python linter and code formatter, written in Rust."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "ruff-0.7.0-py3-none-linux_armv6l.whl", hash = "sha256:0cdf20c2b6ff98e37df47b2b0bd3a34aaa155f59a11182c1303cce79be715628"},
|
||||
{file = "ruff-0.7.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:496494d350c7fdeb36ca4ef1c9f21d80d182423718782222c29b3e72b3512737"},
|
||||
{file = "ruff-0.7.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:214b88498684e20b6b2b8852c01d50f0651f3cc6118dfa113b4def9f14faaf06"},
|
||||
{file = "ruff-0.7.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:630fce3fefe9844e91ea5bbf7ceadab4f9981f42b704fae011bb8efcaf5d84be"},
|
||||
{file = "ruff-0.7.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:211d877674e9373d4bb0f1c80f97a0201c61bcd1e9d045b6e9726adc42c156aa"},
|
||||
{file = "ruff-0.7.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:194d6c46c98c73949a106425ed40a576f52291c12bc21399eb8f13a0f7073495"},
|
||||
{file = "ruff-0.7.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:82c2579b82b9973a110fab281860403b397c08c403de92de19568f32f7178598"},
|
||||
{file = "ruff-0.7.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9af971fe85dcd5eaed8f585ddbc6bdbe8c217fb8fcf510ea6bca5bdfff56040e"},
|
||||
{file = "ruff-0.7.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b641c7f16939b7d24b7bfc0be4102c56562a18281f84f635604e8a6989948914"},
|
||||
{file = "ruff-0.7.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d71672336e46b34e0c90a790afeac8a31954fd42872c1f6adaea1dff76fd44f9"},
|
||||
{file = "ruff-0.7.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:ab7d98c7eed355166f367597e513a6c82408df4181a937628dbec79abb2a1fe4"},
|
||||
{file = "ruff-0.7.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1eb54986f770f49edb14f71d33312d79e00e629a57387382200b1ef12d6a4ef9"},
|
||||
{file = "ruff-0.7.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:dc452ba6f2bb9cf8726a84aa877061a2462afe9ae0ea1d411c53d226661c601d"},
|
||||
{file = "ruff-0.7.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:4b406c2dce5be9bad59f2de26139a86017a517e6bcd2688da515481c05a2cb11"},
|
||||
{file = "ruff-0.7.0-py3-none-win32.whl", hash = "sha256:f6c968509f767776f524a8430426539587d5ec5c662f6addb6aa25bc2e8195ec"},
|
||||
{file = "ruff-0.7.0-py3-none-win_amd64.whl", hash = "sha256:ff4aabfbaaba880e85d394603b9e75d32b0693152e16fa659a3064a85df7fce2"},
|
||||
{file = "ruff-0.7.0-py3-none-win_arm64.whl", hash = "sha256:10842f69c245e78d6adec7e1db0a7d9ddc2fff0621d730e61657b64fa36f207e"},
|
||||
{file = "ruff-0.7.0.tar.gz", hash = "sha256:47a86360cf62d9cd53ebfb0b5eb0e882193fc191c6d717e8bef4462bc3b9ea2b"},
|
||||
{file = "ruff-0.7.1-py3-none-linux_armv6l.whl", hash = "sha256:cb1bc5ed9403daa7da05475d615739cc0212e861b7306f314379d958592aaa89"},
|
||||
{file = "ruff-0.7.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:27c1c52a8d199a257ff1e5582d078eab7145129aa02721815ca8fa4f9612dc35"},
|
||||
{file = "ruff-0.7.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:588a34e1ef2ea55b4ddfec26bbe76bc866e92523d8c6cdec5e8aceefeff02d99"},
|
||||
{file = "ruff-0.7.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94fc32f9cdf72dc75c451e5f072758b118ab8100727168a3df58502b43a599ca"},
|
||||
{file = "ruff-0.7.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:985818742b833bffa543a84d1cc11b5e6871de1b4e0ac3060a59a2bae3969250"},
|
||||
{file = "ruff-0.7.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32f1e8a192e261366c702c5fb2ece9f68d26625f198a25c408861c16dc2dea9c"},
|
||||
{file = "ruff-0.7.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:699085bf05819588551b11751eff33e9ca58b1b86a6843e1b082a7de40da1565"},
|
||||
{file = "ruff-0.7.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:344cc2b0814047dc8c3a8ff2cd1f3d808bb23c6658db830d25147339d9bf9ea7"},
|
||||
{file = "ruff-0.7.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4316bbf69d5a859cc937890c7ac7a6551252b6a01b1d2c97e8fc96e45a7c8b4a"},
|
||||
{file = "ruff-0.7.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:79d3af9dca4c56043e738a4d6dd1e9444b6d6c10598ac52d146e331eb155a8ad"},
|
||||
{file = "ruff-0.7.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c5c121b46abde94a505175524e51891f829414e093cd8326d6e741ecfc0a9112"},
|
||||
{file = "ruff-0.7.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8422104078324ea250886954e48f1373a8fe7de59283d747c3a7eca050b4e378"},
|
||||
{file = "ruff-0.7.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:56aad830af8a9db644e80098fe4984a948e2b6fc2e73891538f43bbe478461b8"},
|
||||
{file = "ruff-0.7.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:658304f02f68d3a83c998ad8bf91f9b4f53e93e5412b8f2388359d55869727fd"},
|
||||
{file = "ruff-0.7.1-py3-none-win32.whl", hash = "sha256:b517a2011333eb7ce2d402652ecaa0ac1a30c114fbbd55c6b8ee466a7f600ee9"},
|
||||
{file = "ruff-0.7.1-py3-none-win_amd64.whl", hash = "sha256:f38c41fcde1728736b4eb2b18850f6d1e3eedd9678c914dede554a70d5241307"},
|
||||
{file = "ruff-0.7.1-py3-none-win_arm64.whl", hash = "sha256:19aa200ec824c0f36d0c9114c8ec0087082021732979a359d6f3c390a6ff2a37"},
|
||||
{file = "ruff-0.7.1.tar.gz", hash = "sha256:9d8a41d4aa2dad1575adb98a82870cf5db5f76b2938cf2206c22c940034a36f4"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3204,13 +3206,13 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "starlette"
|
||||
version = "0.37.2"
|
||||
version = "0.41.2"
|
||||
description = "The little ASGI library that shines."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "starlette-0.37.2-py3-none-any.whl", hash = "sha256:6fe59f29268538e5d0d182f2791a479a0c64638e6935d1c6989e63fb2699c6ee"},
|
||||
{file = "starlette-0.37.2.tar.gz", hash = "sha256:9af890290133b79fc3db55474ade20f6220a364a0402e0b556e7cd5e1e093823"},
|
||||
{file = "starlette-0.41.2-py3-none-any.whl", hash = "sha256:fbc189474b4731cf30fcef52f18a8d070e3f3b46c6a04c97579e85e6ffca942d"},
|
||||
{file = "starlette-0.41.2.tar.gz", hash = "sha256:9834fd799d1a87fd346deb76158668cfa0b0d56f85caefe8268e2d97c3468b62"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
@ -1,3 +1,3 @@
|
||||
{
|
||||
"flutter": "3.24.3"
|
||||
"flutter": "3.24.4"
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ plugins {
|
||||
id "com.android.application"
|
||||
id "kotlin-android"
|
||||
id "dev.flutter.flutter-gradle-plugin"
|
||||
id "kotlin-kapt"
|
||||
id 'com.google.devtools.ksp'
|
||||
}
|
||||
|
||||
def localProperties = new Properties()
|
||||
@ -31,12 +31,13 @@ android {
|
||||
compileSdkVersion 34
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
sourceCompatibility JavaVersion.VERSION_17
|
||||
targetCompatibility JavaVersion.VERSION_17
|
||||
coreLibraryDesugaringEnabled true
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = '1.8'
|
||||
jvmTarget = '17'
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
@ -74,6 +75,7 @@ android {
|
||||
signingConfig signingConfigs.release
|
||||
}
|
||||
}
|
||||
namespace 'app.alextran.immich'
|
||||
}
|
||||
|
||||
flutter {
|
||||
@ -81,11 +83,11 @@ flutter {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
def kotlin_version = '1.9.24'
|
||||
def kotlin_coroutines_version = '1.8.1'
|
||||
def work_version = '2.9.0'
|
||||
def concurrent_version = '1.1.0'
|
||||
def guava_version = '33.2.0-android'
|
||||
def kotlin_version = '2.0.20'
|
||||
def kotlin_coroutines_version = '1.9.0'
|
||||
def work_version = '2.9.1'
|
||||
def concurrent_version = '1.2.0'
|
||||
def guava_version = '33.3.1-android'
|
||||
def glide_version = '4.16.0'
|
||||
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||
@ -94,7 +96,8 @@ dependencies {
|
||||
implementation "androidx.concurrent:concurrent-futures:$concurrent_version"
|
||||
implementation "com.google.guava:guava:$guava_version"
|
||||
implementation "com.github.bumptech.glide:glide:$glide_version"
|
||||
kapt "com.github.bumptech.glide:compiler:$glide_version"
|
||||
ksp "com.github.bumptech.glide:ksp:$glide_version"
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.2'
|
||||
}
|
||||
|
||||
// This is uncommented in F-Droid build script
|
||||
|
@ -1,6 +1,6 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="app.alextran.immich">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- Flutter needs it to communicate with the running application
|
||||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
</manifest>
|
||||
</manifest>
|
||||
|
@ -1,4 +1,4 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="app.alextran.immich"
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
@ -1,6 +1,6 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="app.alextran.immich">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- Flutter needs it to communicate with the running application
|
||||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
</manifest>
|
||||
</manifest>
|
||||
|
@ -1,5 +1,5 @@
|
||||
allprojects {
|
||||
ext.kotlin_version = '1.9.24'
|
||||
ext.kotlin_version = '2.0.20'
|
||||
|
||||
repositories {
|
||||
google()
|
||||
|
@ -1,3 +1,5 @@
|
||||
org.gradle.jvmargs=-Xmx1536M
|
||||
org.gradle.jvmargs=-Xmx4096M
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
android.enableJetifier=true
|
||||
android.nonTransitiveRClass=false
|
||||
android.nonFinalResIds=false
|
||||
|
@ -1,7 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-all.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.4-all.zip
|
||||
distributionSha256Sum=fe696c020f241a5f69c30f763c5a7f38eec54b490db19cd2b0962dda420d7d12
|
@ -18,9 +18,9 @@ pluginManagement {
|
||||
|
||||
plugins {
|
||||
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
|
||||
id "com.android.application" version "7.4.2" apply false
|
||||
id "org.jetbrains.kotlin.android" version "1.9.0" apply false
|
||||
id "org.jetbrains.kotlin.kapt" version "1.9.0" apply false
|
||||
id "com.android.application" version '8.7.2' apply false
|
||||
id "org.jetbrains.kotlin.android" version "2.0.20" apply false
|
||||
id 'com.google.devtools.ksp' version '2.0.20-1.0.24' apply false
|
||||
}
|
||||
|
||||
include ":app"
|
||||
|
@ -48,7 +48,7 @@ PODS:
|
||||
- flutter_udid (0.0.1):
|
||||
- Flutter
|
||||
- SAMKeychain
|
||||
- flutter_web_auth (0.5.0):
|
||||
- flutter_web_auth (0.6.0):
|
||||
- Flutter
|
||||
- fluttertoast (0.0.2):
|
||||
- Flutter
|
||||
@ -202,7 +202,7 @@ SPEC CHECKSUMS:
|
||||
flutter_local_notifications: 4cde75091f6327eb8517fa068a0a5950212d2086
|
||||
flutter_native_splash: edf599c81f74d093a4daf8e17bd7a018854bc778
|
||||
flutter_udid: a2482c67a61b9c806ef59dd82ed8d007f1b7ac04
|
||||
flutter_web_auth: c25208760459cec375a3c39f6a8759165ca0fa4d
|
||||
flutter_web_auth: acc15a8fd7bba796a933c724a6dffc3d00f07c27
|
||||
fluttertoast: e9a18c7be5413da53898f660530c56f35edfba9c
|
||||
geolocator_apple: 6cbaf322953988e009e5ecb481f07efece75c450
|
||||
image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1
|
||||
|
2
mobile/lib/entities/album.entity.g.dart
generated
2
mobile/lib/entities/album.entity.g.dart
generated
@ -131,7 +131,7 @@ const AlbumSchema = CollectionSchema(
|
||||
getId: _albumGetId,
|
||||
getLinks: _albumGetLinks,
|
||||
attach: _albumAttach,
|
||||
version: '3.1.0+1',
|
||||
version: '3.1.8',
|
||||
);
|
||||
|
||||
int _albumEstimateSize(
|
||||
|
@ -49,7 +49,7 @@ const AndroidDeviceAssetSchema = CollectionSchema(
|
||||
getId: _androidDeviceAssetGetId,
|
||||
getLinks: _androidDeviceAssetGetLinks,
|
||||
attach: _androidDeviceAssetAttach,
|
||||
version: '3.1.0+1',
|
||||
version: '3.1.8',
|
||||
);
|
||||
|
||||
int _androidDeviceAssetEstimateSize(
|
||||
|
@ -524,7 +524,8 @@ bool isRotated270CW(int orientation) {
|
||||
|
||||
/// Returns `true` if this [Asset] is flipped 90° or 270° clockwise
|
||||
bool isFlipped(AssetResponseDto response) {
|
||||
final int orientation = response.exifInfo?.orientation?.toInt() ?? 0;
|
||||
final int orientation =
|
||||
int.tryParse(response.exifInfo?.orientation ?? '0') ?? 0;
|
||||
return orientation != 0 &&
|
||||
(isRotated90CW(orientation) || isRotated270CW(orientation));
|
||||
}
|
||||
|
2
mobile/lib/entities/asset.entity.g.dart
generated
2
mobile/lib/entities/asset.entity.g.dart
generated
@ -180,7 +180,7 @@ const AssetSchema = CollectionSchema(
|
||||
getId: _assetGetId,
|
||||
getLinks: _assetGetLinks,
|
||||
attach: _assetAttach,
|
||||
version: '3.1.0+1',
|
||||
version: '3.1.8',
|
||||
);
|
||||
|
||||
int _assetEstimateSize(
|
||||
|
2
mobile/lib/entities/backup_album.entity.g.dart
generated
2
mobile/lib/entities/backup_album.entity.g.dart
generated
@ -45,7 +45,7 @@ const BackupAlbumSchema = CollectionSchema(
|
||||
getId: _backupAlbumGetId,
|
||||
getLinks: _backupAlbumGetLinks,
|
||||
attach: _backupAlbumAttach,
|
||||
version: '3.1.0+1',
|
||||
version: '3.1.8',
|
||||
);
|
||||
|
||||
int _backupAlbumEstimateSize(
|
||||
|
@ -34,7 +34,7 @@ const DuplicatedAssetSchema = CollectionSchema(
|
||||
getId: _duplicatedAssetGetId,
|
||||
getLinks: _duplicatedAssetGetLinks,
|
||||
attach: _duplicatedAssetAttach,
|
||||
version: '3.1.0+1',
|
||||
version: '3.1.8',
|
||||
);
|
||||
|
||||
int _duplicatedAssetEstimateSize(
|
||||
|
2
mobile/lib/entities/etag.entity.g.dart
generated
2
mobile/lib/entities/etag.entity.g.dart
generated
@ -58,7 +58,7 @@ const ETagSchema = CollectionSchema(
|
||||
getId: _eTagGetId,
|
||||
getLinks: _eTagGetLinks,
|
||||
attach: _eTagAttach,
|
||||
version: '3.1.0+1',
|
||||
version: '3.1.8',
|
||||
);
|
||||
|
||||
int _eTagEstimateSize(
|
||||
|
2
mobile/lib/entities/exif_info.entity.g.dart
generated
2
mobile/lib/entities/exif_info.entity.g.dart
generated
@ -109,7 +109,7 @@ const ExifInfoSchema = CollectionSchema(
|
||||
getId: _exifInfoGetId,
|
||||
getLinks: _exifInfoGetLinks,
|
||||
attach: _exifInfoAttach,
|
||||
version: '3.1.0+1',
|
||||
version: '3.1.8',
|
||||
);
|
||||
|
||||
int _exifInfoEstimateSize(
|
||||
|
@ -66,7 +66,7 @@ const IOSDeviceAssetSchema = CollectionSchema(
|
||||
getId: _iOSDeviceAssetGetId,
|
||||
getLinks: _iOSDeviceAssetGetLinks,
|
||||
attach: _iOSDeviceAssetAttach,
|
||||
version: '3.1.0+1',
|
||||
version: '3.1.8',
|
||||
);
|
||||
|
||||
int _iOSDeviceAssetEstimateSize(
|
||||
|
2
mobile/lib/entities/logger_message.entity.g.dart
generated
2
mobile/lib/entities/logger_message.entity.g.dart
generated
@ -60,7 +60,7 @@ const LoggerMessageSchema = CollectionSchema(
|
||||
getId: _loggerMessageGetId,
|
||||
getLinks: _loggerMessageGetLinks,
|
||||
attach: _loggerMessageAttach,
|
||||
version: '3.1.0+1',
|
||||
version: '3.1.8',
|
||||
);
|
||||
|
||||
int _loggerMessageEstimateSize(
|
||||
|
2
mobile/lib/entities/store.entity.g.dart
generated
2
mobile/lib/entities/store.entity.g.dart
generated
@ -39,7 +39,7 @@ const StoreValueSchema = CollectionSchema(
|
||||
getId: _storeValueGetId,
|
||||
getLinks: _storeValueGetLinks,
|
||||
attach: _storeValueAttach,
|
||||
version: '3.1.0+1',
|
||||
version: '3.1.8',
|
||||
);
|
||||
|
||||
int _storeValueEstimateSize(
|
||||
|
2
mobile/lib/entities/user.entity.g.dart
generated
2
mobile/lib/entities/user.entity.g.dart
generated
@ -129,7 +129,7 @@ const UserSchema = CollectionSchema(
|
||||
getId: _userGetId,
|
||||
getLinks: _userGetLinks,
|
||||
attach: _userAttach,
|
||||
version: '3.1.0+1',
|
||||
version: '3.1.8',
|
||||
);
|
||||
|
||||
int _userEstimateSize(
|
||||
|
37
mobile/lib/models/search/search_result.model.dart
Normal file
37
mobile/lib/models/search/search_result.model.dart
Normal file
@ -0,0 +1,37 @@
|
||||
import 'package:collection/collection.dart';
|
||||
|
||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||
|
||||
class SearchResult {
|
||||
final List<Asset> assets;
|
||||
final int? nextPage;
|
||||
|
||||
SearchResult({
|
||||
required this.assets,
|
||||
this.nextPage,
|
||||
});
|
||||
|
||||
SearchResult copyWith({
|
||||
List<Asset>? assets,
|
||||
int? nextPage,
|
||||
}) {
|
||||
return SearchResult(
|
||||
assets: assets ?? this.assets,
|
||||
nextPage: nextPage ?? this.nextPage,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() => 'SearchResult(assets: $assets, nextPage: $nextPage)';
|
||||
|
||||
@override
|
||||
bool operator ==(covariant SearchResult other) {
|
||||
if (identical(this, other)) return true;
|
||||
final listEquals = const DeepCollectionEquality().equals;
|
||||
|
||||
return listEquals(other.assets, assets) && other.nextPage == nextPage;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => assets.hashCode ^ nextPage.hashCode;
|
||||
}
|
@ -58,23 +58,22 @@ class SearchPage extends HookConsumerWidget {
|
||||
final mediaTypeCurrentFilterWidget = useState<Widget?>(null);
|
||||
final displayOptionCurrentFilterWidget = useState<Widget?>(null);
|
||||
|
||||
final currentPage = useState(1);
|
||||
final searchProvider = ref.watch(paginatedSearchProvider);
|
||||
final searchResultCount = useState(0);
|
||||
final isSearching = useState(false);
|
||||
|
||||
search() async {
|
||||
if (prefilter == null && filter.value == previousFilter.value) return;
|
||||
|
||||
isSearching.value = true;
|
||||
ref.watch(paginatedSearchProvider.notifier).clear();
|
||||
|
||||
currentPage.value = 1;
|
||||
|
||||
final searchResult = await ref
|
||||
.watch(paginatedSearchProvider.notifier)
|
||||
.getNextPage(filter.value, currentPage.value);
|
||||
|
||||
await ref.watch(paginatedSearchProvider.notifier).search(filter.value);
|
||||
previousFilter.value = filter.value;
|
||||
searchResultCount.value = searchResult.length;
|
||||
isSearching.value = false;
|
||||
}
|
||||
|
||||
loadMoreSearchResult() async {
|
||||
isSearching.value = true;
|
||||
await ref.watch(paginatedSearchProvider.notifier).search(filter.value);
|
||||
isSearching.value = false;
|
||||
}
|
||||
|
||||
searchPrefilter() {
|
||||
@ -97,20 +96,16 @@ class SearchPage extends HookConsumerWidget {
|
||||
|
||||
useEffect(
|
||||
() {
|
||||
Future.microtask(
|
||||
() => ref.invalidate(paginatedSearchProvider),
|
||||
);
|
||||
searchPrefilter();
|
||||
|
||||
return null;
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
loadMoreSearchResult() async {
|
||||
currentPage.value += 1;
|
||||
final searchResult = await ref
|
||||
.watch(paginatedSearchProvider.notifier)
|
||||
.getNextPage(filter.value, currentPage.value);
|
||||
searchResultCount.value = searchResult.length;
|
||||
}
|
||||
|
||||
showPeoplePicker() {
|
||||
handleOnSelect(Set<Person> value) {
|
||||
filter.value = filter.value.copyWith(
|
||||
@ -465,41 +460,6 @@ class SearchPage extends HookConsumerWidget {
|
||||
search();
|
||||
}
|
||||
|
||||
buildSearchResult() {
|
||||
return switch (searchProvider) {
|
||||
AsyncData() => Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: NotificationListener<ScrollEndNotification>(
|
||||
onNotification: (notification) {
|
||||
final metrics = notification.metrics;
|
||||
final shouldLoadMore = searchResultCount.value > 75;
|
||||
if (metrics.pixels >= metrics.maxScrollExtent &&
|
||||
shouldLoadMore) {
|
||||
loadMoreSearchResult();
|
||||
}
|
||||
return true;
|
||||
},
|
||||
child: MultiselectGrid(
|
||||
renderListProvider: paginatedSearchRenderListProvider,
|
||||
archiveEnabled: true,
|
||||
deleteEnabled: true,
|
||||
editEnabled: true,
|
||||
favoriteEnabled: true,
|
||||
stackEnabled: false,
|
||||
emptyIndicator: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: SearchEmptyContent(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
AsyncError(:final error) => Text('Error: $error'),
|
||||
_ => const Expanded(child: Center(child: CircularProgressIndicator())),
|
||||
};
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
resizeToAvoidBottomInset: true,
|
||||
appBar: AppBar(
|
||||
@ -635,13 +595,67 @@ class SearchPage extends HookConsumerWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
buildSearchResult(),
|
||||
SearchResultGrid(
|
||||
onScrollEnd: loadMoreSearchResult,
|
||||
isSearching: isSearching.value,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SearchResultGrid extends StatelessWidget {
|
||||
final VoidCallback onScrollEnd;
|
||||
final bool isSearching;
|
||||
|
||||
const SearchResultGrid({
|
||||
super.key,
|
||||
required this.onScrollEnd,
|
||||
this.isSearching = false,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: NotificationListener<ScrollEndNotification>(
|
||||
onNotification: (notification) {
|
||||
final isBottomSheetNotification = notification.context
|
||||
?.findAncestorWidgetOfExactType<
|
||||
DraggableScrollableSheet>() !=
|
||||
null;
|
||||
|
||||
final metrics = notification.metrics;
|
||||
final isVerticalScroll = metrics.axis == Axis.vertical;
|
||||
|
||||
if (metrics.pixels >= metrics.maxScrollExtent &&
|
||||
isVerticalScroll &&
|
||||
!isBottomSheetNotification) {
|
||||
onScrollEnd();
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
child: MultiselectGrid(
|
||||
renderListProvider: paginatedSearchRenderListProvider,
|
||||
archiveEnabled: true,
|
||||
deleteEnabled: true,
|
||||
editEnabled: true,
|
||||
favoriteEnabled: true,
|
||||
stackEnabled: false,
|
||||
emptyIndicator: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: !isSearching ? SearchEmptyContent() : SizedBox.shrink(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SearchEmptyContent extends StatelessWidget {
|
||||
const SearchEmptyContent({super.key});
|
||||
|
||||
|
@ -84,34 +84,48 @@ class AssetNotifier extends StateNotifier<bool> {
|
||||
_deleteInProgress = true;
|
||||
state = true;
|
||||
try {
|
||||
// Filter the assets based on the backed-up status
|
||||
final assets = onlyBackedUp
|
||||
? deleteAssets.where((e) => e.storage == AssetState.merged)
|
||||
: deleteAssets;
|
||||
|
||||
if (assets.isEmpty) {
|
||||
return false; // No assets to delete
|
||||
}
|
||||
|
||||
// Proceed with local deletion of the filtered assets
|
||||
final localDeleted = await _deleteLocalAssets(assets);
|
||||
|
||||
if (localDeleted.isNotEmpty) {
|
||||
final localOnlyIds = deleteAssets
|
||||
final localOnlyIds = assets
|
||||
.where((e) => e.storage == AssetState.local)
|
||||
.map((e) => e.id)
|
||||
.toList();
|
||||
// Update merged assets to remote only
|
||||
|
||||
// Update merged assets to remote-only
|
||||
final mergedAssets =
|
||||
deleteAssets.where((e) => e.storage == AssetState.merged).map((e) {
|
||||
assets.where((e) => e.storage == AssetState.merged).map((e) {
|
||||
e.localId = null;
|
||||
return e;
|
||||
}).toList();
|
||||
|
||||
// Update the local database
|
||||
await _db.writeTxn(() async {
|
||||
if (mergedAssets.isNotEmpty) {
|
||||
await _db.assets.putAll(mergedAssets);
|
||||
await _db.assets
|
||||
.putAll(mergedAssets); // Use the filtered merged assets
|
||||
}
|
||||
await _db.exifInfos.deleteAll(localOnlyIds);
|
||||
await _db.assets.deleteAll(localOnlyIds);
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
} finally {
|
||||
_deleteInProgress = false;
|
||||
state = false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1,46 +1,39 @@
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/models/search/search_result.model.dart';
|
||||
import 'package:immich_mobile/providers/asset_viewer/render_list.provider.dart';
|
||||
import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart';
|
||||
import 'package:immich_mobile/models/search/search_filter.model.dart';
|
||||
import 'package:immich_mobile/services/search.service.dart';
|
||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
part 'paginated_search.provider.g.dart';
|
||||
|
||||
@riverpod
|
||||
class PaginatedSearch extends _$PaginatedSearch {
|
||||
Future<List<Asset>?> _search(SearchFilter filter, int page) async {
|
||||
final service = ref.read(searchServiceProvider);
|
||||
final result = await service.search(filter, page);
|
||||
final paginatedSearchProvider =
|
||||
StateNotifierProvider<PaginatedSearchNotifier, SearchResult>(
|
||||
(ref) => PaginatedSearchNotifier(ref.watch(searchServiceProvider)),
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
class PaginatedSearchNotifier extends StateNotifier<SearchResult> {
|
||||
final SearchService _searchService;
|
||||
|
||||
@override
|
||||
Future<List<Asset>> build() async {
|
||||
return [];
|
||||
}
|
||||
PaginatedSearchNotifier(this._searchService)
|
||||
: super(SearchResult(assets: [], nextPage: 1));
|
||||
|
||||
Future<List<Asset>> getNextPage(SearchFilter filter, int nextPage) async {
|
||||
state = const AsyncValue.loading();
|
||||
search(SearchFilter filter) async {
|
||||
if (state.nextPage == null) return;
|
||||
|
||||
final newState = await AsyncValue.guard(() async {
|
||||
final assets = await _search(filter, nextPage);
|
||||
final result = await _searchService.search(filter, state.nextPage!);
|
||||
|
||||
if (assets != null) {
|
||||
return [...?state.value, ...assets];
|
||||
}
|
||||
});
|
||||
if (result == null) return;
|
||||
|
||||
state = newState.valueOrNull == null
|
||||
? const AsyncValue.data([])
|
||||
: AsyncValue.data(newState.value!);
|
||||
|
||||
return newState.valueOrNull ?? [];
|
||||
state = SearchResult(
|
||||
assets: [...state.assets, ...result.assets],
|
||||
nextPage: result.nextPage,
|
||||
);
|
||||
}
|
||||
|
||||
clear() {
|
||||
state = const AsyncValue.data([]);
|
||||
state = SearchResult(assets: [], nextPage: 1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -48,15 +41,11 @@ class PaginatedSearch extends _$PaginatedSearch {
|
||||
AsyncValue<RenderList> paginatedSearchRenderList(
|
||||
PaginatedSearchRenderListRef ref,
|
||||
) {
|
||||
final assets = ref.watch(paginatedSearchProvider).value;
|
||||
final result = ref.watch(paginatedSearchProvider);
|
||||
|
||||
if (assets != null) {
|
||||
return ref.watch(
|
||||
renderListProviderWithGrouping(
|
||||
(assets, GroupAssetsBy.none),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return const AsyncValue.loading();
|
||||
}
|
||||
return ref.watch(
|
||||
renderListProviderWithGrouping(
|
||||
(result.assets, GroupAssetsBy.none),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ part of 'paginated_search.provider.dart';
|
||||
// **************************************************************************
|
||||
|
||||
String _$paginatedSearchRenderListHash() =>
|
||||
r'c2cc2381ee6ea8f8e08d6d4c1289bbf0c6b9647e';
|
||||
r'4585c832106b16b6d294055f47bbbe83e0802846';
|
||||
|
||||
/// See also [paginatedSearchRenderList].
|
||||
@ProviderFor(paginatedSearchRenderList)
|
||||
@ -24,21 +24,5 @@ final paginatedSearchRenderListProvider =
|
||||
|
||||
typedef PaginatedSearchRenderListRef
|
||||
= AutoDisposeProviderRef<AsyncValue<RenderList>>;
|
||||
String _$paginatedSearchHash() => r'8312f358261368cf2b5572b839fdd8f8fbe9a62e';
|
||||
|
||||
/// See also [PaginatedSearch].
|
||||
@ProviderFor(PaginatedSearch)
|
||||
final paginatedSearchProvider =
|
||||
AutoDisposeAsyncNotifierProvider<PaginatedSearch, List<Asset>>.internal(
|
||||
PaginatedSearch.new,
|
||||
name: r'paginatedSearchProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$paginatedSearchHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$PaginatedSearch = AutoDisposeAsyncNotifier<List<Asset>>;
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member
|
||||
|
@ -1,8 +1,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/extensions/string_extensions.dart';
|
||||
import 'package:immich_mobile/interfaces/asset.interface.dart';
|
||||
import 'package:immich_mobile/models/search/search_filter.model.dart';
|
||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||
import 'package:immich_mobile/models/search/search_result.model.dart';
|
||||
import 'package:immich_mobile/providers/api.provider.dart';
|
||||
import 'package:immich_mobile/repositories/asset.repository.dart';
|
||||
import 'package:immich_mobile/services/api.service.dart';
|
||||
@ -44,7 +46,7 @@ class SearchService {
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<Asset>?> search(SearchFilter filter, int page) async {
|
||||
Future<SearchResult?> search(SearchFilter filter, int page) async {
|
||||
try {
|
||||
SearchResponseDto? response;
|
||||
AssetTypeEnum? type;
|
||||
@ -103,8 +105,12 @@ class SearchService {
|
||||
return null;
|
||||
}
|
||||
|
||||
return _assetRepository
|
||||
.getAllByRemoteId(response.assets.items.map((e) => e.id));
|
||||
return SearchResult(
|
||||
assets: await _assetRepository.getAllByRemoteId(
|
||||
response.assets.items.map((e) => e.id),
|
||||
),
|
||||
nextPage: response.assets.nextPage?.toInt(),
|
||||
);
|
||||
} catch (error, stackTrace) {
|
||||
_log.severe("Failed to search for assets", error, stackTrace);
|
||||
}
|
||||
|
@ -203,18 +203,30 @@ class MultiselectGrid extends HookConsumerWidget {
|
||||
void onDeleteLocal(bool onlyBackedUp) async {
|
||||
processing.value = true;
|
||||
try {
|
||||
// Select only the local assets from the selection
|
||||
final localIds = selection.value.where((a) => a.isLocal).toList();
|
||||
|
||||
// Delete only the backed-up assets if 'onlyBackedUp' is true
|
||||
final isDeleted = await ref
|
||||
.read(assetProvider.notifier)
|
||||
.deleteLocalOnlyAssets(localIds, onlyBackedUp: onlyBackedUp);
|
||||
|
||||
if (isDeleted) {
|
||||
// Show a toast with the correct number of deleted assets
|
||||
final deletedCount = localIds
|
||||
.where(
|
||||
(e) => !onlyBackedUp || e.isRemote,
|
||||
) // Only count backed-up assets
|
||||
.length;
|
||||
|
||||
ImmichToast.show(
|
||||
context: context,
|
||||
msg: 'assets_removed_permanently_from_device'
|
||||
.tr(args: ["${localIds.length}"]),
|
||||
.tr(args: ["$deletedCount"]),
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
);
|
||||
|
||||
// Reset the selection
|
||||
selectionEnabledHook.value = false;
|
||||
}
|
||||
} finally {
|
||||
|
@ -131,17 +131,36 @@ class ThumbnailImage extends ConsumerWidget {
|
||||
}
|
||||
|
||||
Widget buildImage() {
|
||||
final image = SizedBox(
|
||||
width: 300,
|
||||
height: 300,
|
||||
final image = SizedBox.expand(
|
||||
child: Hero(
|
||||
tag: isFromDto
|
||||
? '${asset.remoteId}-$heroOffset'
|
||||
: asset.id + heroOffset,
|
||||
child: ImmichThumbnail(
|
||||
asset: asset,
|
||||
height: 250,
|
||||
width: 250,
|
||||
child: Stack(
|
||||
children: [
|
||||
SizedBox.expand(
|
||||
child: ImmichThumbnail(
|
||||
asset: asset,
|
||||
height: 250,
|
||||
width: 250,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
decoration: const BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
Color.fromRGBO(0, 0, 0, 0.1),
|
||||
Colors.transparent,
|
||||
Colors.transparent,
|
||||
Color.fromRGBO(0, 0, 0, 0.1),
|
||||
],
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
stops: [0, 0.3, 0.6, 1],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
@ -153,11 +172,8 @@ class ThumbnailImage extends ConsumerWidget {
|
||||
color: canDeselect ? assetContainerColor : Colors.grey,
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: const BorderRadius.only(
|
||||
topRight: Radius.circular(15.0),
|
||||
bottomRight: Radius.circular(15.0),
|
||||
bottomLeft: Radius.circular(15.0),
|
||||
topLeft: Radius.zero,
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(15.0),
|
||||
),
|
||||
child: image,
|
||||
),
|
||||
@ -177,7 +193,33 @@ class ThumbnailImage extends ConsumerWidget {
|
||||
)
|
||||
: const Border(),
|
||||
),
|
||||
child: buildImage(),
|
||||
child: Stack(
|
||||
children: [
|
||||
buildImage(),
|
||||
if (showStorageIndicator)
|
||||
Positioned(
|
||||
right: 8,
|
||||
bottom: 5,
|
||||
child: Icon(
|
||||
storageIcon(asset),
|
||||
color: Colors.white.withOpacity(.8),
|
||||
size: 16,
|
||||
),
|
||||
),
|
||||
if (asset.isFavorite)
|
||||
const Positioned(
|
||||
left: 8,
|
||||
bottom: 5,
|
||||
child: Icon(
|
||||
Icons.favorite,
|
||||
color: Colors.white,
|
||||
size: 16,
|
||||
),
|
||||
),
|
||||
if (!asset.isImage) buildVideoIcon(),
|
||||
if (asset.stackCount > 0) buildStackIcon(),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (multiselectEnabled)
|
||||
Padding(
|
||||
@ -187,28 +229,6 @@ class ThumbnailImage extends ConsumerWidget {
|
||||
child: buildSelectionIcon(asset),
|
||||
),
|
||||
),
|
||||
if (showStorageIndicator)
|
||||
Positioned(
|
||||
right: 8,
|
||||
bottom: 5,
|
||||
child: Icon(
|
||||
storageIcon(asset),
|
||||
color: Colors.white.withOpacity(.8),
|
||||
size: 16,
|
||||
),
|
||||
),
|
||||
if (asset.isFavorite)
|
||||
const Positioned(
|
||||
left: 8,
|
||||
bottom: 5,
|
||||
child: Icon(
|
||||
Icons.favorite,
|
||||
color: Colors.white,
|
||||
size: 18,
|
||||
),
|
||||
),
|
||||
if (!asset.isImage) buildVideoIcon(),
|
||||
if (asset.stackCount > 0) buildStackIcon(),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
2
mobile/openapi/README.md
generated
2
mobile/openapi/README.md
generated
@ -306,6 +306,7 @@ Class | Method | HTTP request | Description
|
||||
- [CreateAlbumDto](doc//CreateAlbumDto.md)
|
||||
- [CreateLibraryDto](doc//CreateLibraryDto.md)
|
||||
- [CreateProfileImageResponseDto](doc//CreateProfileImageResponseDto.md)
|
||||
- [DatabaseBackupConfig](doc//DatabaseBackupConfig.md)
|
||||
- [DownloadArchiveInfo](doc//DownloadArchiveInfo.md)
|
||||
- [DownloadInfoDto](doc//DownloadInfoDto.md)
|
||||
- [DownloadResponse](doc//DownloadResponse.md)
|
||||
@ -413,6 +414,7 @@ Class | Method | HTTP request | Description
|
||||
- [StackCreateDto](doc//StackCreateDto.md)
|
||||
- [StackResponseDto](doc//StackResponseDto.md)
|
||||
- [StackUpdateDto](doc//StackUpdateDto.md)
|
||||
- [SystemConfigBackupsDto](doc//SystemConfigBackupsDto.md)
|
||||
- [SystemConfigDto](doc//SystemConfigDto.md)
|
||||
- [SystemConfigFFmpegDto](doc//SystemConfigFFmpegDto.md)
|
||||
- [SystemConfigFacesDto](doc//SystemConfigFacesDto.md)
|
||||
|
2
mobile/openapi/lib/api.dart
generated
2
mobile/openapi/lib/api.dart
generated
@ -120,6 +120,7 @@ part 'model/colorspace.dart';
|
||||
part 'model/create_album_dto.dart';
|
||||
part 'model/create_library_dto.dart';
|
||||
part 'model/create_profile_image_response_dto.dart';
|
||||
part 'model/database_backup_config.dart';
|
||||
part 'model/download_archive_info.dart';
|
||||
part 'model/download_info_dto.dart';
|
||||
part 'model/download_response.dart';
|
||||
@ -227,6 +228,7 @@ part 'model/source_type.dart';
|
||||
part 'model/stack_create_dto.dart';
|
||||
part 'model/stack_response_dto.dart';
|
||||
part 'model/stack_update_dto.dart';
|
||||
part 'model/system_config_backups_dto.dart';
|
||||
part 'model/system_config_dto.dart';
|
||||
part 'model/system_config_f_fmpeg_dto.dart';
|
||||
part 'model/system_config_faces_dto.dart';
|
||||
|
4
mobile/openapi/lib/api_client.dart
generated
4
mobile/openapi/lib/api_client.dart
generated
@ -294,6 +294,8 @@ class ApiClient {
|
||||
return CreateLibraryDto.fromJson(value);
|
||||
case 'CreateProfileImageResponseDto':
|
||||
return CreateProfileImageResponseDto.fromJson(value);
|
||||
case 'DatabaseBackupConfig':
|
||||
return DatabaseBackupConfig.fromJson(value);
|
||||
case 'DownloadArchiveInfo':
|
||||
return DownloadArchiveInfo.fromJson(value);
|
||||
case 'DownloadInfoDto':
|
||||
@ -508,6 +510,8 @@ class ApiClient {
|
||||
return StackResponseDto.fromJson(value);
|
||||
case 'StackUpdateDto':
|
||||
return StackUpdateDto.fromJson(value);
|
||||
case 'SystemConfigBackupsDto':
|
||||
return SystemConfigBackupsDto.fromJson(value);
|
||||
case 'SystemConfigDto':
|
||||
return SystemConfigDto.fromJson(value);
|
||||
case 'SystemConfigFFmpegDto':
|
||||
|
@ -14,6 +14,7 @@ class AllJobStatusResponseDto {
|
||||
/// Returns a new [AllJobStatusResponseDto] instance.
|
||||
AllJobStatusResponseDto({
|
||||
required this.backgroundTask,
|
||||
required this.backupDatabase,
|
||||
required this.duplicateDetection,
|
||||
required this.faceDetection,
|
||||
required this.facialRecognition,
|
||||
@ -31,6 +32,8 @@ class AllJobStatusResponseDto {
|
||||
|
||||
JobStatusDto backgroundTask;
|
||||
|
||||
JobStatusDto backupDatabase;
|
||||
|
||||
JobStatusDto duplicateDetection;
|
||||
|
||||
JobStatusDto faceDetection;
|
||||
@ -60,6 +63,7 @@ class AllJobStatusResponseDto {
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is AllJobStatusResponseDto &&
|
||||
other.backgroundTask == backgroundTask &&
|
||||
other.backupDatabase == backupDatabase &&
|
||||
other.duplicateDetection == duplicateDetection &&
|
||||
other.faceDetection == faceDetection &&
|
||||
other.facialRecognition == facialRecognition &&
|
||||
@ -78,6 +82,7 @@ class AllJobStatusResponseDto {
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(backgroundTask.hashCode) +
|
||||
(backupDatabase.hashCode) +
|
||||
(duplicateDetection.hashCode) +
|
||||
(faceDetection.hashCode) +
|
||||
(facialRecognition.hashCode) +
|
||||
@ -93,11 +98,12 @@ class AllJobStatusResponseDto {
|
||||
(videoConversion.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'AllJobStatusResponseDto[backgroundTask=$backgroundTask, duplicateDetection=$duplicateDetection, faceDetection=$faceDetection, facialRecognition=$facialRecognition, library_=$library_, metadataExtraction=$metadataExtraction, migration=$migration, notifications=$notifications, search=$search, sidecar=$sidecar, smartSearch=$smartSearch, storageTemplateMigration=$storageTemplateMigration, thumbnailGeneration=$thumbnailGeneration, videoConversion=$videoConversion]';
|
||||
String toString() => 'AllJobStatusResponseDto[backgroundTask=$backgroundTask, backupDatabase=$backupDatabase, duplicateDetection=$duplicateDetection, faceDetection=$faceDetection, facialRecognition=$facialRecognition, library_=$library_, metadataExtraction=$metadataExtraction, migration=$migration, notifications=$notifications, search=$search, sidecar=$sidecar, smartSearch=$smartSearch, storageTemplateMigration=$storageTemplateMigration, thumbnailGeneration=$thumbnailGeneration, videoConversion=$videoConversion]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'backgroundTask'] = this.backgroundTask;
|
||||
json[r'backupDatabase'] = this.backupDatabase;
|
||||
json[r'duplicateDetection'] = this.duplicateDetection;
|
||||
json[r'faceDetection'] = this.faceDetection;
|
||||
json[r'facialRecognition'] = this.facialRecognition;
|
||||
@ -124,6 +130,7 @@ class AllJobStatusResponseDto {
|
||||
|
||||
return AllJobStatusResponseDto(
|
||||
backgroundTask: JobStatusDto.fromJson(json[r'backgroundTask'])!,
|
||||
backupDatabase: JobStatusDto.fromJson(json[r'backupDatabase'])!,
|
||||
duplicateDetection: JobStatusDto.fromJson(json[r'duplicateDetection'])!,
|
||||
faceDetection: JobStatusDto.fromJson(json[r'faceDetection'])!,
|
||||
facialRecognition: JobStatusDto.fromJson(json[r'facialRecognition'])!,
|
||||
@ -185,6 +192,7 @@ class AllJobStatusResponseDto {
|
||||
/// The list of required keys that must be present in a JSON.
|
||||
static const requiredKeys = <String>{
|
||||
'backgroundTask',
|
||||
'backupDatabase',
|
||||
'duplicateDetection',
|
||||
'faceDetection',
|
||||
'facialRecognition',
|
||||
|
116
mobile/openapi/lib/model/database_backup_config.dart
generated
Normal file
116
mobile/openapi/lib/model/database_backup_config.dart
generated
Normal file
@ -0,0 +1,116 @@
|
||||
//
|
||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||
//
|
||||
// @dart=2.18
|
||||
|
||||
// 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 DatabaseBackupConfig {
|
||||
/// Returns a new [DatabaseBackupConfig] instance.
|
||||
DatabaseBackupConfig({
|
||||
required this.cronExpression,
|
||||
required this.enabled,
|
||||
required this.keepLastAmount,
|
||||
});
|
||||
|
||||
String cronExpression;
|
||||
|
||||
bool enabled;
|
||||
|
||||
/// Minimum value: 1
|
||||
num keepLastAmount;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is DatabaseBackupConfig &&
|
||||
other.cronExpression == cronExpression &&
|
||||
other.enabled == enabled &&
|
||||
other.keepLastAmount == keepLastAmount;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(cronExpression.hashCode) +
|
||||
(enabled.hashCode) +
|
||||
(keepLastAmount.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'DatabaseBackupConfig[cronExpression=$cronExpression, enabled=$enabled, keepLastAmount=$keepLastAmount]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'cronExpression'] = this.cronExpression;
|
||||
json[r'enabled'] = this.enabled;
|
||||
json[r'keepLastAmount'] = this.keepLastAmount;
|
||||
return json;
|
||||
}
|
||||
|
||||
/// Returns a new [DatabaseBackupConfig] instance and imports its values from
|
||||
/// [value] if it's a [Map], null otherwise.
|
||||
// ignore: prefer_constructors_over_static_methods
|
||||
static DatabaseBackupConfig? fromJson(dynamic value) {
|
||||
upgradeDto(value, "DatabaseBackupConfig");
|
||||
if (value is Map) {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return DatabaseBackupConfig(
|
||||
cronExpression: mapValueOfType<String>(json, r'cronExpression')!,
|
||||
enabled: mapValueOfType<bool>(json, r'enabled')!,
|
||||
keepLastAmount: num.parse('${json[r'keepLastAmount']}'),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static List<DatabaseBackupConfig> listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <DatabaseBackupConfig>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = DatabaseBackupConfig.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toList(growable: growable);
|
||||
}
|
||||
|
||||
static Map<String, DatabaseBackupConfig> mapFromJson(dynamic json) {
|
||||
final map = <String, DatabaseBackupConfig>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||
for (final entry in json.entries) {
|
||||
final value = DatabaseBackupConfig.fromJson(entry.value);
|
||||
if (value != null) {
|
||||
map[entry.key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
// maps a json object with a list of DatabaseBackupConfig-objects as value to a dart map
|
||||
static Map<String, List<DatabaseBackupConfig>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||
final map = <String, List<DatabaseBackupConfig>>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
// ignore: parameter_assignments
|
||||
json = json.cast<String, dynamic>();
|
||||
for (final entry in json.entries) {
|
||||
map[entry.key] = DatabaseBackupConfig.listFromJson(entry.value, growable: growable,);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/// The list of required keys that must be present in a JSON.
|
||||
static const requiredKeys = <String>{
|
||||
'cronExpression',
|
||||
'enabled',
|
||||
'keepLastAmount',
|
||||
};
|
||||
}
|
||||
|
3
mobile/openapi/lib/model/job_name.dart
generated
3
mobile/openapi/lib/model/job_name.dart
generated
@ -37,6 +37,7 @@ class JobName {
|
||||
static const sidecar = JobName._(r'sidecar');
|
||||
static const library_ = JobName._(r'library');
|
||||
static const notifications = JobName._(r'notifications');
|
||||
static const backupDatabase = JobName._(r'backupDatabase');
|
||||
|
||||
/// List of all possible values in this [enum][JobName].
|
||||
static const values = <JobName>[
|
||||
@ -54,6 +55,7 @@ class JobName {
|
||||
sidecar,
|
||||
library_,
|
||||
notifications,
|
||||
backupDatabase,
|
||||
];
|
||||
|
||||
static JobName? fromJson(dynamic value) => JobNameTypeTransformer().decode(value);
|
||||
@ -106,6 +108,7 @@ class JobNameTypeTransformer {
|
||||
case r'sidecar': return JobName.sidecar;
|
||||
case r'library': return JobName.library_;
|
||||
case r'notifications': return JobName.notifications;
|
||||
case r'backupDatabase': return JobName.backupDatabase;
|
||||
default:
|
||||
if (!allowNull) {
|
||||
throw ArgumentError('Unknown enum value to decode: $data');
|
||||
|
99
mobile/openapi/lib/model/system_config_backups_dto.dart
generated
Normal file
99
mobile/openapi/lib/model/system_config_backups_dto.dart
generated
Normal file
@ -0,0 +1,99 @@
|
||||
//
|
||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||
//
|
||||
// @dart=2.18
|
||||
|
||||
// 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 SystemConfigBackupsDto {
|
||||
/// Returns a new [SystemConfigBackupsDto] instance.
|
||||
SystemConfigBackupsDto({
|
||||
required this.database,
|
||||
});
|
||||
|
||||
DatabaseBackupConfig database;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is SystemConfigBackupsDto &&
|
||||
other.database == database;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(database.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'SystemConfigBackupsDto[database=$database]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'database'] = this.database;
|
||||
return json;
|
||||
}
|
||||
|
||||
/// Returns a new [SystemConfigBackupsDto] instance and imports its values from
|
||||
/// [value] if it's a [Map], null otherwise.
|
||||
// ignore: prefer_constructors_over_static_methods
|
||||
static SystemConfigBackupsDto? fromJson(dynamic value) {
|
||||
upgradeDto(value, "SystemConfigBackupsDto");
|
||||
if (value is Map) {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return SystemConfigBackupsDto(
|
||||
database: DatabaseBackupConfig.fromJson(json[r'database'])!,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static List<SystemConfigBackupsDto> listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <SystemConfigBackupsDto>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = SystemConfigBackupsDto.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toList(growable: growable);
|
||||
}
|
||||
|
||||
static Map<String, SystemConfigBackupsDto> mapFromJson(dynamic json) {
|
||||
final map = <String, SystemConfigBackupsDto>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||
for (final entry in json.entries) {
|
||||
final value = SystemConfigBackupsDto.fromJson(entry.value);
|
||||
if (value != null) {
|
||||
map[entry.key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
// maps a json object with a list of SystemConfigBackupsDto-objects as value to a dart map
|
||||
static Map<String, List<SystemConfigBackupsDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||
final map = <String, List<SystemConfigBackupsDto>>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
// ignore: parameter_assignments
|
||||
json = json.cast<String, dynamic>();
|
||||
for (final entry in json.entries) {
|
||||
map[entry.key] = SystemConfigBackupsDto.listFromJson(entry.value, growable: growable,);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/// The list of required keys that must be present in a JSON.
|
||||
static const requiredKeys = <String>{
|
||||
'database',
|
||||
};
|
||||
}
|
||||
|
10
mobile/openapi/lib/model/system_config_dto.dart
generated
10
mobile/openapi/lib/model/system_config_dto.dart
generated
@ -13,6 +13,7 @@ part of openapi.api;
|
||||
class SystemConfigDto {
|
||||
/// Returns a new [SystemConfigDto] instance.
|
||||
SystemConfigDto({
|
||||
required this.backup,
|
||||
required this.ffmpeg,
|
||||
required this.image,
|
||||
required this.job,
|
||||
@ -33,6 +34,8 @@ class SystemConfigDto {
|
||||
required this.user,
|
||||
});
|
||||
|
||||
SystemConfigBackupsDto backup;
|
||||
|
||||
SystemConfigFFmpegDto ffmpeg;
|
||||
|
||||
SystemConfigImageDto image;
|
||||
@ -71,6 +74,7 @@ class SystemConfigDto {
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is SystemConfigDto &&
|
||||
other.backup == backup &&
|
||||
other.ffmpeg == ffmpeg &&
|
||||
other.image == image &&
|
||||
other.job == job &&
|
||||
@ -93,6 +97,7 @@ class SystemConfigDto {
|
||||
@override
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(backup.hashCode) +
|
||||
(ffmpeg.hashCode) +
|
||||
(image.hashCode) +
|
||||
(job.hashCode) +
|
||||
@ -113,10 +118,11 @@ class SystemConfigDto {
|
||||
(user.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'SystemConfigDto[ffmpeg=$ffmpeg, image=$image, job=$job, library_=$library_, logging=$logging, machineLearning=$machineLearning, map=$map, metadata=$metadata, newVersionCheck=$newVersionCheck, notifications=$notifications, oauth=$oauth, passwordLogin=$passwordLogin, reverseGeocoding=$reverseGeocoding, server=$server, storageTemplate=$storageTemplate, theme=$theme, trash=$trash, user=$user]';
|
||||
String toString() => 'SystemConfigDto[backup=$backup, ffmpeg=$ffmpeg, image=$image, job=$job, library_=$library_, logging=$logging, machineLearning=$machineLearning, map=$map, metadata=$metadata, newVersionCheck=$newVersionCheck, notifications=$notifications, oauth=$oauth, passwordLogin=$passwordLogin, reverseGeocoding=$reverseGeocoding, server=$server, storageTemplate=$storageTemplate, theme=$theme, trash=$trash, user=$user]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'backup'] = this.backup;
|
||||
json[r'ffmpeg'] = this.ffmpeg;
|
||||
json[r'image'] = this.image;
|
||||
json[r'job'] = this.job;
|
||||
@ -147,6 +153,7 @@ class SystemConfigDto {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return SystemConfigDto(
|
||||
backup: SystemConfigBackupsDto.fromJson(json[r'backup'])!,
|
||||
ffmpeg: SystemConfigFFmpegDto.fromJson(json[r'ffmpeg'])!,
|
||||
image: SystemConfigImageDto.fromJson(json[r'image'])!,
|
||||
job: SystemConfigJobDto.fromJson(json[r'job'])!,
|
||||
@ -212,6 +219,7 @@ class SystemConfigDto {
|
||||
|
||||
/// The list of required keys that must be present in a JSON.
|
||||
static const requiredKeys = <String>{
|
||||
'backup',
|
||||
'ffmpeg',
|
||||
'image',
|
||||
'job',
|
||||
|
@ -23,7 +23,6 @@ class SystemConfigFFmpegDto {
|
||||
required this.crf,
|
||||
required this.gopSize,
|
||||
required this.maxBitrate,
|
||||
required this.npl,
|
||||
required this.preferredHwDevice,
|
||||
required this.preset,
|
||||
required this.refs,
|
||||
@ -62,9 +61,6 @@ class SystemConfigFFmpegDto {
|
||||
|
||||
String maxBitrate;
|
||||
|
||||
/// Minimum value: 0
|
||||
int npl;
|
||||
|
||||
String preferredHwDevice;
|
||||
|
||||
String preset;
|
||||
@ -102,7 +98,6 @@ class SystemConfigFFmpegDto {
|
||||
other.crf == crf &&
|
||||
other.gopSize == gopSize &&
|
||||
other.maxBitrate == maxBitrate &&
|
||||
other.npl == npl &&
|
||||
other.preferredHwDevice == preferredHwDevice &&
|
||||
other.preset == preset &&
|
||||
other.refs == refs &&
|
||||
@ -128,7 +123,6 @@ class SystemConfigFFmpegDto {
|
||||
(crf.hashCode) +
|
||||
(gopSize.hashCode) +
|
||||
(maxBitrate.hashCode) +
|
||||
(npl.hashCode) +
|
||||
(preferredHwDevice.hashCode) +
|
||||
(preset.hashCode) +
|
||||
(refs.hashCode) +
|
||||
@ -142,7 +136,7 @@ class SystemConfigFFmpegDto {
|
||||
(twoPass.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'SystemConfigFFmpegDto[accel=$accel, accelDecode=$accelDecode, acceptedAudioCodecs=$acceptedAudioCodecs, acceptedContainers=$acceptedContainers, acceptedVideoCodecs=$acceptedVideoCodecs, bframes=$bframes, cqMode=$cqMode, crf=$crf, gopSize=$gopSize, maxBitrate=$maxBitrate, npl=$npl, preferredHwDevice=$preferredHwDevice, preset=$preset, refs=$refs, targetAudioCodec=$targetAudioCodec, targetResolution=$targetResolution, targetVideoCodec=$targetVideoCodec, temporalAQ=$temporalAQ, threads=$threads, tonemap=$tonemap, transcode=$transcode, twoPass=$twoPass]';
|
||||
String toString() => 'SystemConfigFFmpegDto[accel=$accel, accelDecode=$accelDecode, acceptedAudioCodecs=$acceptedAudioCodecs, acceptedContainers=$acceptedContainers, acceptedVideoCodecs=$acceptedVideoCodecs, bframes=$bframes, cqMode=$cqMode, crf=$crf, gopSize=$gopSize, maxBitrate=$maxBitrate, preferredHwDevice=$preferredHwDevice, preset=$preset, refs=$refs, targetAudioCodec=$targetAudioCodec, targetResolution=$targetResolution, targetVideoCodec=$targetVideoCodec, temporalAQ=$temporalAQ, threads=$threads, tonemap=$tonemap, transcode=$transcode, twoPass=$twoPass]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
@ -156,7 +150,6 @@ class SystemConfigFFmpegDto {
|
||||
json[r'crf'] = this.crf;
|
||||
json[r'gopSize'] = this.gopSize;
|
||||
json[r'maxBitrate'] = this.maxBitrate;
|
||||
json[r'npl'] = this.npl;
|
||||
json[r'preferredHwDevice'] = this.preferredHwDevice;
|
||||
json[r'preset'] = this.preset;
|
||||
json[r'refs'] = this.refs;
|
||||
@ -190,7 +183,6 @@ class SystemConfigFFmpegDto {
|
||||
crf: mapValueOfType<int>(json, r'crf')!,
|
||||
gopSize: mapValueOfType<int>(json, r'gopSize')!,
|
||||
maxBitrate: mapValueOfType<String>(json, r'maxBitrate')!,
|
||||
npl: mapValueOfType<int>(json, r'npl')!,
|
||||
preferredHwDevice: mapValueOfType<String>(json, r'preferredHwDevice')!,
|
||||
preset: mapValueOfType<String>(json, r'preset')!,
|
||||
refs: mapValueOfType<int>(json, r'refs')!,
|
||||
@ -259,7 +251,6 @@ class SystemConfigFFmpegDto {
|
||||
'crf',
|
||||
'gopSize',
|
||||
'maxBitrate',
|
||||
'npl',
|
||||
'preferredHwDevice',
|
||||
'preset',
|
||||
'refs',
|
||||
|
@ -548,10 +548,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_local_notifications
|
||||
sha256: dd6676d8c2926537eccdf9f72128bbb2a9d0814689527b17f92c248ff192eaf3
|
||||
sha256: "674173fd3c9eda9d4c8528da2ce0ea69f161577495a9cc835a2a4ecd7eadeb35"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "17.2.1+2"
|
||||
version: "17.2.4"
|
||||
flutter_local_notifications_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -622,10 +622,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_web_auth
|
||||
sha256: a69fa8f43b9e4d86ac72176bf747b735e7b977dd7cf215076d95b87cb05affdd
|
||||
sha256: "95e4856e24fb6ac1678f5ff334743b63f782d839ab324543d29ccbd295176209"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.0"
|
||||
version: "0.6.0"
|
||||
flutter_web_plugins:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
@ -876,26 +876,26 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: isar
|
||||
sha256: "99165dadb2cf2329d3140198363a7e7bff9bbd441871898a87e26914d25cf1ea"
|
||||
url: "https://pub.dev"
|
||||
sha256: e17a9555bc7f22ff26568b8c64d019b4ffa2dc6bd4cb1c8d9b269aefd32e53ad
|
||||
url: "https://pub.isar-community.dev"
|
||||
source: hosted
|
||||
version: "3.1.0+1"
|
||||
version: "3.1.8"
|
||||
isar_flutter_libs:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: isar_flutter_libs
|
||||
sha256: bc6768cc4b9c61aabff77152e7f33b4b17d2fc93134f7af1c3dd51500fe8d5e8
|
||||
url: "https://pub.dev"
|
||||
sha256: "78710781e658ce4bff59b3f38c5b2735e899e627f4e926e1221934e77b95231a"
|
||||
url: "https://pub.isar-community.dev"
|
||||
source: hosted
|
||||
version: "3.1.0+1"
|
||||
version: "3.1.8"
|
||||
isar_generator:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: isar_generator
|
||||
sha256: "76c121e1295a30423604f2f819bc255bc79f852f3bc8743a24017df6068ad133"
|
||||
url: "https://pub.dev"
|
||||
sha256: "484e73d3b7e81dbd816852fe0b9497333118a9aeb646fd2d349a62cc8980ffe1"
|
||||
url: "https://pub.isar-community.dev"
|
||||
source: hosted
|
||||
version: "3.1.0+1"
|
||||
version: "3.1.8"
|
||||
js:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -1211,10 +1211,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: photo_manager
|
||||
sha256: "70159eee32203e8162d49d588232f0299ed3f383c63eef1e899cb6b83dee6b26"
|
||||
sha256: f5ef2618870e9a50d8bfeb81a02c242d580ae8614bd5ea9e1b80dbb7e49d4260
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.5.1"
|
||||
version: "3.6.1"
|
||||
photo_manager_image_provider:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -1712,18 +1712,18 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: video_player
|
||||
sha256: e30df0d226c4ef82e2c150ebf6834b3522cf3f654d8e2f9419d376cdc071425d
|
||||
sha256: "4a8c3492d734f7c39c2588a3206707a05ee80cef52e8c7f3b2078d430c84bc17"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.9.1"
|
||||
version: "2.9.2"
|
||||
video_player_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: video_player_android
|
||||
sha256: "4de50df9ee786f5891d3281e1e633d7b142ef1acf47392592eb91cba5d355849"
|
||||
sha256: "391e092ba4abe2f93b3e625bd6b6a6ec7d7414279462c1c0ee42b5ab8d0a0898"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.6.0"
|
||||
version: "2.7.16"
|
||||
video_player_avfoundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -1861,5 +1861,5 @@ packages:
|
||||
source: hosted
|
||||
version: "3.1.2"
|
||||
sdks:
|
||||
dart: ">=3.5.0 <4.0.0"
|
||||
flutter: ">=3.24.3"
|
||||
dart: ">=3.5.3 <4.0.0"
|
||||
flutter: ">=3.24.4"
|
||||
|
@ -6,14 +6,16 @@ version: 1.119.1+164
|
||||
|
||||
environment:
|
||||
sdk: '>=3.3.0 <4.0.0'
|
||||
flutter: 3.24.3
|
||||
flutter: 3.24.4
|
||||
|
||||
isar_version: &isar_version 3.1.8 # define the version to be used
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
|
||||
path_provider_ios:
|
||||
photo_manager: ^3.5.1
|
||||
photo_manager: ^3.6.1
|
||||
photo_manager_image_provider: ^2.2.0
|
||||
flutter_hooks: ^0.20.4
|
||||
hooks_riverpod: ^2.4.9
|
||||
@ -23,7 +25,7 @@ dependencies:
|
||||
intl: ^0.19.0
|
||||
auto_route: ^9.2.0
|
||||
fluttertoast: ^8.2.4
|
||||
video_player: ^2.8.2
|
||||
video_player: ^2.9.2
|
||||
chewie: ^1.7.4
|
||||
socket_io_client: ^2.0.3+1
|
||||
maplibre_gl: 0.19.0+2
|
||||
@ -42,10 +44,14 @@ dependencies:
|
||||
path_provider: ^2.1.2
|
||||
collection: ^1.18.0
|
||||
http_parser: ^4.0.2
|
||||
flutter_web_auth: ^0.5.0
|
||||
flutter_web_auth: ^0.6.0
|
||||
easy_image_viewer: ^1.4.0
|
||||
isar: ^3.1.0+1
|
||||
isar_flutter_libs: ^3.1.0+1
|
||||
isar:
|
||||
version: *isar_version
|
||||
hosted: https://pub.isar-community.dev/
|
||||
isar_flutter_libs: # contains Isar Core
|
||||
version: *isar_version
|
||||
hosted: https://pub.isar-community.dev/
|
||||
permission_handler: ^11.2.0
|
||||
device_info_plus: ^11.0.0
|
||||
connectivity_plus: ^6.0.0
|
||||
@ -92,7 +98,9 @@ dev_dependencies:
|
||||
auto_route_generator: ^9.0.0
|
||||
flutter_launcher_icons: ^0.14.0
|
||||
flutter_native_splash: ^2.3.9
|
||||
isar_generator: ^3.1.0+1
|
||||
isar_generator:
|
||||
version: *isar_version
|
||||
hosted: https://pub.isar-community.dev/
|
||||
integration_test:
|
||||
sdk: flutter
|
||||
custom_lint: ^0.6.4
|
||||
|
@ -7745,6 +7745,9 @@
|
||||
"backgroundTask": {
|
||||
"$ref": "#/components/schemas/JobStatusDto"
|
||||
},
|
||||
"backupDatabase": {
|
||||
"$ref": "#/components/schemas/JobStatusDto"
|
||||
},
|
||||
"duplicateDetection": {
|
||||
"$ref": "#/components/schemas/JobStatusDto"
|
||||
},
|
||||
@ -7787,6 +7790,7 @@
|
||||
},
|
||||
"required": [
|
||||
"backgroundTask",
|
||||
"backupDatabase",
|
||||
"duplicateDetection",
|
||||
"faceDetection",
|
||||
"facialRecognition",
|
||||
@ -8754,6 +8758,26 @@
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"DatabaseBackupConfig": {
|
||||
"properties": {
|
||||
"cronExpression": {
|
||||
"type": "string"
|
||||
},
|
||||
"enabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"keepLastAmount": {
|
||||
"minimum": 1,
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"cronExpression",
|
||||
"enabled",
|
||||
"keepLastAmount"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"DownloadArchiveInfo": {
|
||||
"properties": {
|
||||
"assetIds": {
|
||||
@ -9289,7 +9313,8 @@
|
||||
"search",
|
||||
"sidecar",
|
||||
"library",
|
||||
"notifications"
|
||||
"notifications",
|
||||
"backupDatabase"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
@ -11456,8 +11481,22 @@
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"SystemConfigBackupsDto": {
|
||||
"properties": {
|
||||
"database": {
|
||||
"$ref": "#/components/schemas/DatabaseBackupConfig"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"database"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"SystemConfigDto": {
|
||||
"properties": {
|
||||
"backup": {
|
||||
"$ref": "#/components/schemas/SystemConfigBackupsDto"
|
||||
},
|
||||
"ffmpeg": {
|
||||
"$ref": "#/components/schemas/SystemConfigFFmpegDto"
|
||||
},
|
||||
@ -11517,6 +11556,7 @@
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"backup",
|
||||
"ffmpeg",
|
||||
"image",
|
||||
"job",
|
||||
@ -11585,10 +11625,6 @@
|
||||
"maxBitrate": {
|
||||
"type": "string"
|
||||
},
|
||||
"npl": {
|
||||
"minimum": 0,
|
||||
"type": "integer"
|
||||
},
|
||||
"preferredHwDevice": {
|
||||
"type": "string"
|
||||
},
|
||||
@ -11637,7 +11673,6 @@
|
||||
"crf",
|
||||
"gopSize",
|
||||
"maxBitrate",
|
||||
"npl",
|
||||
"preferredHwDevice",
|
||||
"preset",
|
||||
"refs",
|
||||
|
@ -1 +1 @@
|
||||
20.18.0
|
||||
22.11.0
|
||||
|
10
open-api/typescript-sdk/package-lock.json
generated
10
open-api/typescript-sdk/package-lock.json
generated
@ -12,7 +12,7 @@
|
||||
"@oazapfts/runtime": "^1.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.17.0",
|
||||
"@types/node": "^22.8.6",
|
||||
"typescript": "^5.3.3"
|
||||
}
|
||||
},
|
||||
@ -22,13 +22,13 @@
|
||||
"integrity": "sha512-8tKiYffhwTGHSHYGnZ3oneLGCjX0po/XAXQ5Ng9fqKkvIdl/xz8+Vh8i+6xjzZqvZ2pLVpUcuSfnvNI/x67L0g=="
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "20.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.2.tgz",
|
||||
"integrity": "sha512-OOHK4sjXqkL7yQ7VEEHcf6+0jSvKjWqwnaCtY7AKD/VLEvRHMsxxu7eI8ErnjxHS8VwmekD4PeVCpu4qZEZSxg==",
|
||||
"version": "22.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz",
|
||||
"integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~6.19.2"
|
||||
"undici-types": "~6.19.8"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
|
@ -19,7 +19,7 @@
|
||||
"@oazapfts/runtime": "^1.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.17.0",
|
||||
"@types/node": "^22.8.6",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"repository": {
|
||||
@ -28,6 +28,6 @@
|
||||
"directory": "open-api/typescript-sdk"
|
||||
},
|
||||
"volta": {
|
||||
"node": "20.18.0"
|
||||
"node": "22.11.0"
|
||||
}
|
||||
}
|
||||
|
@ -535,6 +535,7 @@ export type JobStatusDto = {
|
||||
};
|
||||
export type AllJobStatusResponseDto = {
|
||||
backgroundTask: JobStatusDto;
|
||||
backupDatabase: JobStatusDto;
|
||||
duplicateDetection: JobStatusDto;
|
||||
faceDetection: JobStatusDto;
|
||||
facialRecognition: JobStatusDto;
|
||||
@ -1084,6 +1085,14 @@ export type AssetFullSyncDto = {
|
||||
updatedUntil: string;
|
||||
userId?: string;
|
||||
};
|
||||
export type DatabaseBackupConfig = {
|
||||
cronExpression: string;
|
||||
enabled: boolean;
|
||||
keepLastAmount: number;
|
||||
};
|
||||
export type SystemConfigBackupsDto = {
|
||||
database: DatabaseBackupConfig;
|
||||
};
|
||||
export type SystemConfigFFmpegDto = {
|
||||
accel: TranscodeHWAccel;
|
||||
accelDecode: boolean;
|
||||
@ -1095,7 +1104,6 @@ export type SystemConfigFFmpegDto = {
|
||||
crf: number;
|
||||
gopSize: number;
|
||||
maxBitrate: string;
|
||||
npl: number;
|
||||
preferredHwDevice: string;
|
||||
preset: string;
|
||||
refs: number;
|
||||
@ -1240,6 +1248,7 @@ export type SystemConfigUserDto = {
|
||||
deleteDelay: number;
|
||||
};
|
||||
export type SystemConfigDto = {
|
||||
backup: SystemConfigBackupsDto;
|
||||
ffmpeg: SystemConfigFFmpegDto;
|
||||
image: SystemConfigImageDto;
|
||||
job: SystemConfigJobDto;
|
||||
@ -3454,7 +3463,8 @@ export enum JobName {
|
||||
Search = "search",
|
||||
Sidecar = "sidecar",
|
||||
Library = "library",
|
||||
Notifications = "notifications"
|
||||
Notifications = "notifications",
|
||||
BackupDatabase = "backupDatabase"
|
||||
}
|
||||
export enum JobCommand {
|
||||
Start = "start",
|
||||
|
@ -32,6 +32,7 @@
|
||||
<a href="README_pt_BR.md">Português Brasileiro</a>
|
||||
<a href="README_sv_SE.md">Svenska</a>
|
||||
<a href="README_ar_JO.md">العربية</a>
|
||||
<a href="README_vi_VN.md">Tiếng Việt</a>
|
||||
<a href="README_th_TH.md">ภาษาไทย</a>
|
||||
</p>
|
||||
|
||||
@ -102,6 +103,8 @@ Für die Handy-App kannst Du `https://demo.immich.app/api` als `Server Endpoint
|
||||
| Offline Unterstützung | Ja | Nein |
|
||||
| Schreibgeschützte Gallerie | Ja | Ja |
|
||||
| Gestapelte Bilder | Ja | Ja |
|
||||
| Tags | Nein | Ja |
|
||||
| Ordner-Ansicht | Nein | Ja |
|
||||
|
||||
|
||||
## Übersetzungen
|
||||
|
@ -1 +1 @@
|
||||
20.18.0
|
||||
22.11.0
|
||||
|
@ -1,5 +1,5 @@
|
||||
# dev build
|
||||
FROM ghcr.io/immich-app/base-server-dev:20241025@sha256:a55edebdb00f2cd034f99245612833ae4fbb05745f4b1503b8fc97f6a4311502 AS dev
|
||||
FROM ghcr.io/immich-app/base-server-dev:20241029@sha256:31c2d6cba42253680ff7738dffa66e97dfe5b1c7faf23b29d07ab456e9c1e2b9 AS dev
|
||||
|
||||
RUN apt-get install --no-install-recommends -yqq tini
|
||||
WORKDIR /usr/src/app
|
||||
@ -25,7 +25,7 @@ COPY --from=dev /usr/src/app/node_modules/@img ./node_modules/@img
|
||||
COPY --from=dev /usr/src/app/node_modules/exiftool-vendored.pl ./node_modules/exiftool-vendored.pl
|
||||
|
||||
# web build
|
||||
FROM node:20.18.0-alpine3.20@sha256:c13b26e7e602ef2f1074aef304ce6e9b7dd284c419b35d89fcf3cc8e44a8def9 AS web
|
||||
FROM node:22.11.0-alpine3.20@sha256:f265794478aa0b1a23d85a492c8311ed795bc527c3fe7e43453b3c872dcd71a3 AS web
|
||||
|
||||
WORKDIR /usr/src/open-api/typescript-sdk
|
||||
COPY open-api/typescript-sdk/package*.json open-api/typescript-sdk/tsconfig*.json ./
|
||||
@ -42,7 +42,7 @@ RUN npm run build
|
||||
|
||||
|
||||
# prod build
|
||||
FROM ghcr.io/immich-app/base-server-prod:20241025@sha256:ee25f3135ee6e70810c810cd9789068594ffb1a48e7c28fe77303553e904979e
|
||||
FROM ghcr.io/immich-app/base-server-prod:20241029@sha256:669cc57091e3d2924b9e3001acb4998ed9c9585370017adcdf7beed21d249aae
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
ENV NODE_ENV=production \
|
||||
|
3309
server/package-lock.json
generated
3309
server/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -36,7 +36,6 @@
|
||||
"dependencies": {
|
||||
"@nestjs/bullmq": "^10.0.1",
|
||||
"@nestjs/common": "^10.2.2",
|
||||
"@nestjs/config": "^3.0.0",
|
||||
"@nestjs/core": "^10.2.2",
|
||||
"@nestjs/event-emitter": "^2.0.4",
|
||||
"@nestjs/platform-express": "^10.2.2",
|
||||
@ -45,10 +44,10 @@
|
||||
"@nestjs/swagger": "^7.1.8",
|
||||
"@nestjs/typeorm": "^10.0.0",
|
||||
"@nestjs/websockets": "^10.2.2",
|
||||
"@opentelemetry/auto-instrumentations-node": "^0.51.0",
|
||||
"@opentelemetry/auto-instrumentations-node": "^0.52.0",
|
||||
"@opentelemetry/context-async-hooks": "^1.24.0",
|
||||
"@opentelemetry/exporter-prometheus": "^0.53.0",
|
||||
"@opentelemetry/sdk-node": "^0.53.0",
|
||||
"@opentelemetry/exporter-prometheus": "^0.54.0",
|
||||
"@opentelemetry/sdk-node": "^0.54.0",
|
||||
"@react-email/components": "^0.0.25",
|
||||
"@socket.io/redis-adapter": "^8.3.0",
|
||||
"archiver": "^7.0.0",
|
||||
@ -88,7 +87,8 @@
|
||||
"tailwindcss-preset-email": "^1.3.2",
|
||||
"thumbhash": "^0.1.1",
|
||||
"typeorm": "^0.3.17",
|
||||
"ua-parser-js": "^1.0.35"
|
||||
"ua-parser-js": "^1.0.35",
|
||||
"validator": "^13.12.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/eslintrc": "^3.1.0",
|
||||
@ -108,7 +108,7 @@
|
||||
"@types/lodash": "^4.14.197",
|
||||
"@types/mock-fs": "^4.13.1",
|
||||
"@types/multer": "^1.4.7",
|
||||
"@types/node": "^20.17.0",
|
||||
"@types/node": "^22.8.6",
|
||||
"@types/nodemailer": "^6.4.14",
|
||||
"@types/picomatch": "^3.0.0",
|
||||
"@types/pngjs": "^6.0.5",
|
||||
@ -139,6 +139,6 @@
|
||||
"vitest": "^2.0.5"
|
||||
},
|
||||
"volta": {
|
||||
"node": "20.18.0"
|
||||
"node": "22.11.0"
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,17 @@
|
||||
import { BullModule } from '@nestjs/bullmq';
|
||||
import { Inject, Module, OnModuleDestroy, OnModuleInit, ValidationPipe } from '@nestjs/common';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { APP_FILTER, APP_GUARD, APP_INTERCEPTOR, APP_PIPE, ModuleRef } from '@nestjs/core';
|
||||
import { ScheduleModule, SchedulerRegistry } from '@nestjs/schedule';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { ClsModule } from 'nestjs-cls';
|
||||
import { OpenTelemetryModule } from 'nestjs-otel';
|
||||
import { commands } from 'src/commands';
|
||||
import { clsConfig, immichAppConfig } from 'src/config';
|
||||
import { IWorker } from 'src/constants';
|
||||
import { controllers } from 'src/controllers';
|
||||
import { entities } from 'src/entities';
|
||||
import { ImmichWorker } from 'src/enum';
|
||||
import { IEventRepository } from 'src/interfaces/event.interface';
|
||||
import { IJobRepository } from 'src/interfaces/job.interface';
|
||||
import { ILoggerRepository } from 'src/interfaces/logger.interface';
|
||||
import { ITelemetryRepository } from 'src/interfaces/telemetry.interface';
|
||||
import { AuthGuard } from 'src/middleware/auth.guard';
|
||||
@ -37,13 +37,12 @@ const middleware = [
|
||||
];
|
||||
|
||||
const configRepository = new ConfigRepository();
|
||||
const { bull, database, otel } = configRepository.getEnv();
|
||||
const { bull, cls, database, otel } = configRepository.getEnv();
|
||||
|
||||
const imports = [
|
||||
BullModule.forRoot(bull.config),
|
||||
BullModule.registerQueue(...bull.queues),
|
||||
ClsModule.forRoot(clsConfig),
|
||||
ConfigModule.forRoot(immichAppConfig),
|
||||
ClsModule.forRoot(cls.config),
|
||||
OpenTelemetryModule.forRoot(otel),
|
||||
TypeOrmModule.forRootAsync({
|
||||
inject: [ModuleRef],
|
||||
@ -59,29 +58,31 @@ const imports = [
|
||||
TypeOrmModule.forFeature(entities),
|
||||
];
|
||||
|
||||
abstract class BaseModule implements OnModuleInit, OnModuleDestroy {
|
||||
private get worker() {
|
||||
return this.getWorker();
|
||||
}
|
||||
|
||||
class BaseModule implements OnModuleInit, OnModuleDestroy {
|
||||
constructor(
|
||||
@Inject(IWorker) private worker: ImmichWorker,
|
||||
@Inject(ILoggerRepository) logger: ILoggerRepository,
|
||||
@Inject(IEventRepository) private eventRepository: IEventRepository,
|
||||
@Inject(IJobRepository) private jobRepository: IJobRepository,
|
||||
@Inject(ITelemetryRepository) private telemetryRepository: ITelemetryRepository,
|
||||
) {
|
||||
logger.setAppName(this.worker);
|
||||
}
|
||||
|
||||
abstract getWorker(): ImmichWorker;
|
||||
|
||||
async onModuleInit() {
|
||||
this.telemetryRepository.setup({ repositories: repositories.map(({ useClass }) => useClass) });
|
||||
|
||||
this.jobRepository.setup({ services });
|
||||
if (this.worker === ImmichWorker.MICROSERVICES) {
|
||||
this.jobRepository.startWorkers();
|
||||
}
|
||||
|
||||
this.eventRepository.setup({ services });
|
||||
await this.eventRepository.emit('app.bootstrap', this.worker);
|
||||
await this.eventRepository.emit('app.bootstrap');
|
||||
}
|
||||
|
||||
async onModuleDestroy() {
|
||||
await this.eventRepository.emit('app.shutdown', this.worker);
|
||||
await this.eventRepository.emit('app.shutdown');
|
||||
await teardownTelemetry();
|
||||
}
|
||||
}
|
||||
@ -89,23 +90,15 @@ abstract class BaseModule implements OnModuleInit, OnModuleDestroy {
|
||||
@Module({
|
||||
imports: [...imports, ScheduleModule.forRoot()],
|
||||
controllers: [...controllers],
|
||||
providers: [...common, ...middleware],
|
||||
providers: [...common, ...middleware, { provide: IWorker, useValue: ImmichWorker.API }],
|
||||
})
|
||||
export class ApiModule extends BaseModule {
|
||||
getWorker() {
|
||||
return ImmichWorker.API;
|
||||
}
|
||||
}
|
||||
export class ApiModule extends BaseModule {}
|
||||
|
||||
@Module({
|
||||
imports: [...imports],
|
||||
providers: [...common, SchedulerRegistry],
|
||||
providers: [...common, { provide: IWorker, useValue: ImmichWorker.MICROSERVICES }, SchedulerRegistry],
|
||||
})
|
||||
export class MicroservicesModule extends BaseModule {
|
||||
getWorker() {
|
||||
return ImmichWorker.MICROSERVICES;
|
||||
}
|
||||
}
|
||||
export class MicroservicesModule extends BaseModule {}
|
||||
|
||||
@Module({
|
||||
imports: [...imports],
|
||||
|
@ -3,7 +3,7 @@ import { ImmichWorker } from 'src/enum';
|
||||
import { ConfigRepository } from 'src/repositories/config.repository';
|
||||
|
||||
const main = async () => {
|
||||
const { workers, port } = new ConfigRepository().getEnv();
|
||||
const { host, workers, port } = new ConfigRepository().getEnv();
|
||||
if (!workers.includes(ImmichWorker.API)) {
|
||||
process.exit();
|
||||
}
|
||||
@ -11,7 +11,7 @@ const main = async () => {
|
||||
const controller = new AbortController();
|
||||
const timeout = setTimeout(() => controller.abort(), 2000);
|
||||
try {
|
||||
const response = await fetch(`http://localhost:${port}/api/server/ping`, {
|
||||
const response = await fetch(`http://${host || 'localhost'}:${port}/api/server/ping`, {
|
||||
signal: controller.signal,
|
||||
});
|
||||
|
||||
|
@ -1,8 +1,4 @@
|
||||
import { ConfigModuleOptions } from '@nestjs/config';
|
||||
import { CronExpression } from '@nestjs/schedule';
|
||||
import { Request, Response } from 'express';
|
||||
import Joi, { Root } from 'joi';
|
||||
import { CLS_ID, ClsModuleOptions } from 'nestjs-cls';
|
||||
import { albumInviteTemplate } from 'src/emails/templates/album-invite';
|
||||
import { albumUpdateTemplate } from 'src/emails/templates/album-update';
|
||||
import { welcomeTemplate } from 'src/emails/templates/welcome';
|
||||
@ -11,8 +7,6 @@ import {
|
||||
Colorspace,
|
||||
CQMode,
|
||||
ImageFormat,
|
||||
ImmichEnvironment,
|
||||
ImmichHeader,
|
||||
LogLevel,
|
||||
ToneMapping,
|
||||
TranscodeHWAccel,
|
||||
@ -24,6 +18,13 @@ import { ConcurrentQueueName, QueueName } from 'src/interfaces/job.interface';
|
||||
import { ImageOptions } from 'src/interfaces/media.interface';
|
||||
|
||||
export interface SystemConfig {
|
||||
backup: {
|
||||
database: {
|
||||
enabled: boolean;
|
||||
cronExpression: string;
|
||||
keepLastAmount: number;
|
||||
};
|
||||
};
|
||||
ffmpeg: {
|
||||
crf: number;
|
||||
threads: number;
|
||||
@ -38,7 +39,6 @@ export interface SystemConfig {
|
||||
bframes: number;
|
||||
refs: number;
|
||||
gopSize: number;
|
||||
npl: number;
|
||||
temporalAQ: boolean;
|
||||
cqMode: CQMode;
|
||||
twoPass: boolean;
|
||||
@ -166,6 +166,13 @@ export interface SystemConfig {
|
||||
}
|
||||
|
||||
export const defaults = Object.freeze<SystemConfig>({
|
||||
backup: {
|
||||
database: {
|
||||
enabled: true,
|
||||
cronExpression: CronExpression.EVERY_DAY_AT_2AM,
|
||||
keepLastAmount: 14,
|
||||
},
|
||||
},
|
||||
ffmpeg: {
|
||||
crf: 23,
|
||||
threads: 0,
|
||||
@ -180,7 +187,6 @@ export const defaults = Object.freeze<SystemConfig>({
|
||||
bframes: -1,
|
||||
refs: 0,
|
||||
gopSize: 0,
|
||||
npl: 0,
|
||||
temporalAQ: false,
|
||||
cqMode: CQMode.AUTO,
|
||||
twoPass: false,
|
||||
@ -326,67 +332,3 @@ export const defaults = Object.freeze<SystemConfig>({
|
||||
deleteDelay: 7,
|
||||
},
|
||||
});
|
||||
|
||||
const WHEN_DB_URL_SET = Joi.when('DB_URL', {
|
||||
is: Joi.exist(),
|
||||
then: Joi.string().optional(),
|
||||
otherwise: Joi.string().required(),
|
||||
});
|
||||
|
||||
export const immichAppConfig: ConfigModuleOptions = {
|
||||
envFilePath: '.env',
|
||||
isGlobal: true,
|
||||
validationSchema: Joi.object({
|
||||
IMMICH_ENV: Joi.string()
|
||||
.optional()
|
||||
.valid(...Object.values(ImmichEnvironment))
|
||||
.default(ImmichEnvironment.PRODUCTION),
|
||||
IMMICH_LOG_LEVEL: Joi.string()
|
||||
.optional()
|
||||
.valid(...Object.values(LogLevel)),
|
||||
|
||||
DB_USERNAME: WHEN_DB_URL_SET,
|
||||
DB_PASSWORD: WHEN_DB_URL_SET,
|
||||
DB_DATABASE_NAME: WHEN_DB_URL_SET,
|
||||
DB_URL: Joi.string().optional(),
|
||||
DB_VECTOR_EXTENSION: Joi.string().optional().valid('pgvector', 'pgvecto.rs').default('pgvecto.rs'),
|
||||
DB_SKIP_MIGRATIONS: Joi.boolean().optional().default(false),
|
||||
|
||||
IMMICH_PORT: Joi.number().optional(),
|
||||
IMMICH_API_METRICS_PORT: Joi.number().optional(),
|
||||
IMMICH_MICROSERVICES_METRICS_PORT: Joi.number().optional(),
|
||||
|
||||
IMMICH_TRUSTED_PROXIES: Joi.extend((joi: Root) => ({
|
||||
type: 'stringArray',
|
||||
base: joi.array(),
|
||||
coerce: (value) => (value.split ? value.split(',') : value),
|
||||
}))
|
||||
.stringArray()
|
||||
.single()
|
||||
.items(
|
||||
Joi.string().ip({
|
||||
version: ['ipv4', 'ipv6'],
|
||||
cidr: 'optional',
|
||||
}),
|
||||
),
|
||||
|
||||
IMMICH_METRICS: Joi.boolean().optional().default(false),
|
||||
IMMICH_HOST_METRICS: Joi.boolean().optional(),
|
||||
IMMICH_API_METRICS: Joi.boolean().optional(),
|
||||
IMMICH_IO_METRICS: Joi.boolean().optional(),
|
||||
}),
|
||||
};
|
||||
|
||||
export const clsConfig: ClsModuleOptions = {
|
||||
middleware: {
|
||||
mount: true,
|
||||
generateId: true,
|
||||
setup: (cls, req: Request, res: Response) => {
|
||||
const headerValues = req.headers[ImmichHeader.CID];
|
||||
const headerValue = Array.isArray(headerValues) ? headerValues[0] : headerValues;
|
||||
const cid = headerValue || cls.get(CLS_ID);
|
||||
cls.set(CLS_ID, cid);
|
||||
res.header(ImmichHeader.CID, cid);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -13,6 +13,8 @@ export const ADDED_IN_PREFIX = 'This property was added in ';
|
||||
|
||||
export const SALT_ROUNDS = 10;
|
||||
|
||||
export const IWorker = 'IWorker';
|
||||
|
||||
const { version } = JSON.parse(readFileSync('./package.json', 'utf8'));
|
||||
export const serverVersion = new SemVer(version);
|
||||
|
||||
|
@ -4,6 +4,7 @@ import _ from 'lodash';
|
||||
import { ADDED_IN_PREFIX, DEPRECATED_IN_PREFIX, LIFECYCLE_EXTENSION } from 'src/constants';
|
||||
import { MetadataKey } from 'src/enum';
|
||||
import { EmitEvent } from 'src/interfaces/event.interface';
|
||||
import { JobName, QueueName } from 'src/interfaces/job.interface';
|
||||
import { setUnion } from 'src/utils/set';
|
||||
|
||||
// PostgreSQL uses a 16-bit integer to indicate the number of bound parameters. This means that the
|
||||
@ -122,6 +123,12 @@ export type EventConfig = {
|
||||
};
|
||||
export const OnEvent = (config: EventConfig) => SetMetadata(MetadataKey.EVENT_CONFIG, config);
|
||||
|
||||
export type JobConfig = {
|
||||
name: JobName;
|
||||
queue: QueueName;
|
||||
};
|
||||
export const OnJob = (config: JobConfig) => SetMetadata(MetadataKey.JOB_CONFIG, config);
|
||||
|
||||
type LifecycleRelease = 'NEXT_RELEASE' | string;
|
||||
type LifecycleMetadata = {
|
||||
addedAt?: LifecycleRelease;
|
||||
|
190
server/src/dtos/env.dto.ts
Normal file
190
server/src/dtos/env.dto.ts
Normal file
@ -0,0 +1,190 @@
|
||||
import { Transform, Type } from 'class-transformer';
|
||||
import { IsEnum, IsInt, IsString } from 'class-validator';
|
||||
import { ImmichEnvironment, LogLevel } from 'src/enum';
|
||||
import { IsIPRange, Optional, ValidateBoolean } from 'src/validation';
|
||||
|
||||
export class EnvDto {
|
||||
@IsInt()
|
||||
@Optional()
|
||||
@Type(() => Number)
|
||||
IMMICH_API_METRICS_PORT?: number;
|
||||
|
||||
@IsString()
|
||||
@Optional()
|
||||
IMMICH_BUILD_DATA?: string;
|
||||
|
||||
@IsString()
|
||||
@Optional()
|
||||
IMMICH_BUILD?: string;
|
||||
|
||||
@IsString()
|
||||
@Optional()
|
||||
IMMICH_BUILD_URL?: string;
|
||||
|
||||
@IsString()
|
||||
@Optional()
|
||||
IMMICH_BUILD_IMAGE?: string;
|
||||
|
||||
@IsString()
|
||||
@Optional()
|
||||
IMMICH_BUILD_IMAGE_URL?: string;
|
||||
|
||||
@IsString()
|
||||
@Optional()
|
||||
IMMICH_CONFIG_FILE?: string;
|
||||
|
||||
@IsEnum(ImmichEnvironment)
|
||||
@Optional()
|
||||
IMMICH_ENV?: ImmichEnvironment;
|
||||
|
||||
@IsString()
|
||||
@Optional()
|
||||
IMMICH_HOST?: string;
|
||||
|
||||
@ValidateBoolean({ optional: true })
|
||||
IMMICH_IGNORE_MOUNT_CHECK_ERRORS?: boolean;
|
||||
|
||||
@IsEnum(LogLevel)
|
||||
@Optional()
|
||||
IMMICH_LOG_LEVEL?: LogLevel;
|
||||
|
||||
@IsInt()
|
||||
@Optional()
|
||||
@Type(() => Number)
|
||||
IMMICH_MICROSERVICES_METRICS_PORT?: number;
|
||||
|
||||
@IsInt()
|
||||
@Optional()
|
||||
@Type(() => Number)
|
||||
IMMICH_PORT?: number;
|
||||
|
||||
@IsString()
|
||||
@Optional()
|
||||
IMMICH_REPOSITORY?: string;
|
||||
|
||||
@IsString()
|
||||
@Optional()
|
||||
IMMICH_REPOSITORY_URL?: string;
|
||||
|
||||
@IsString()
|
||||
@Optional()
|
||||
IMMICH_SOURCE_REF?: string;
|
||||
|
||||
@IsString()
|
||||
@Optional()
|
||||
IMMICH_SOURCE_COMMIT?: string;
|
||||
|
||||
@IsString()
|
||||
@Optional()
|
||||
IMMICH_SOURCE_URL?: string;
|
||||
|
||||
@IsString()
|
||||
@Optional()
|
||||
IMMICH_TELEMETRY_INCLUDE?: string;
|
||||
|
||||
@IsString()
|
||||
@Optional()
|
||||
IMMICH_TELEMETRY_EXCLUDE?: string;
|
||||
|
||||
@IsString()
|
||||
@Optional()
|
||||
IMMICH_THIRD_PARTY_SOURCE_URL?: string;
|
||||
|
||||
@IsString()
|
||||
@Optional()
|
||||
IMMICH_THIRD_PARTY_BUG_FEATURE_URL?: string;
|
||||
|
||||
@IsString()
|
||||
@Optional()
|
||||
IMMICH_THIRD_PARTY_DOCUMENTATION_URL?: string;
|
||||
|
||||
@IsString()
|
||||
@Optional()
|
||||
IMMICH_THIRD_PARTY_SUPPORT_URL?: string;
|
||||
|
||||
@IsIPRange({ requireCIDR: false }, { each: true })
|
||||
@Transform(({ value }) =>
|
||||
value && typeof value === 'string'
|
||||
? value
|
||||
.split(',')
|
||||
.map((value) => value.trim())
|
||||
.filter(Boolean)
|
||||
: value,
|
||||
)
|
||||
@Optional()
|
||||
IMMICH_TRUSTED_PROXIES?: string[];
|
||||
|
||||
@IsString()
|
||||
@Optional()
|
||||
IMMICH_WORKERS_INCLUDE?: string;
|
||||
|
||||
@IsString()
|
||||
@Optional()
|
||||
IMMICH_WORKERS_EXCLUDE?: string;
|
||||
|
||||
@IsString()
|
||||
@Optional()
|
||||
DB_DATABASE_NAME?: string;
|
||||
|
||||
@IsString()
|
||||
@Optional()
|
||||
DB_HOSTNAME?: string;
|
||||
|
||||
@IsString()
|
||||
@Optional()
|
||||
DB_PASSWORD?: string;
|
||||
|
||||
@IsInt()
|
||||
@Optional()
|
||||
@Type(() => Number)
|
||||
DB_PORT?: number;
|
||||
|
||||
@ValidateBoolean({ optional: true })
|
||||
DB_SKIP_MIGRATIONS?: boolean;
|
||||
|
||||
@IsString()
|
||||
@Optional()
|
||||
DB_URL?: string;
|
||||
|
||||
@IsString()
|
||||
@Optional()
|
||||
DB_USERNAME?: string;
|
||||
|
||||
@IsEnum(['pgvector', 'pgvecto.rs'])
|
||||
@Optional()
|
||||
DB_VECTOR_EXTENSION?: 'pgvector' | 'pgvecto.rs';
|
||||
|
||||
@IsString()
|
||||
@Optional()
|
||||
NO_COLOR?: string;
|
||||
|
||||
@IsString()
|
||||
@Optional()
|
||||
REDIS_HOSTNAME?: string;
|
||||
|
||||
@IsInt()
|
||||
@Optional()
|
||||
@Type(() => Number)
|
||||
REDIS_PORT?: number;
|
||||
|
||||
@IsInt()
|
||||
@Optional()
|
||||
@Type(() => Number)
|
||||
REDIS_DBINDEX?: number;
|
||||
|
||||
@IsString()
|
||||
@Optional()
|
||||
REDIS_USERNAME?: string;
|
||||
|
||||
@IsString()
|
||||
@Optional()
|
||||
REDIS_PASSWORD?: string;
|
||||
|
||||
@IsString()
|
||||
@Optional()
|
||||
REDIS_SOCKET?: string;
|
||||
|
||||
@IsString()
|
||||
@Optional()
|
||||
REDIS_URL?: string;
|
||||
}
|
@ -97,4 +97,7 @@ export class AllJobStatusResponseDto implements Record<QueueName, JobStatusDto>
|
||||
|
||||
@ApiProperty({ type: JobStatusDto })
|
||||
[QueueName.NOTIFICATION]!: JobStatusDto;
|
||||
|
||||
@ApiProperty({ type: JobStatusDto })
|
||||
[QueueName.BACKUP_DATABASE]!: JobStatusDto;
|
||||
}
|
||||
|
@ -46,6 +46,30 @@ const isLibraryScanEnabled = (config: SystemConfigLibraryScanDto) => config.enab
|
||||
const isOAuthEnabled = (config: SystemConfigOAuthDto) => config.enabled;
|
||||
const isOAuthOverrideEnabled = (config: SystemConfigOAuthDto) => config.mobileOverrideEnabled;
|
||||
const isEmailNotificationEnabled = (config: SystemConfigSmtpDto) => config.enabled;
|
||||
const isDatabaseBackupEnabled = (config: DatabaseBackupConfig) => config.enabled;
|
||||
|
||||
export class DatabaseBackupConfig {
|
||||
@ValidateBoolean()
|
||||
enabled!: boolean;
|
||||
|
||||
@ValidateIf(isDatabaseBackupEnabled)
|
||||
@IsNotEmpty()
|
||||
@Validate(CronValidator, { message: 'Invalid cron expression' })
|
||||
@IsString()
|
||||
cronExpression!: string;
|
||||
|
||||
@IsInt()
|
||||
@IsPositive()
|
||||
@IsNotEmpty()
|
||||
keepLastAmount!: number;
|
||||
}
|
||||
|
||||
export class SystemConfigBackupsDto {
|
||||
@Type(() => DatabaseBackupConfig)
|
||||
@ValidateNested()
|
||||
@IsObject()
|
||||
database!: DatabaseBackupConfig;
|
||||
}
|
||||
|
||||
export class SystemConfigFFmpegDto {
|
||||
@IsInt()
|
||||
@ -110,12 +134,6 @@ export class SystemConfigFFmpegDto {
|
||||
@ApiProperty({ type: 'integer' })
|
||||
gopSize!: number;
|
||||
|
||||
@IsInt()
|
||||
@Min(0)
|
||||
@Type(() => Number)
|
||||
@ApiProperty({ type: 'integer' })
|
||||
npl!: number;
|
||||
|
||||
@ValidateBoolean()
|
||||
temporalAQ!: boolean;
|
||||
|
||||
@ -549,6 +567,11 @@ class SystemConfigUserDto {
|
||||
}
|
||||
|
||||
export class SystemConfigDto implements SystemConfig {
|
||||
@Type(() => SystemConfigBackupsDto)
|
||||
@ValidateNested()
|
||||
@IsObject()
|
||||
backup!: SystemConfigBackupsDto;
|
||||
|
||||
@Type(() => SystemConfigFFmpegDto)
|
||||
@ValidateNested()
|
||||
@IsObject()
|
||||
|
@ -181,6 +181,7 @@ export enum StorageFolder {
|
||||
UPLOAD = 'upload',
|
||||
PROFILE = 'profile',
|
||||
THUMBNAILS = 'thumbs',
|
||||
BACKUPS = 'backups',
|
||||
}
|
||||
|
||||
export enum SystemMetadataKey {
|
||||
@ -334,6 +335,7 @@ export enum MetadataKey {
|
||||
SHARED_ROUTE = 'shared_route',
|
||||
API_KEY_SECURITY = 'api_key',
|
||||
EVENT_CONFIG = 'event_config',
|
||||
JOB_CONFIG = 'job_config',
|
||||
TELEMETRY_ENABLED = 'telemetry_enabled',
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { RegisterQueueOptions } from '@nestjs/bullmq';
|
||||
import { QueueOptions } from 'bullmq';
|
||||
import { RedisOptions } from 'ioredis';
|
||||
import { ClsModuleOptions } from 'nestjs-cls';
|
||||
import { OpenTelemetryModuleOptions } from 'nestjs-otel/lib/interfaces';
|
||||
import { ImmichEnvironment, ImmichTelemetry, ImmichWorker, LogLevel } from 'src/enum';
|
||||
import { DatabaseConnectionParams, VectorExtension } from 'src/interfaces/database.interface';
|
||||
@ -36,6 +37,10 @@ export interface EnvData {
|
||||
queues: RegisterQueueOptions[];
|
||||
};
|
||||
|
||||
cls: {
|
||||
config: ClsModuleOptions;
|
||||
};
|
||||
|
||||
database: {
|
||||
config: PostgresConnectionOptions & DatabaseConnectionParams;
|
||||
skipMigrations: boolean;
|
||||
@ -88,4 +93,5 @@ export interface EnvData {
|
||||
|
||||
export interface IConfigRepository {
|
||||
getEnv(): EnvData;
|
||||
getWorker(): ImmichWorker | undefined;
|
||||
}
|
||||
|
@ -37,6 +37,7 @@ export enum DatabaseLock {
|
||||
CLIPDimSize = 512,
|
||||
Library = 1337,
|
||||
GetSystemConfig = 69,
|
||||
BackupDatabase = 42,
|
||||
}
|
||||
|
||||
export const EXTENSION_NAMES: Record<DatabaseExtension, string> = {
|
||||
|
@ -2,21 +2,21 @@ import { ClassConstructor } from 'class-transformer';
|
||||
import { SystemConfig } from 'src/config';
|
||||
import { AssetResponseDto } from 'src/dtos/asset-response.dto';
|
||||
import { ReleaseNotification, ServerVersionResponseDto } from 'src/dtos/server.dto';
|
||||
import { ImmichWorker } from 'src/enum';
|
||||
import { JobItem, QueueName } from 'src/interfaces/job.interface';
|
||||
|
||||
export const IEventRepository = 'IEventRepository';
|
||||
|
||||
type EventMap = {
|
||||
// app events
|
||||
'app.bootstrap': [ImmichWorker];
|
||||
'app.shutdown': [ImmichWorker];
|
||||
'app.bootstrap': [];
|
||||
'app.shutdown': [];
|
||||
|
||||
'config.init': [{ newConfig: SystemConfig }];
|
||||
// config events
|
||||
'config.update': [
|
||||
{
|
||||
newConfig: SystemConfig;
|
||||
/** When the server starts, `oldConfig` is `undefined` */
|
||||
oldConfig?: SystemConfig;
|
||||
oldConfig: SystemConfig;
|
||||
},
|
||||
];
|
||||
'config.validate': [{ newConfig: SystemConfig; oldConfig: SystemConfig }];
|
||||
@ -38,6 +38,8 @@ type EventMap = {
|
||||
'assets.delete': [{ assetIds: string[]; userId: string }];
|
||||
'assets.restore': [{ assetIds: string[]; userId: string }];
|
||||
|
||||
'job.start': [QueueName, JobItem];
|
||||
|
||||
// session events
|
||||
'session.delete': [{ sessionId: string }];
|
||||
|
||||
@ -86,9 +88,15 @@ export type EventItem<T extends EmitEvent> = {
|
||||
server: boolean;
|
||||
};
|
||||
|
||||
export enum BootstrapEventPriority {
|
||||
// Database service should be initialized before anything else, most other services need database access
|
||||
DatabaseService = -200,
|
||||
// Initialise config after other bootstrap services, stop other services from using config on bootstrap
|
||||
SystemConfig = 100,
|
||||
}
|
||||
|
||||
export interface IEventRepository {
|
||||
setup(options: { services: ClassConstructor<unknown>[] }): void;
|
||||
on<T extends keyof EventMap>(item: EventItem<T>): void;
|
||||
emit<T extends keyof EventMap>(event: T, ...args: ArgsOf<T>): Promise<void>;
|
||||
|
||||
/**
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { ClassConstructor } from 'class-transformer';
|
||||
import { EmailImageAttachment } from 'src/interfaces/notification.interface';
|
||||
|
||||
export enum QueueName {
|
||||
@ -15,11 +16,15 @@ export enum QueueName {
|
||||
SIDECAR = 'sidecar',
|
||||
LIBRARY = 'library',
|
||||
NOTIFICATION = 'notifications',
|
||||
BACKUP_DATABASE = 'backupDatabase',
|
||||
}
|
||||
|
||||
export type ConcurrentQueueName = Exclude<
|
||||
QueueName,
|
||||
QueueName.STORAGE_TEMPLATE_MIGRATION | QueueName.FACIAL_RECOGNITION | QueueName.DUPLICATE_DETECTION
|
||||
| QueueName.STORAGE_TEMPLATE_MIGRATION
|
||||
| QueueName.FACIAL_RECOGNITION
|
||||
| QueueName.DUPLICATE_DETECTION
|
||||
| QueueName.BACKUP_DATABASE
|
||||
>;
|
||||
|
||||
export enum JobCommand {
|
||||
@ -31,6 +36,9 @@ export enum JobCommand {
|
||||
}
|
||||
|
||||
export enum JobName {
|
||||
//backups
|
||||
BACKUP_DATABASE = 'database-backup',
|
||||
|
||||
// conversion
|
||||
QUEUE_VIDEO_CONVERSION = 'queue-video-conversion',
|
||||
VIDEO_CONVERSION = 'video-conversion',
|
||||
@ -209,6 +217,9 @@ export enum QueueCleanType {
|
||||
}
|
||||
|
||||
export type JobItem =
|
||||
// Backups
|
||||
| { name: JobName.BACKUP_DATABASE; data?: IBaseJob }
|
||||
|
||||
// Transcoding
|
||||
| { name: JobName.QUEUE_VIDEO_CONVERSION; data: IBaseJob }
|
||||
| { name: JobName.VIDEO_CONVERSION; data: IEntityJob }
|
||||
@ -228,8 +239,8 @@ export type JobItem =
|
||||
|
||||
// Migration
|
||||
| { name: JobName.QUEUE_MIGRATION; data?: IBaseJob }
|
||||
| { name: JobName.MIGRATE_ASSET; data?: IEntityJob }
|
||||
| { name: JobName.MIGRATE_PERSON; data?: IEntityJob }
|
||||
| { name: JobName.MIGRATE_ASSET; data: IEntityJob }
|
||||
| { name: JobName.MIGRATE_PERSON; data: IEntityJob }
|
||||
|
||||
// Metadata Extraction
|
||||
| { name: JobName.QUEUE_METADATA_EXTRACTION; data: IBaseJob }
|
||||
@ -276,7 +287,7 @@ export type JobItem =
|
||||
| { name: JobName.LIBRARY_SYNC_FILE; data: ILibraryFileJob }
|
||||
| { name: JobName.LIBRARY_QUEUE_SYNC_FILES; data: IEntityJob }
|
||||
| { name: JobName.LIBRARY_QUEUE_SYNC_ASSETS; data: IEntityJob }
|
||||
| { name: JobName.LIBRARY_SYNC_ASSET; data: IEntityJob }
|
||||
| { name: JobName.LIBRARY_SYNC_ASSET; data: ILibraryAssetJob }
|
||||
| { name: JobName.LIBRARY_DELETE; data: IEntityJob }
|
||||
| { name: JobName.LIBRARY_QUEUE_SYNC_ALL; data?: IBaseJob }
|
||||
| { name: JobName.LIBRARY_QUEUE_CLEANUP; data: IBaseJob }
|
||||
@ -295,14 +306,15 @@ export enum JobStatus {
|
||||
FAILED = 'failed',
|
||||
SKIPPED = 'skipped',
|
||||
}
|
||||
|
||||
export type JobHandler<T = any> = (data: T) => Promise<JobStatus>;
|
||||
export type JobItemHandler = (item: JobItem) => Promise<void>;
|
||||
export type Jobs = { [K in JobItem['name']]: (JobItem & { name: K })['data'] };
|
||||
export type JobOf<T extends JobName> = Jobs[T];
|
||||
|
||||
export const IJobRepository = 'IJobRepository';
|
||||
|
||||
export interface IJobRepository {
|
||||
addHandler(queueName: QueueName, concurrency: number, handler: JobItemHandler): void;
|
||||
setup(options: { services: ClassConstructor<unknown>[] }): void;
|
||||
startWorkers(): void;
|
||||
run(job: JobItem): Promise<JobStatus>;
|
||||
addCronJob(name: string, expression: string, onTick: () => void, start?: boolean): void;
|
||||
updateCronJob(name: string, expression?: string, start?: boolean): void;
|
||||
setConcurrency(queueName: QueueName, concurrency: number): void;
|
||||
|
@ -59,6 +59,7 @@ export interface VideoStreamInfo {
|
||||
frameCount: number;
|
||||
isHDR: boolean;
|
||||
bitrate: number;
|
||||
pixelFormat: string;
|
||||
}
|
||||
|
||||
export interface AudioStreamInfo {
|
||||
|
25
server/src/interfaces/process.interface.ts
Normal file
25
server/src/interfaces/process.interface.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { ChildProcessWithoutNullStreams, SpawnOptionsWithoutStdio } from 'node:child_process';
|
||||
import { Readable } from 'node:stream';
|
||||
|
||||
export interface ImmichReadStream {
|
||||
stream: Readable;
|
||||
type?: string;
|
||||
length?: number;
|
||||
}
|
||||
|
||||
export interface ImmichZipStream extends ImmichReadStream {
|
||||
addFile: (inputPath: string, filename: string) => void;
|
||||
finalize: () => Promise<void>;
|
||||
}
|
||||
|
||||
export interface DiskUsage {
|
||||
available: number;
|
||||
free: number;
|
||||
total: number;
|
||||
}
|
||||
|
||||
export const IProcessRepository = 'IProcessRepository';
|
||||
|
||||
export interface IProcessRepository {
|
||||
spawn(command: string, args?: readonly string[], options?: SpawnOptionsWithoutStdio): ChildProcessWithoutNullStreams;
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
import { WatchOptions } from 'chokidar';
|
||||
import { Stats } from 'node:fs';
|
||||
import { FileReadOptions } from 'node:fs/promises';
|
||||
import { Readable } from 'node:stream';
|
||||
import { Readable, Writable } from 'node:stream';
|
||||
import { CrawlOptionsDto, WalkOptionsDto } from 'src/dtos/library.dto';
|
||||
|
||||
export interface ImmichReadStream {
|
||||
@ -36,6 +36,7 @@ export interface IStorageRepository {
|
||||
createReadStream(filepath: string, mimeType?: string | null): Promise<ImmichReadStream>;
|
||||
readFile(filepath: string, options?: FileReadOptions<Buffer>): Promise<Buffer>;
|
||||
createFile(filepath: string, buffer: Buffer): Promise<void>;
|
||||
createWriteStream(filepath: string): Writable;
|
||||
createOrOverwriteFile(filepath: string, buffer: Buffer): Promise<void>;
|
||||
overwriteFile(filepath: string, buffer: Buffer): Promise<void>;
|
||||
realpath(filepath: string): Promise<string>;
|
||||
|
@ -0,0 +1,12 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class RemoveNplFromSystemConfig1730227312171 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`
|
||||
update system_metadata
|
||||
set value = value #- '{ffmpeg,npl}'
|
||||
where key = 'system-config' and value->'ffmpeg'->'npl' is not null`);
|
||||
}
|
||||
|
||||
public async down(): Promise<void> {}
|
||||
}
|
@ -8,6 +8,7 @@ const getEnv = () => {
|
||||
|
||||
const resetEnv = () => {
|
||||
for (const env of [
|
||||
'IMMICH_ENV',
|
||||
'IMMICH_WORKERS_INCLUDE',
|
||||
'IMMICH_WORKERS_EXCLUDE',
|
||||
'IMMICH_TRUSTED_PROXIES',
|
||||
@ -62,6 +63,18 @@ describe('getEnv', () => {
|
||||
resetEnv();
|
||||
});
|
||||
|
||||
it('should use defaults', () => {
|
||||
const config = getEnv();
|
||||
|
||||
expect(config).toMatchObject({
|
||||
host: undefined,
|
||||
port: 2283,
|
||||
environment: 'production',
|
||||
configFile: undefined,
|
||||
logLevel: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
describe('database', () => {
|
||||
it('should use defaults', () => {
|
||||
const { database } = getEnv();
|
||||
@ -202,6 +215,11 @@ describe('getEnv', () => {
|
||||
trustedProxies: ['10.1.0.0', '10.2.0.0', '169.254.0.0/16'],
|
||||
});
|
||||
});
|
||||
|
||||
it('should reject invalid trusted proxies', () => {
|
||||
process.env.IMMICH_TRUSTED_PROXIES = '10.1';
|
||||
expect(() => getEnv()).toThrowError('Invalid environment variables: IMMICH_TRUSTED_PROXIES');
|
||||
});
|
||||
});
|
||||
|
||||
describe('telemetry', () => {
|
||||
|
@ -1,15 +1,18 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Inject, Injectable, Optional } from '@nestjs/common';
|
||||
import { plainToInstance } from 'class-transformer';
|
||||
import { validateSync } from 'class-validator';
|
||||
import { Request, Response } from 'express';
|
||||
import { CLS_ID } from 'nestjs-cls';
|
||||
import { join, resolve } from 'node:path';
|
||||
import { citiesFile, excludePaths } from 'src/constants';
|
||||
import { citiesFile, excludePaths, IWorker } from 'src/constants';
|
||||
import { Telemetry } from 'src/decorators';
|
||||
import { ImmichEnvironment, ImmichTelemetry, ImmichWorker, LogLevel } from 'src/enum';
|
||||
import { EnvDto } from 'src/dtos/env.dto';
|
||||
import { ImmichEnvironment, ImmichHeader, ImmichTelemetry, ImmichWorker } from 'src/enum';
|
||||
import { EnvData, IConfigRepository } from 'src/interfaces/config.interface';
|
||||
import { DatabaseExtension } from 'src/interfaces/database.interface';
|
||||
import { QueueName } from 'src/interfaces/job.interface';
|
||||
import { setDifference } from 'src/utils/set';
|
||||
|
||||
// TODO replace src/config validation with class-validator, here
|
||||
|
||||
const productionKeys = {
|
||||
client:
|
||||
'LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUF2LzdTMzJjUkE1KysxTm5WRHNDTQpzcFAvakpISU1xT0pYRm5oNE53QTJPcHorUk1mZGNvOTJQc09naCt3d1FlRXYxVTJjMnBqelRpUS8ybHJLcS9rCnpKUmxYd2M0Y1Vlc1FETUpPRitQMnFPTlBiQUprWHZDWFlCVUxpdENJa29Md2ZoU0dOanlJS2FSRGhkL3ROeU4KOCtoTlJabllUMWhTSWo5U0NrS3hVQ096YXRQVjRtQ0RlclMrYkUrZ0VVZVdwOTlWOWF6dkYwRkltblRXcFFTdwpjOHdFWmdPTWg0c3ZoNmFpY3dkemtQQ3dFTGFrMFZhQkgzMUJFVUNRTGI5K0FJdEhBVXRKQ0t4aGI1V2pzMXM5CmJyWGZpMHZycGdjWi82RGFuWTJxZlNQem5PbXZEMkZycmxTMXE0SkpOM1ZvN1d3LzBZeS95TWNtelRXWmhHdWgKVVFJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tDQo=',
|
||||
@ -33,8 +36,16 @@ const asSet = <T>(value: string | undefined, defaults: T[]) => {
|
||||
};
|
||||
|
||||
const getEnv = (): EnvData => {
|
||||
const includedWorkers = asSet(process.env.IMMICH_WORKERS_INCLUDE, [ImmichWorker.API, ImmichWorker.MICROSERVICES]);
|
||||
const excludedWorkers = asSet(process.env.IMMICH_WORKERS_EXCLUDE, []);
|
||||
const dto = plainToInstance(EnvDto, process.env);
|
||||
const errors = validateSync(dto);
|
||||
if (errors.length > 0) {
|
||||
throw new Error(
|
||||
`Invalid environment variables: ${errors.map((error) => `${error.property}=${error.value}`).join(', ')}`,
|
||||
);
|
||||
}
|
||||
|
||||
const includedWorkers = asSet(dto.IMMICH_WORKERS_INCLUDE, [ImmichWorker.API, ImmichWorker.MICROSERVICES]);
|
||||
const excludedWorkers = asSet(dto.IMMICH_WORKERS_EXCLUDE, []);
|
||||
const workers = [...setDifference(includedWorkers, excludedWorkers)];
|
||||
for (const worker of workers) {
|
||||
if (!WORKER_TYPES.has(worker)) {
|
||||
@ -42,9 +53,9 @@ const getEnv = (): EnvData => {
|
||||
}
|
||||
}
|
||||
|
||||
const environment = process.env.IMMICH_ENV as ImmichEnvironment;
|
||||
const environment = dto.IMMICH_ENV || ImmichEnvironment.PRODUCTION;
|
||||
const isProd = environment === ImmichEnvironment.PRODUCTION;
|
||||
const buildFolder = process.env.IMMICH_BUILD_DATA || '/build';
|
||||
const buildFolder = dto.IMMICH_BUILD_DATA || '/build';
|
||||
const folders = {
|
||||
// eslint-disable-next-line unicorn/prefer-module
|
||||
dist: resolve(`${__dirname}/..`),
|
||||
@ -52,18 +63,18 @@ const getEnv = (): EnvData => {
|
||||
web: join(buildFolder, 'www'),
|
||||
};
|
||||
|
||||
const databaseUrl = process.env.DB_URL;
|
||||
const databaseUrl = dto.DB_URL;
|
||||
|
||||
let redisConfig = {
|
||||
host: process.env.REDIS_HOSTNAME || 'redis',
|
||||
port: Number.parseInt(process.env.REDIS_PORT || '') || 6379,
|
||||
db: Number.parseInt(process.env.REDIS_DBINDEX || '') || 0,
|
||||
username: process.env.REDIS_USERNAME || undefined,
|
||||
password: process.env.REDIS_PASSWORD || undefined,
|
||||
path: process.env.REDIS_SOCKET || undefined,
|
||||
host: dto.REDIS_HOSTNAME || 'redis',
|
||||
port: dto.REDIS_PORT || 6379,
|
||||
db: dto.REDIS_DBINDEX || 0,
|
||||
username: dto.REDIS_USERNAME || undefined,
|
||||
password: dto.REDIS_PASSWORD || undefined,
|
||||
path: dto.REDIS_SOCKET || undefined,
|
||||
};
|
||||
|
||||
const redisUrl = process.env.REDIS_URL;
|
||||
const redisUrl = dto.REDIS_URL;
|
||||
if (redisUrl && redisUrl.startsWith('ioredis://')) {
|
||||
try {
|
||||
redisConfig = JSON.parse(Buffer.from(redisUrl.slice(10), 'base64').toString());
|
||||
@ -73,11 +84,11 @@ const getEnv = (): EnvData => {
|
||||
}
|
||||
|
||||
const includedTelemetries =
|
||||
process.env.IMMICH_TELEMETRY_INCLUDE === 'all'
|
||||
dto.IMMICH_TELEMETRY_INCLUDE === 'all'
|
||||
? new Set(Object.values(ImmichTelemetry))
|
||||
: asSet<ImmichTelemetry>(process.env.IMMICH_TELEMETRY_INCLUDE, []);
|
||||
: asSet<ImmichTelemetry>(dto.IMMICH_TELEMETRY_INCLUDE, []);
|
||||
|
||||
const excludedTelemetries = asSet<ImmichTelemetry>(process.env.IMMICH_TELEMETRY_EXCLUDE, []);
|
||||
const excludedTelemetries = asSet<ImmichTelemetry>(dto.IMMICH_TELEMETRY_EXCLUDE, []);
|
||||
const telemetries = setDifference(includedTelemetries, excludedTelemetries);
|
||||
for (const telemetry of telemetries) {
|
||||
if (!TELEMETRY_TYPES.has(telemetry)) {
|
||||
@ -86,26 +97,26 @@ const getEnv = (): EnvData => {
|
||||
}
|
||||
|
||||
return {
|
||||
host: process.env.IMMICH_HOST,
|
||||
port: Number(process.env.IMMICH_PORT) || 2283,
|
||||
host: dto.IMMICH_HOST,
|
||||
port: dto.IMMICH_PORT || 2283,
|
||||
environment,
|
||||
configFile: process.env.IMMICH_CONFIG_FILE,
|
||||
logLevel: process.env.IMMICH_LOG_LEVEL as LogLevel,
|
||||
configFile: dto.IMMICH_CONFIG_FILE,
|
||||
logLevel: dto.IMMICH_LOG_LEVEL,
|
||||
|
||||
buildMetadata: {
|
||||
build: process.env.IMMICH_BUILD,
|
||||
buildUrl: process.env.IMMICH_BUILD_URL,
|
||||
buildImage: process.env.IMMICH_BUILD_IMAGE,
|
||||
buildImageUrl: process.env.IMMICH_BUILD_IMAGE_URL,
|
||||
repository: process.env.IMMICH_REPOSITORY,
|
||||
repositoryUrl: process.env.IMMICH_REPOSITORY_URL,
|
||||
sourceRef: process.env.IMMICH_SOURCE_REF,
|
||||
sourceCommit: process.env.IMMICH_SOURCE_COMMIT,
|
||||
sourceUrl: process.env.IMMICH_SOURCE_URL,
|
||||
thirdPartySourceUrl: process.env.IMMICH_THIRD_PARTY_SOURCE_URL,
|
||||
thirdPartyBugFeatureUrl: process.env.IMMICH_THIRD_PARTY_BUG_FEATURE_URL,
|
||||
thirdPartyDocumentationUrl: process.env.IMMICH_THIRD_PARTY_DOCUMENTATION_URL,
|
||||
thirdPartySupportUrl: process.env.IMMICH_THIRD_PARTY_SUPPORT_URL,
|
||||
build: dto.IMMICH_BUILD,
|
||||
buildUrl: dto.IMMICH_BUILD_URL,
|
||||
buildImage: dto.IMMICH_BUILD_IMAGE,
|
||||
buildImageUrl: dto.IMMICH_BUILD_IMAGE_URL,
|
||||
repository: dto.IMMICH_REPOSITORY,
|
||||
repositoryUrl: dto.IMMICH_REPOSITORY_URL,
|
||||
sourceRef: dto.IMMICH_SOURCE_REF,
|
||||
sourceCommit: dto.IMMICH_SOURCE_COMMIT,
|
||||
sourceUrl: dto.IMMICH_SOURCE_URL,
|
||||
thirdPartySourceUrl: dto.IMMICH_THIRD_PARTY_SOURCE_URL,
|
||||
thirdPartyBugFeatureUrl: dto.IMMICH_THIRD_PARTY_BUG_FEATURE_URL,
|
||||
thirdPartyDocumentationUrl: dto.IMMICH_THIRD_PARTY_DOCUMENTATION_URL,
|
||||
thirdPartySupportUrl: dto.IMMICH_THIRD_PARTY_SUPPORT_URL,
|
||||
},
|
||||
|
||||
bull: {
|
||||
@ -121,6 +132,22 @@ const getEnv = (): EnvData => {
|
||||
queues: Object.values(QueueName).map((name) => ({ name })),
|
||||
},
|
||||
|
||||
cls: {
|
||||
config: {
|
||||
middleware: {
|
||||
mount: true,
|
||||
generateId: true,
|
||||
setup: (cls, req: Request, res: Response) => {
|
||||
const headerValues = req.headers[ImmichHeader.CID];
|
||||
const headerValue = Array.isArray(headerValues) ? headerValues[0] : headerValues;
|
||||
const cid = headerValue || cls.get(CLS_ID);
|
||||
cls.set(CLS_ID, cid);
|
||||
res.header(ImmichHeader.CID, cid);
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
database: {
|
||||
config: {
|
||||
type: 'postgres',
|
||||
@ -135,26 +162,22 @@ const getEnv = (): EnvData => {
|
||||
? { connectionType: 'url', url: databaseUrl }
|
||||
: {
|
||||
connectionType: 'parts',
|
||||
host: process.env.DB_HOSTNAME || 'database',
|
||||
port: Number(process.env.DB_PORT) || 5432,
|
||||
username: process.env.DB_USERNAME || 'postgres',
|
||||
password: process.env.DB_PASSWORD || 'postgres',
|
||||
database: process.env.DB_DATABASE_NAME || 'immich',
|
||||
host: dto.DB_HOSTNAME || 'database',
|
||||
port: dto.DB_PORT || 5432,
|
||||
username: dto.DB_USERNAME || 'postgres',
|
||||
password: dto.DB_PASSWORD || 'postgres',
|
||||
database: dto.DB_DATABASE_NAME || 'immich',
|
||||
}),
|
||||
},
|
||||
|
||||
skipMigrations: process.env.DB_SKIP_MIGRATIONS === 'true',
|
||||
vectorExtension:
|
||||
process.env.DB_VECTOR_EXTENSION === 'pgvector' ? DatabaseExtension.VECTOR : DatabaseExtension.VECTORS,
|
||||
skipMigrations: dto.DB_SKIP_MIGRATIONS ?? false,
|
||||
vectorExtension: dto.DB_VECTOR_EXTENSION === 'pgvector' ? DatabaseExtension.VECTOR : DatabaseExtension.VECTORS,
|
||||
},
|
||||
|
||||
licensePublicKey: isProd ? productionKeys : stagingKeys,
|
||||
|
||||
network: {
|
||||
trustedProxies: (process.env.IMMICH_TRUSTED_PROXIES ?? '')
|
||||
.split(',')
|
||||
.map((value) => value.trim())
|
||||
.filter(Boolean),
|
||||
trustedProxies: dto.IMMICH_TRUSTED_PROXIES ?? [],
|
||||
},
|
||||
|
||||
otel: {
|
||||
@ -185,18 +208,18 @@ const getEnv = (): EnvData => {
|
||||
},
|
||||
|
||||
storage: {
|
||||
ignoreMountCheckErrors: process.env.IMMICH_IGNORE_MOUNT_CHECK_ERRORS === 'true',
|
||||
ignoreMountCheckErrors: !!dto.IMMICH_IGNORE_MOUNT_CHECK_ERRORS,
|
||||
},
|
||||
|
||||
telemetry: {
|
||||
apiPort: Number(process.env.IMMICH_API_METRICS_PORT || '') || 8081,
|
||||
microservicesPort: Number(process.env.IMMICH_MICROSERVICES_METRICS_PORT || '') || 8082,
|
||||
apiPort: dto.IMMICH_API_METRICS_PORT || 8081,
|
||||
microservicesPort: dto.IMMICH_MICROSERVICES_METRICS_PORT || 8082,
|
||||
metrics: telemetries,
|
||||
},
|
||||
|
||||
workers,
|
||||
|
||||
noColor: !!process.env.NO_COLOR,
|
||||
noColor: !!dto.NO_COLOR,
|
||||
};
|
||||
};
|
||||
|
||||
@ -205,6 +228,8 @@ let cached: EnvData | undefined;
|
||||
@Injectable()
|
||||
@Telemetry({ enabled: false })
|
||||
export class ConfigRepository implements IConfigRepository {
|
||||
constructor(@Inject(IWorker) @Optional() private worker?: ImmichWorker) {}
|
||||
|
||||
getEnv(): EnvData {
|
||||
if (!cached) {
|
||||
cached = getEnv();
|
||||
@ -212,6 +237,10 @@ export class ConfigRepository implements IConfigRepository {
|
||||
|
||||
return cached;
|
||||
}
|
||||
|
||||
getWorker() {
|
||||
return this.worker;
|
||||
}
|
||||
}
|
||||
|
||||
export const clearEnvCache = () => (cached = undefined);
|
||||
|
@ -57,7 +57,6 @@ export class EventRepository implements OnGatewayConnection, OnGatewayDisconnect
|
||||
|
||||
setup({ services }: { services: ClassConstructor<unknown>[] }) {
|
||||
const reflector = this.moduleRef.get(Reflector, { strict: false });
|
||||
const repository = this.moduleRef.get<IEventRepository>(IEventRepository);
|
||||
const items: Item<EmitEvent>[] = [];
|
||||
|
||||
// discovery
|
||||
@ -94,7 +93,7 @@ export class EventRepository implements OnGatewayConnection, OnGatewayDisconnect
|
||||
|
||||
// register by priority
|
||||
for (const handler of handlers) {
|
||||
repository.on(handler);
|
||||
this.addHandler(handler);
|
||||
}
|
||||
}
|
||||
|
||||
@ -134,7 +133,7 @@ export class EventRepository implements OnGatewayConnection, OnGatewayDisconnect
|
||||
await client.leave(client.nsp.name);
|
||||
}
|
||||
|
||||
on<T extends EmitEvent>(item: EventItem<T>): void {
|
||||
private addHandler<T extends EmitEvent>(item: EventItem<T>): void {
|
||||
const event = item.event;
|
||||
|
||||
if (!this.emitHandlers[event]) {
|
||||
|
@ -22,6 +22,7 @@ import { INotificationRepository } from 'src/interfaces/notification.interface';
|
||||
import { IOAuthRepository } from 'src/interfaces/oauth.interface';
|
||||
import { IPartnerRepository } from 'src/interfaces/partner.interface';
|
||||
import { IPersonRepository } from 'src/interfaces/person.interface';
|
||||
import { IProcessRepository } from 'src/interfaces/process.interface';
|
||||
import { ISearchRepository } from 'src/interfaces/search.interface';
|
||||
import { IServerInfoRepository } from 'src/interfaces/server-info.interface';
|
||||
import { ISessionRepository } from 'src/interfaces/session.interface';
|
||||
@ -59,6 +60,7 @@ import { NotificationRepository } from 'src/repositories/notification.repository
|
||||
import { OAuthRepository } from 'src/repositories/oauth.repository';
|
||||
import { PartnerRepository } from 'src/repositories/partner.repository';
|
||||
import { PersonRepository } from 'src/repositories/person.repository';
|
||||
import { ProcessRepository } from 'src/repositories/process.repository';
|
||||
import { SearchRepository } from 'src/repositories/search.repository';
|
||||
import { ServerInfoRepository } from 'src/repositories/server-info.repository';
|
||||
import { SessionRepository } from 'src/repositories/session.repository';
|
||||
@ -98,6 +100,7 @@ export const repositories = [
|
||||
{ provide: IOAuthRepository, useClass: OAuthRepository },
|
||||
{ provide: IPartnerRepository, useClass: PartnerRepository },
|
||||
{ provide: IPersonRepository, useClass: PersonRepository },
|
||||
{ provide: IProcessRepository, useClass: ProcessRepository },
|
||||
{ provide: ISearchRepository, useClass: SearchRepository },
|
||||
{ provide: IServerInfoRepository, useClass: ServerInfoRepository },
|
||||
{ provide: ISessionRepository, useClass: SessionRepository },
|
||||
|
@ -1,121 +1,122 @@
|
||||
import { getQueueToken } from '@nestjs/bullmq';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { ModuleRef } from '@nestjs/core';
|
||||
import { ModuleRef, Reflector } from '@nestjs/core';
|
||||
import { SchedulerRegistry } from '@nestjs/schedule';
|
||||
import { Job, JobsOptions, Processor, Queue, Worker, WorkerOptions } from 'bullmq';
|
||||
import { JobsOptions, Queue, Worker } from 'bullmq';
|
||||
import { ClassConstructor } from 'class-transformer';
|
||||
import { CronJob, CronTime } from 'cron';
|
||||
import { setTimeout } from 'node:timers/promises';
|
||||
import { JobConfig } from 'src/decorators';
|
||||
import { MetadataKey } from 'src/enum';
|
||||
import { IConfigRepository } from 'src/interfaces/config.interface';
|
||||
import { IEventRepository } from 'src/interfaces/event.interface';
|
||||
import {
|
||||
IEntityJob,
|
||||
IJobRepository,
|
||||
JobCounts,
|
||||
JobItem,
|
||||
JobName,
|
||||
JobOf,
|
||||
JobStatus,
|
||||
QueueCleanType,
|
||||
QueueName,
|
||||
QueueStatus,
|
||||
} from 'src/interfaces/job.interface';
|
||||
import { ILoggerRepository } from 'src/interfaces/logger.interface';
|
||||
import { getKeyByValue, getMethodNames, ImmichStartupError } from 'src/utils/misc';
|
||||
|
||||
export const JOBS_TO_QUEUE: Record<JobName, QueueName> = {
|
||||
// misc
|
||||
[JobName.ASSET_DELETION]: QueueName.BACKGROUND_TASK,
|
||||
[JobName.ASSET_DELETION_CHECK]: QueueName.BACKGROUND_TASK,
|
||||
[JobName.USER_DELETE_CHECK]: QueueName.BACKGROUND_TASK,
|
||||
[JobName.USER_DELETION]: QueueName.BACKGROUND_TASK,
|
||||
[JobName.DELETE_FILES]: QueueName.BACKGROUND_TASK,
|
||||
[JobName.CLEAN_OLD_AUDIT_LOGS]: QueueName.BACKGROUND_TASK,
|
||||
[JobName.CLEAN_OLD_SESSION_TOKENS]: QueueName.BACKGROUND_TASK,
|
||||
[JobName.PERSON_CLEANUP]: QueueName.BACKGROUND_TASK,
|
||||
[JobName.USER_SYNC_USAGE]: QueueName.BACKGROUND_TASK,
|
||||
|
||||
// conversion
|
||||
[JobName.QUEUE_VIDEO_CONVERSION]: QueueName.VIDEO_CONVERSION,
|
||||
[JobName.VIDEO_CONVERSION]: QueueName.VIDEO_CONVERSION,
|
||||
|
||||
// thumbnails
|
||||
[JobName.QUEUE_GENERATE_THUMBNAILS]: QueueName.THUMBNAIL_GENERATION,
|
||||
[JobName.GENERATE_THUMBNAILS]: QueueName.THUMBNAIL_GENERATION,
|
||||
[JobName.GENERATE_PERSON_THUMBNAIL]: QueueName.THUMBNAIL_GENERATION,
|
||||
|
||||
// tags
|
||||
[JobName.TAG_CLEANUP]: QueueName.BACKGROUND_TASK,
|
||||
|
||||
// metadata
|
||||
[JobName.QUEUE_METADATA_EXTRACTION]: QueueName.METADATA_EXTRACTION,
|
||||
[JobName.METADATA_EXTRACTION]: QueueName.METADATA_EXTRACTION,
|
||||
[JobName.LINK_LIVE_PHOTOS]: QueueName.METADATA_EXTRACTION,
|
||||
|
||||
// storage template
|
||||
[JobName.STORAGE_TEMPLATE_MIGRATION]: QueueName.STORAGE_TEMPLATE_MIGRATION,
|
||||
[JobName.STORAGE_TEMPLATE_MIGRATION_SINGLE]: QueueName.STORAGE_TEMPLATE_MIGRATION,
|
||||
|
||||
// migration
|
||||
[JobName.QUEUE_MIGRATION]: QueueName.MIGRATION,
|
||||
[JobName.MIGRATE_ASSET]: QueueName.MIGRATION,
|
||||
[JobName.MIGRATE_PERSON]: QueueName.MIGRATION,
|
||||
|
||||
// facial recognition
|
||||
[JobName.QUEUE_FACE_DETECTION]: QueueName.FACE_DETECTION,
|
||||
[JobName.FACE_DETECTION]: QueueName.FACE_DETECTION,
|
||||
[JobName.QUEUE_FACIAL_RECOGNITION]: QueueName.FACIAL_RECOGNITION,
|
||||
[JobName.FACIAL_RECOGNITION]: QueueName.FACIAL_RECOGNITION,
|
||||
|
||||
// smart search
|
||||
[JobName.QUEUE_SMART_SEARCH]: QueueName.SMART_SEARCH,
|
||||
[JobName.SMART_SEARCH]: QueueName.SMART_SEARCH,
|
||||
|
||||
// duplicate detection
|
||||
[JobName.QUEUE_DUPLICATE_DETECTION]: QueueName.DUPLICATE_DETECTION,
|
||||
[JobName.DUPLICATE_DETECTION]: QueueName.DUPLICATE_DETECTION,
|
||||
|
||||
// XMP sidecars
|
||||
[JobName.QUEUE_SIDECAR]: QueueName.SIDECAR,
|
||||
[JobName.SIDECAR_DISCOVERY]: QueueName.SIDECAR,
|
||||
[JobName.SIDECAR_SYNC]: QueueName.SIDECAR,
|
||||
[JobName.SIDECAR_WRITE]: QueueName.SIDECAR,
|
||||
|
||||
// Library management
|
||||
[JobName.LIBRARY_SYNC_FILE]: QueueName.LIBRARY,
|
||||
[JobName.LIBRARY_QUEUE_SYNC_FILES]: QueueName.LIBRARY,
|
||||
[JobName.LIBRARY_QUEUE_SYNC_ASSETS]: QueueName.LIBRARY,
|
||||
[JobName.LIBRARY_DELETE]: QueueName.LIBRARY,
|
||||
[JobName.LIBRARY_SYNC_ASSET]: QueueName.LIBRARY,
|
||||
[JobName.LIBRARY_QUEUE_SYNC_ALL]: QueueName.LIBRARY,
|
||||
[JobName.LIBRARY_QUEUE_CLEANUP]: QueueName.LIBRARY,
|
||||
|
||||
// Notification
|
||||
[JobName.SEND_EMAIL]: QueueName.NOTIFICATION,
|
||||
[JobName.NOTIFY_ALBUM_INVITE]: QueueName.NOTIFICATION,
|
||||
[JobName.NOTIFY_ALBUM_UPDATE]: QueueName.NOTIFICATION,
|
||||
[JobName.NOTIFY_SIGNUP]: QueueName.NOTIFICATION,
|
||||
|
||||
// Version check
|
||||
[JobName.VERSION_CHECK]: QueueName.BACKGROUND_TASK,
|
||||
|
||||
// Trash
|
||||
[JobName.QUEUE_TRASH_EMPTY]: QueueName.BACKGROUND_TASK,
|
||||
type JobMapItem = {
|
||||
jobName: JobName;
|
||||
queueName: QueueName;
|
||||
handler: (job: JobOf<any>) => Promise<JobStatus>;
|
||||
label: string;
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
export class JobRepository implements IJobRepository {
|
||||
private workers: Partial<Record<QueueName, Worker>> = {};
|
||||
private handlers: Partial<Record<JobName, JobMapItem>> = {};
|
||||
|
||||
constructor(
|
||||
private moduleReference: ModuleRef,
|
||||
private schedulerReqistry: SchedulerRegistry,
|
||||
private moduleRef: ModuleRef,
|
||||
private schedulerRegistry: SchedulerRegistry,
|
||||
@Inject(IConfigRepository) private configRepository: IConfigRepository,
|
||||
@Inject(IEventRepository) private eventRepository: IEventRepository,
|
||||
@Inject(ILoggerRepository) private logger: ILoggerRepository,
|
||||
) {
|
||||
this.logger.setContext(JobRepository.name);
|
||||
}
|
||||
|
||||
addHandler(queueName: QueueName, concurrency: number, handler: (item: JobItem) => Promise<void>) {
|
||||
setup({ services }: { services: ClassConstructor<unknown>[] }) {
|
||||
const reflector = this.moduleRef.get(Reflector, { strict: false });
|
||||
|
||||
// discovery
|
||||
for (const Service of services) {
|
||||
const instance = this.moduleRef.get<any>(Service);
|
||||
for (const methodName of getMethodNames(instance)) {
|
||||
const handler = instance[methodName];
|
||||
const config = reflector.get<JobConfig>(MetadataKey.JOB_CONFIG, handler);
|
||||
if (!config) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const { name: jobName, queue: queueName } = config;
|
||||
const label = `${Service.name}.${handler.name}`;
|
||||
|
||||
// one handler per job
|
||||
if (this.handlers[jobName]) {
|
||||
const jobKey = getKeyByValue(JobName, jobName);
|
||||
const errorMessage = `Failed to add job handler for ${label}`;
|
||||
this.logger.error(
|
||||
`${errorMessage}. JobName.${jobKey} is already handled by ${this.handlers[jobName].label}.`,
|
||||
);
|
||||
throw new ImmichStartupError(errorMessage);
|
||||
}
|
||||
|
||||
this.handlers[jobName] = {
|
||||
label,
|
||||
jobName,
|
||||
queueName,
|
||||
handler: handler.bind(instance),
|
||||
};
|
||||
|
||||
this.logger.verbose(`Added job handler: ${jobName} => ${label}`);
|
||||
}
|
||||
}
|
||||
|
||||
// no missing handlers
|
||||
for (const [jobKey, jobName] of Object.entries(JobName)) {
|
||||
const item = this.handlers[jobName];
|
||||
if (!item) {
|
||||
const errorMessage = `Failed to find job handler for Job.${jobKey} ("${jobName}")`;
|
||||
this.logger.error(
|
||||
`${errorMessage}. Make sure to add the @OnJob({ name: JobName.${jobKey}, queue: QueueName.XYZ }) decorator for the new job.`,
|
||||
);
|
||||
throw new ImmichStartupError(errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
startWorkers() {
|
||||
const { bull } = this.configRepository.getEnv();
|
||||
const workerHandler: Processor = async (job: Job) => handler(job as JobItem);
|
||||
const workerOptions: WorkerOptions = { ...bull.config, concurrency };
|
||||
this.workers[queueName] = new Worker(queueName, workerHandler, workerOptions);
|
||||
for (const queueName of Object.values(QueueName)) {
|
||||
this.logger.debug(`Starting worker for queue: ${queueName}`);
|
||||
this.workers[queueName] = new Worker(
|
||||
queueName,
|
||||
(job) => this.eventRepository.emit('job.start', queueName, job as JobItem),
|
||||
{ ...bull.config, concurrency: 1 },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async run({ name, data }: JobItem) {
|
||||
const item = this.handlers[name as JobName];
|
||||
if (!item) {
|
||||
this.logger.warn(`Skipping unknown job: "${name}"`);
|
||||
return JobStatus.SKIPPED;
|
||||
}
|
||||
|
||||
return item.handler(data);
|
||||
}
|
||||
|
||||
addCronJob(name: string, expression: string, onTick: () => void, start = true): void {
|
||||
@ -138,11 +139,11 @@ export class JobRepository implements IJobRepository {
|
||||
true,
|
||||
);
|
||||
|
||||
this.schedulerReqistry.addCronJob(name, job);
|
||||
this.schedulerRegistry.addCronJob(name, job);
|
||||
}
|
||||
|
||||
updateCronJob(name: string, expression?: string, start?: boolean): void {
|
||||
const job = this.schedulerReqistry.getCronJob(name);
|
||||
const job = this.schedulerRegistry.getCronJob(name);
|
||||
if (expression) {
|
||||
job.setTime(new CronTime(expression));
|
||||
}
|
||||
@ -201,6 +202,10 @@ export class JobRepository implements IJobRepository {
|
||||
) as unknown as Promise<JobCounts>;
|
||||
}
|
||||
|
||||
private getQueueName(name: JobName) {
|
||||
return (this.handlers[name] as JobMapItem).queueName;
|
||||
}
|
||||
|
||||
async queueAll(items: JobItem[]): Promise<void> {
|
||||
if (items.length === 0) {
|
||||
return;
|
||||
@ -209,7 +214,7 @@ export class JobRepository implements IJobRepository {
|
||||
const promises = [];
|
||||
const itemsByQueue = {} as Record<string, (JobItem & { data: any; options: JobsOptions | undefined })[]>;
|
||||
for (const item of items) {
|
||||
const queueName = JOBS_TO_QUEUE[item.name];
|
||||
const queueName = this.getQueueName(item.name);
|
||||
const job = {
|
||||
name: item.name,
|
||||
data: item.data || {},
|
||||
@ -270,11 +275,11 @@ export class JobRepository implements IJobRepository {
|
||||
}
|
||||
|
||||
private getQueue(queue: QueueName): Queue {
|
||||
return this.moduleReference.get<Queue>(getQueueToken(queue), { strict: false });
|
||||
return this.moduleRef.get<Queue>(getQueueToken(queue), { strict: false });
|
||||
}
|
||||
|
||||
public async removeJob(jobId: string, name: JobName): Promise<IEntityJob | undefined> {
|
||||
const existingJob = await this.getQueue(JOBS_TO_QUEUE[name]).getJob(jobId);
|
||||
const existingJob = await this.getQueue(this.getQueueName(name)).getJob(jobId);
|
||||
if (!existingJob) {
|
||||
return;
|
||||
}
|
||||
|
@ -249,6 +249,7 @@ export class MapRepository implements IMapRepository {
|
||||
const input = createReadStream(filePath);
|
||||
let bufferGeodata: QueryDeepPartialEntity<GeodataPlacesEntity>[] = [];
|
||||
const lineReader = readLine.createInterface({ input });
|
||||
let count = 0;
|
||||
|
||||
for await (const line of lineReader) {
|
||||
const lineSplit = line.split('\t');
|
||||
@ -257,8 +258,12 @@ export class MapRepository implements IMapRepository {
|
||||
}
|
||||
const geoData = lineToEntityMapper(lineSplit);
|
||||
bufferGeodata.push(geoData);
|
||||
if (bufferGeodata.length > 1000) {
|
||||
if (bufferGeodata.length >= 1000) {
|
||||
await queryRunner.manager.upsert(GeodataPlacesEntity, bufferGeodata, ['id']);
|
||||
count += bufferGeodata.length;
|
||||
if (count % 10_000 === 0) {
|
||||
this.logger.log(`${count} geodata records imported`);
|
||||
}
|
||||
bufferGeodata = [];
|
||||
}
|
||||
}
|
||||
|
@ -126,6 +126,7 @@ export class MediaRepository implements IMediaRepository {
|
||||
rotation: this.parseInt(stream.rotation),
|
||||
isHDR: stream.color_transfer === 'smpte2084' || stream.color_transfer === 'arib-std-b67',
|
||||
bitrate: this.parseInt(stream.bit_rate),
|
||||
pixelFormat: stream.pix_fmt || 'yuv420p',
|
||||
})),
|
||||
audioStreams: results.streams
|
||||
.filter((stream) => stream.codec_type === 'audio')
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user