Compare commits

..

3 Commits

Author SHA1 Message Date
Alex The Bot
01ba859567 Version v1.109.1 2024-07-18 17:55:58 +00:00
Mert
173c9070c8 fix(ml): re-add worker env (#11192)
re-add worker env
2024-07-18 17:50:52 +00:00
Saschl
d37e8ede3b feat: optionally generate thumbnails for invalid images (#11126) 2024-07-18 12:07:22 -04:00
26 changed files with 88 additions and 39 deletions

6
cli/package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "@immich/cli",
"version": "2.2.9",
"version": "2.2.10",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@immich/cli",
"version": "2.2.9",
"version": "2.2.10",
"license": "GNU Affero General Public License version 3",
"dependencies": {
"fast-glob": "^3.3.2",
@@ -49,7 +49,7 @@
},
"../open-api/typescript-sdk": {
"name": "@immich/sdk",
"version": "1.109.0",
"version": "1.109.1",
"dev": true,
"license": "GNU Affero General Public License version 3",
"dependencies": {

View File

@@ -1,6 +1,6 @@
{
"name": "@immich/cli",
"version": "2.2.9",
"version": "2.2.10",
"description": "Command Line Interface (CLI) for Immich",
"type": "module",
"exports": "./dist/index.js",

View File

@@ -38,17 +38,18 @@ Regardless of filesystem, it is not recommended to use a network share for your
## General
| Variable | Description | Default | Containers | Workers |
| :---------------------------------- | :---------------------------------------------- | :--------------------------: | :----------------------- | :----------------- |
| `TZ` | Timezone | | server | microservices |
| `IMMICH_ENV` | Environment (production, development) | `production` | server, machine learning | api, microservices |
| `IMMICH_LOG_LEVEL` | Log Level (verbose, debug, log, warn, error) | `log` | server, machine learning | api, microservices |
| `IMMICH_MEDIA_LOCATION` | Media Location | `./upload`<sup>\*1</sup> | server | api, microservices |
| `IMMICH_CONFIG_FILE` | Path to config file | | server | api, microservices |
| `NO_COLOR` | Set to `true` to disable color-coded log output | `false` | server, machine learning | |
| `CPU_CORES` | Amount of cores available to the immich server | auto-detected cpu core count | server | |
| `IMMICH_API_METRICS_PORT` | Port for the OTEL metrics | `8081` | server | api |
| `IMMICH_MICROSERVICES_METRICS_PORT` | Port for the OTEL metrics | `8082` | server | microservices |
| Variable | Description | Default | Containers | Workers |
| :---------------------------------- | :-------------------------------------------------- | :--------------------------: | :----------------------- | :----------------- |
| `TZ` | Timezone | | server | microservices |
| `IMMICH_ENV` | Environment (production, development) | `production` | server, machine learning | api, microservices |
| `IMMICH_LOG_LEVEL` | Log Level (verbose, debug, log, warn, error) | `log` | server, machine learning | api, microservices |
| `IMMICH_MEDIA_LOCATION` | Media Location | `./upload`<sup>\*1</sup> | server | api, microservices |
| `IMMICH_CONFIG_FILE` | Path to config file | | server | api, microservices |
| `NO_COLOR` | Set to `true` to disable color-coded log output | `false` | server, machine learning | |
| `CPU_CORES` | Amount of cores available to the immich server | auto-detected cpu core count | server | |
| `IMMICH_API_METRICS_PORT` | Port for the OTEL metrics | `8081` | server | api |
| `IMMICH_MICROSERVICES_METRICS_PORT` | Port for the OTEL metrics | `8082` | server | microservices |
| `IMMICH_PROCESS_INVALID_IMAGES` | When `true`, generate thumbnails for invalid images | | server | microservices |
\*1: With the default `WORKDIR` of `/usr/src/app`, this path will resolve to `/usr/src/app/upload`.
It only need to be set if the Immich deployment method is changing.

View File

@@ -1,4 +1,8 @@
[
{
"label": "v1.109.1",
"url": "https://v1.109.1.archive.immich.app"
},
{
"label": "v1.109.0",
"url": "https://v1.109.0.archive.immich.app"

8
e2e/package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "immich-e2e",
"version": "1.109.0",
"version": "1.109.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "immich-e2e",
"version": "1.109.0",
"version": "1.109.1",
"license": "GNU Affero General Public License version 3",
"devDependencies": {
"@immich/cli": "file:../cli",
@@ -42,7 +42,7 @@
},
"../cli": {
"name": "@immich/cli",
"version": "2.2.9",
"version": "2.2.10",
"dev": true,
"license": "GNU Affero General Public License version 3",
"dependencies": {
@@ -86,7 +86,7 @@
},
"../open-api/typescript-sdk": {
"name": "@immich/sdk",
"version": "1.109.0",
"version": "1.109.1",
"dev": true,
"license": "GNU Affero General Public License version 3",
"dependencies": {

View File

@@ -1,6 +1,6 @@
{
"name": "immich-e2e",
"version": "1.109.0",
"version": "1.109.1",
"description": "",
"main": "index.js",
"type": "module",

View File

@@ -1,6 +1,6 @@
[tool.poetry]
name = "machine-learning"
version = "1.109.0"
version = "1.109.1"
description = ""
authors = ["Hau Tran <alex.tran1502@gmail.com>"]
readme = "README.md"

View File

@@ -12,6 +12,7 @@ fi
: "${IMMICH_HOST:=[::]}"
: "${IMMICH_PORT:=3003}"
: "${MACHINE_LEARNING_WORKERS:=1}"
gunicorn app.main:app \
-k app.config.CustomUvicornWorker \

View File

@@ -36,7 +36,7 @@ platform :android do
build_type: 'Release',
properties: {
"android.injected.version.code" => 149,
"android.injected.version.name" => "1.109.0",
"android.injected.version.name" => "1.109.1",
}
)
upload_to_play_store(skip_upload_apk: true, skip_upload_images: true, skip_upload_screenshots: true, aab: '../build/app/outputs/bundle/release/app-release.aab')

View File

@@ -19,7 +19,7 @@ platform :ios do
desc "iOS Release"
lane :release do
increment_version_number(
version_number: "1.109.0"
version_number: "1.109.1"
)
increment_build_number(
build_number: latest_testflight_build_number + 1,

View File

@@ -3,7 +3,7 @@ Immich API
This Dart package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project:
- API version: 1.109.0
- API version: 1.109.1
- Generator version: 7.5.0
- Build package: org.openapitools.codegen.languages.DartClientCodegen

View File

@@ -2,7 +2,7 @@ name: immich_mobile
description: Immich - selfhosted backup media file on mobile phone
publish_to: 'none'
version: 1.109.0+149
version: 1.109.1+149
environment:
sdk: '>=3.3.0 <4.0.0'

View File

@@ -7007,7 +7007,7 @@
"info": {
"title": "Immich",
"description": "Immich API",
"version": "1.109.0",
"version": "1.109.1",
"contact": {}
},
"tags": [],

View File

@@ -1,12 +1,12 @@
{
"name": "@immich/sdk",
"version": "1.109.0",
"version": "1.109.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@immich/sdk",
"version": "1.109.0",
"version": "1.109.1",
"license": "GNU Affero General Public License version 3",
"dependencies": {
"@oazapfts/runtime": "^1.0.2"

View File

@@ -1,6 +1,6 @@
{
"name": "@immich/sdk",
"version": "1.109.0",
"version": "1.109.1",
"description": "Auto-generated TypeScript SDK for the Immich API",
"type": "module",
"main": "./build/index.js",

View File

@@ -1,6 +1,6 @@
/**
* Immich
* 1.109.0
* 1.109.1
* DO NOT MODIFY - This file has been generated using oazapfts.
* See https://www.npmjs.com/package/oazapfts
*/

View File

@@ -1,12 +1,12 @@
{
"name": "immich",
"version": "1.109.0",
"version": "1.109.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "immich",
"version": "1.109.0",
"version": "1.109.1",
"license": "GNU Affero General Public License version 3",
"dependencies": {
"@nestjs/bullmq": "^10.0.1",

View File

@@ -1,6 +1,6 @@
{
"name": "immich",
"version": "1.109.0",
"version": "1.109.1",
"description": "",
"author": "",
"private": true,

View File

@@ -16,6 +16,7 @@ export interface ThumbnailOptions {
colorspace: string;
quality: number;
crop?: CropOptions;
processInvalidImages: boolean;
}
export interface VideoStreamInfo {

View File

@@ -45,7 +45,8 @@ export class MediaRepository implements IMediaRepository {
}
async generateThumbnail(input: string | Buffer, output: string, options: ThumbnailOptions): Promise<void> {
const pipeline = sharp(input, { failOn: 'error', limitInputPixels: false })
// some invalid images can still be processed by sharp, but we want to fail on them by default to avoid crashes
const pipeline = sharp(input, { failOn: options.processInvalidImages ? 'none' : 'error', limitInputPixels: false })
.pipelineColorspace(options.colorspace === Colorspace.SRGB ? 'srgb' : 'rgb16')
.rotate();

View File

@@ -296,6 +296,7 @@ describe(MediaService.name, () => {
format,
quality: 80,
colorspace: Colorspace.SRGB,
processInvalidImages: false,
});
expect(assetMock.update).toHaveBeenCalledWith({ id: 'asset-id', previewPath });
});
@@ -326,6 +327,7 @@ describe(MediaService.name, () => {
format: ImageFormat.JPEG,
quality: 80,
colorspace: Colorspace.P3,
processInvalidImages: false,
},
);
expect(assetMock.update).toHaveBeenCalledWith({
@@ -468,6 +470,7 @@ describe(MediaService.name, () => {
format,
quality: 80,
colorspace: Colorspace.SRGB,
processInvalidImages: false,
});
expect(assetMock.update).toHaveBeenCalledWith({ id: 'asset-id', thumbnailPath });
},
@@ -498,6 +501,7 @@ describe(MediaService.name, () => {
size: 250,
quality: 80,
colorspace: Colorspace.P3,
processInvalidImages: false,
},
);
expect(assetMock.update).toHaveBeenCalledWith({
@@ -524,6 +528,7 @@ describe(MediaService.name, () => {
size: 250,
quality: 80,
colorspace: Colorspace.P3,
processInvalidImages: false,
},
],
]);
@@ -548,6 +553,7 @@ describe(MediaService.name, () => {
size: 250,
quality: 80,
colorspace: Colorspace.P3,
processInvalidImages: false,
},
],
]);
@@ -570,6 +576,7 @@ describe(MediaService.name, () => {
size: 250,
quality: 80,
colorspace: Colorspace.P3,
processInvalidImages: false,
},
);
expect(mediaMock.getImageDimensions).not.toHaveBeenCalled();
@@ -590,11 +597,34 @@ describe(MediaService.name, () => {
size: 250,
quality: 80,
colorspace: Colorspace.P3,
processInvalidImages: false,
},
);
expect(mediaMock.getImageDimensions).not.toHaveBeenCalled();
});
it('should process invalid images if enabled', async () => {
vi.stubEnv('IMMICH_PROCESS_INVALID_IMAGES', 'true');
assetMock.getByIds.mockResolvedValue([assetStub.imageDng]);
await sut.handleGenerateThumbnail({ id: assetStub.image.id });
expect(mediaMock.generateThumbnail).toHaveBeenCalledWith(
assetStub.imageDng.originalPath,
'upload/thumbs/user-id/as/se/asset-id-thumbnail.webp',
{
format: ImageFormat.WEBP,
size: 250,
quality: 80,
colorspace: Colorspace.P3,
processInvalidImages: true,
},
);
expect(mediaMock.getImageDimensions).not.toHaveBeenCalled();
vi.unstubAllEnvs();
});
describe('handleGenerateThumbhash', () => {
it('should skip thumbhash generation if asset not found', async () => {
assetMock.getByIds.mockResolvedValue([]);

View File

@@ -199,7 +199,13 @@ export class MediaService {
try {
const useExtracted = didExtract && (await this.shouldUseExtractedImage(extractedPath, image.previewSize));
const colorspace = this.isSRGB(asset) ? Colorspace.SRGB : image.colorspace;
const imageOptions = { format, size, colorspace, quality: image.quality };
const imageOptions = {
format,
size,
colorspace,
quality: image.quality,
processInvalidImages: process.env.IMMICH_PROCESS_INVALID_IMAGES === 'true',
};
const outputPath = useExtracted ? extractedPath : asset.originalPath;
await this.mediaRepository.generateThumbnail(outputPath, path, imageOptions);

View File

@@ -958,6 +958,7 @@ describe(PersonService.name, () => {
width: 274,
height: 274,
},
processInvalidImages: false,
},
);
expect(personMock.update).toHaveBeenCalledWith({
@@ -987,6 +988,7 @@ describe(PersonService.name, () => {
width: 510,
height: 510,
},
processInvalidImages: false,
},
);
});
@@ -1012,6 +1014,7 @@ describe(PersonService.name, () => {
width: 408,
height: 408,
},
processInvalidImages: false,
},
);
});
@@ -1038,6 +1041,7 @@ describe(PersonService.name, () => {
width: 588,
height: 588,
},
processInvalidImages: false,
},
);
});

View File

@@ -559,6 +559,7 @@ export class PersonService {
colorspace: image.colorspace,
quality: image.quality,
crop: this.getCrop({ old: { width: oldWidth, height: oldHeight }, new: { width, height } }, { x1, y1, x2, y2 }),
processInvalidImages: process.env.IMMICH_PROCESS_INVALID_IMAGES === 'true',
} as const;
await this.mediaRepository.generateThumbnail(inputPath, thumbnailPath, thumbnailOptions);

6
web/package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "immich-web",
"version": "1.109.0",
"version": "1.109.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "immich-web",
"version": "1.109.0",
"version": "1.109.1",
"license": "GNU Affero General Public License version 3",
"dependencies": {
"@formatjs/icu-messageformat-parser": "^2.7.8",
@@ -70,7 +70,7 @@
},
"../open-api/typescript-sdk": {
"name": "@immich/sdk",
"version": "1.109.0",
"version": "1.109.1",
"license": "GNU Affero General Public License version 3",
"dependencies": {
"@oazapfts/runtime": "^1.0.2"

View File

@@ -1,6 +1,6 @@
{
"name": "immich-web",
"version": "1.109.0",
"version": "1.109.1",
"license": "GNU Affero General Public License version 3",
"scripts": {
"dev": "vite dev --host 0.0.0.0 --port 3000",