1
0
forked from Cutlery/immich

update target paths, fix tests

This commit is contained in:
mertalev 2024-03-24 02:16:09 -04:00
parent 35a2aa472a
commit a9b90787b2
No known key found for this signature in database
GPG Key ID: 9181CD92C0A1C5E3
4 changed files with 80 additions and 69 deletions

View File

@ -4,6 +4,7 @@ import { SystemConfigCore } from 'src/cores/system-config.core';
import { AssetEntity } from 'src/entities/asset.entity'; import { AssetEntity } from 'src/entities/asset.entity';
import { AssetPathType, PathType, PersonPathType } from 'src/entities/move.entity'; import { AssetPathType, PathType, PersonPathType } from 'src/entities/move.entity';
import { PersonEntity } from 'src/entities/person.entity'; import { PersonEntity } from 'src/entities/person.entity';
import { ImageFormat } from 'src/entities/system-config.entity';
import { IAssetRepository } from 'src/interfaces/asset.interface'; import { IAssetRepository } from 'src/interfaces/asset.interface';
import { ICryptoRepository } from 'src/interfaces/crypto.interface'; import { ICryptoRepository } from 'src/interfaces/crypto.interface';
import { IMoveRepository } from 'src/interfaces/move.interface'; import { IMoveRepository } from 'src/interfaces/move.interface';
@ -95,8 +96,8 @@ export class StorageCore {
return StorageCore.getNestedPath(StorageFolder.THUMBNAILS, person.ownerId, `${person.id}.jpeg`); return StorageCore.getNestedPath(StorageFolder.THUMBNAILS, person.ownerId, `${person.id}.jpeg`);
} }
static getImagePath(asset: AssetEntity, type: GeneratedImageType) { static getImagePath(asset: AssetEntity, type: GeneratedImageType, format: ImageFormat) {
return StorageCore.getNestedPath(StorageFolder.THUMBNAILS, asset.ownerId, `${asset.id}.${type}`); return StorageCore.getNestedPath(StorageFolder.THUMBNAILS, asset.ownerId, `${asset.id}-${type}.${format}`);
} }
static getEncodedVideoPath(asset: AssetEntity) { static getEncodedVideoPath(asset: AssetEntity) {
@ -119,34 +120,23 @@ export class StorageCore {
return path.startsWith(THUMBNAIL_DIR) || path.startsWith(ENCODED_VIDEO_DIR); return path.startsWith(THUMBNAIL_DIR) || path.startsWith(ENCODED_VIDEO_DIR);
} }
async moveAssetFile(asset: AssetEntity, pathType: GeneratedAssetType) { async moveAssetImage(asset: AssetEntity, pathType: GeneratedAssetType, format: ImageFormat) {
const { id: entityId, previewPath, thumbnailPath, encodedVideoPath } = asset; const { id: entityId, previewPath, thumbnailPath } = asset;
switch (pathType) { return this.moveFile({
case AssetPathType.PREVIEW: { entityId,
return this.moveFile({ pathType,
entityId, oldPath: pathType === AssetPathType.PREVIEW ? previewPath : thumbnailPath,
pathType, newPath: StorageCore.getImagePath(asset, AssetPathType.THUMBNAIL, format),
oldPath: previewPath, });
newPath: StorageCore.getImagePath(asset, AssetPathType.PREVIEW), }
});
} async moveAssetVideo(asset: AssetEntity) {
case AssetPathType.THUMBNAIL: { return this.moveFile({
return this.moveFile({ entityId: asset.id,
entityId, pathType: AssetPathType.ENCODED_VIDEO,
pathType, oldPath: asset.encodedVideoPath,
oldPath: thumbnailPath, newPath: StorageCore.getEncodedVideoPath(asset),
newPath: StorageCore.getImagePath(asset, AssetPathType.THUMBNAIL), });
});
}
case AssetPathType.ENCODED_VIDEO: {
return this.moveFile({
entityId,
pathType,
oldPath: encodedVideoPath,
newPath: StorageCore.getEncodedVideoPath(asset),
});
}
}
} }
async movePersonFile(person: PersonEntity, pathType: PersonPathType) { async movePersonFile(person: PersonEntity, pathType: PersonPathType) {

View File

@ -4,6 +4,7 @@ import { ExifEntity } from 'src/entities/exif.entity';
import { import {
AudioCodec, AudioCodec,
Colorspace, Colorspace,
ImageFormat,
SystemConfigKey, SystemConfigKey,
ToneMapping, ToneMapping,
TranscodeHWAccel, TranscodeHWAccel,
@ -214,15 +215,19 @@ describe(MediaService.name, () => {
await sut.handleGeneratePreview({ id: assetStub.image.id }); await sut.handleGeneratePreview({ id: assetStub.image.id });
expect(storageMock.mkdirSync).toHaveBeenCalledWith('upload/thumbs/user-id/as/se'); expect(storageMock.mkdirSync).toHaveBeenCalledWith('upload/thumbs/user-id/as/se');
expect(mediaMock.resize).toHaveBeenCalledWith('/original/path.jpg', 'upload/thumbs/user-id/as/se/asset-id.jpeg', { expect(mediaMock.resize).toHaveBeenCalledWith(
size: 1440, '/original/path.jpg',
format: 'jpeg', 'upload/thumbs/user-id/as/se/asset-id-preview.jpeg',
quality: 80, {
colorspace: Colorspace.SRGB, size: 1440,
}); format: ImageFormat.JPEG,
quality: 80,
colorspace: Colorspace.SRGB,
},
);
expect(assetMock.update).toHaveBeenCalledWith({ expect(assetMock.update).toHaveBeenCalledWith({
id: 'asset-id', id: 'asset-id',
previewPath: 'upload/thumbs/user-id/as/se/asset-id.jpeg', previewPath: 'upload/thumbs/user-id/as/se/asset-id-preview.jpeg',
}); });
}); });
@ -233,15 +238,19 @@ describe(MediaService.name, () => {
await sut.handleGeneratePreview({ id: assetStub.image.id }); await sut.handleGeneratePreview({ id: assetStub.image.id });
expect(storageMock.mkdirSync).toHaveBeenCalledWith('upload/thumbs/user-id/as/se'); expect(storageMock.mkdirSync).toHaveBeenCalledWith('upload/thumbs/user-id/as/se');
expect(mediaMock.resize).toHaveBeenCalledWith('/original/path.jpg', 'upload/thumbs/user-id/as/se/asset-id.jpeg', { expect(mediaMock.resize).toHaveBeenCalledWith(
size: 1440, '/original/path.jpg',
format: 'jpeg', 'upload/thumbs/user-id/as/se/asset-id-preview.jpeg',
quality: 80, {
colorspace: Colorspace.P3, size: 1440,
}); format: ImageFormat.JPEG,
quality: 80,
colorspace: Colorspace.P3,
},
);
expect(assetMock.update).toHaveBeenCalledWith({ expect(assetMock.update).toHaveBeenCalledWith({
id: 'asset-id', id: 'asset-id',
previewPath: 'upload/thumbs/user-id/as/se/asset-id.jpeg', previewPath: 'upload/thumbs/user-id/as/se/asset-id-preview.jpeg',
}); });
}); });
@ -253,7 +262,7 @@ describe(MediaService.name, () => {
expect(storageMock.mkdirSync).toHaveBeenCalledWith('upload/thumbs/user-id/as/se'); expect(storageMock.mkdirSync).toHaveBeenCalledWith('upload/thumbs/user-id/as/se');
expect(mediaMock.transcode).toHaveBeenCalledWith( expect(mediaMock.transcode).toHaveBeenCalledWith(
'/original/path.ext', '/original/path.ext',
'upload/thumbs/user-id/as/se/asset-id.jpeg', 'upload/thumbs/user-id/as/se/asset-id-preview.jpeg',
{ {
inputOptions: ['-ss 00:00:00', '-sws_flags accurate_rnd+bitexact+full_chroma_int'], inputOptions: ['-ss 00:00:00', '-sws_flags accurate_rnd+bitexact+full_chroma_int'],
outputOptions: [ outputOptions: [
@ -266,7 +275,7 @@ describe(MediaService.name, () => {
); );
expect(assetMock.update).toHaveBeenCalledWith({ expect(assetMock.update).toHaveBeenCalledWith({
id: 'asset-id', id: 'asset-id',
previewPath: 'upload/thumbs/user-id/as/se/asset-id.jpeg', previewPath: 'upload/thumbs/user-id/as/se/asset-id-preview.jpeg',
}); });
}); });
@ -278,7 +287,7 @@ describe(MediaService.name, () => {
expect(storageMock.mkdirSync).toHaveBeenCalledWith('upload/thumbs/user-id/as/se'); expect(storageMock.mkdirSync).toHaveBeenCalledWith('upload/thumbs/user-id/as/se');
expect(mediaMock.transcode).toHaveBeenCalledWith( expect(mediaMock.transcode).toHaveBeenCalledWith(
'/original/path.ext', '/original/path.ext',
'upload/thumbs/user-id/as/se/asset-id.jpeg', 'upload/thumbs/user-id/as/se/asset-id-preview.jpeg',
{ {
inputOptions: ['-ss 00:00:00', '-sws_flags accurate_rnd+bitexact+full_chroma_int'], inputOptions: ['-ss 00:00:00', '-sws_flags accurate_rnd+bitexact+full_chroma_int'],
outputOptions: [ outputOptions: [
@ -291,7 +300,7 @@ describe(MediaService.name, () => {
); );
expect(assetMock.update).toHaveBeenCalledWith({ expect(assetMock.update).toHaveBeenCalledWith({
id: 'asset-id', id: 'asset-id',
previewPath: 'upload/thumbs/user-id/as/se/asset-id.jpeg', previewPath: 'upload/thumbs/user-id/as/se/asset-id-preview.jpeg',
}); });
}); });
@ -306,7 +315,7 @@ describe(MediaService.name, () => {
expect(mediaMock.transcode).toHaveBeenCalledWith( expect(mediaMock.transcode).toHaveBeenCalledWith(
'/original/path.ext', '/original/path.ext',
'upload/thumbs/user-id/as/se/asset-id.jpeg', 'upload/thumbs/user-id/as/se/asset-id-preview.jpeg',
{ {
inputOptions: ['-ss 00:00:00', '-sws_flags accurate_rnd+bitexact+full_chroma_int'], inputOptions: ['-ss 00:00:00', '-sws_flags accurate_rnd+bitexact+full_chroma_int'],
outputOptions: [ outputOptions: [
@ -337,15 +346,19 @@ describe(MediaService.name, () => {
assetMock.getByIds.mockResolvedValue([assetStub.image]); assetMock.getByIds.mockResolvedValue([assetStub.image]);
await sut.handleGenerateThumbnail({ id: assetStub.image.id }); await sut.handleGenerateThumbnail({ id: assetStub.image.id });
expect(mediaMock.resize).toHaveBeenCalledWith('/original/path.jpg', 'upload/thumbs/user-id/as/se/asset-id.webp', { expect(mediaMock.resize).toHaveBeenCalledWith(
format: 'webp', '/original/path.jpg',
size: 250, 'upload/thumbs/user-id/as/se/asset-id-thumbnail.webp',
quality: 80, {
colorspace: Colorspace.SRGB, format: ImageFormat.WEBP,
}); size: 250,
quality: 80,
colorspace: Colorspace.SRGB,
},
);
expect(assetMock.update).toHaveBeenCalledWith({ expect(assetMock.update).toHaveBeenCalledWith({
id: 'asset-id', id: 'asset-id',
thumbnailPath: 'upload/thumbs/user-id/as/se/asset-id.webp', thumbnailPath: 'upload/thumbs/user-id/as/se/asset-id-thumbnail.webp',
}); });
}); });
}); });
@ -357,15 +370,19 @@ describe(MediaService.name, () => {
await sut.handleGenerateThumbnail({ id: assetStub.image.id }); await sut.handleGenerateThumbnail({ id: assetStub.image.id });
expect(storageMock.mkdirSync).toHaveBeenCalledWith('upload/thumbs/user-id/as/se'); expect(storageMock.mkdirSync).toHaveBeenCalledWith('upload/thumbs/user-id/as/se');
expect(mediaMock.resize).toHaveBeenCalledWith('/original/path.jpg', 'upload/thumbs/user-id/as/se/asset-id.webp', { expect(mediaMock.resize).toHaveBeenCalledWith(
format: 'webp', '/original/path.jpg',
size: 250, 'upload/thumbs/user-id/as/se/asset-id-thumbnail.webp',
quality: 80, {
colorspace: Colorspace.P3, format: ImageFormat.WEBP,
}); size: 250,
quality: 80,
colorspace: Colorspace.P3,
},
);
expect(assetMock.update).toHaveBeenCalledWith({ expect(assetMock.update).toHaveBeenCalledWith({
id: 'asset-id', id: 'asset-id',
thumbnailPath: 'upload/thumbs/user-id/as/se/asset-id.webp', thumbnailPath: 'upload/thumbs/user-id/as/se/asset-id-thumbnail.webp',
}); });
}); });

View File

@ -153,14 +153,15 @@ export class MediaService {
} }
async handleAssetMigration({ id }: IEntityJob): Promise<JobStatus> { async handleAssetMigration({ id }: IEntityJob): Promise<JobStatus> {
const { thumbnail } = await this.configCore.getConfig();
const [asset] = await this.assetRepository.getByIds([id]); const [asset] = await this.assetRepository.getByIds([id]);
if (!asset) { if (!asset) {
return JobStatus.FAILED; return JobStatus.FAILED;
} }
await this.storageCore.moveAssetFile(asset, AssetPathType.PREVIEW); await this.storageCore.moveAssetImage(asset, AssetPathType.PREVIEW, thumbnail.previewFormat);
await this.storageCore.moveAssetFile(asset, AssetPathType.THUMBNAIL); await this.storageCore.moveAssetImage(asset, AssetPathType.THUMBNAIL, thumbnail.thumbnailFormat);
await this.storageCore.moveAssetFile(asset, AssetPathType.ENCODED_VIDEO); await this.storageCore.moveAssetVideo(asset);
return JobStatus.SUCCESS; return JobStatus.SUCCESS;
} }
@ -179,7 +180,7 @@ export class MediaService {
private async generateThumbnail(asset: AssetEntity, type: GeneratedImageType, format: ImageFormat) { private async generateThumbnail(asset: AssetEntity, type: GeneratedImageType, format: ImageFormat) {
const { thumbnail, ffmpeg } = await this.configCore.getConfig(); const { thumbnail, ffmpeg } = await this.configCore.getConfig();
const size = type === AssetPathType.PREVIEW ? thumbnail.previewSize : thumbnail.thumbnailSize; const size = type === AssetPathType.PREVIEW ? thumbnail.previewSize : thumbnail.thumbnailSize;
const path = StorageCore.getImagePath(asset, type); const path = StorageCore.getImagePath(asset, type, format);
this.storageCore.ensureFolders(path); this.storageCore.ensureFolders(path);
switch (asset.type) { switch (asset.type) {

View File

@ -4,6 +4,7 @@ import {
AudioCodec, AudioCodec,
CQMode, CQMode,
Colorspace, Colorspace,
ImageFormat,
LogLevel, LogLevel,
SystemConfig, SystemConfig,
SystemConfigEntity, SystemConfigEntity,
@ -120,8 +121,10 @@ const updatedConfig = Object.freeze<SystemConfig>({
template: '{{y}}/{{y}}-{{MM}}-{{dd}}/{{filename}}', template: '{{y}}/{{y}}-{{MM}}-{{dd}}/{{filename}}',
}, },
thumbnail: { thumbnail: {
webpSize: 250, thumbnailFormat: ImageFormat.WEBP,
jpegSize: 1440, thumbnailSize: 250,
previewFormat: ImageFormat.JPEG,
previewSize: 1440,
quality: 80, quality: 80,
colorspace: Colorspace.P3, colorspace: Colorspace.P3,
}, },