mirror of
https://github.com/immich-app/immich.git
synced 2024-12-29 15:11:58 +00:00
feat(server): Storage template support album condition (#12000)
feat(server): Storage template support album condition ([Request](https://github.com/immich-app/immich/discussions/11999))
This commit is contained in:
parent
9894b9513b
commit
b051b29eca
2 changed files with 45 additions and 3 deletions
|
@ -15,6 +15,7 @@ import { IStorageRepository } from 'src/interfaces/storage.interface';
|
|||
import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface';
|
||||
import { IUserRepository } from 'src/interfaces/user.interface';
|
||||
import { StorageTemplateService } from 'src/services/storage-template.service';
|
||||
import { albumStub } from 'test/fixtures/album.stub';
|
||||
import { assetStub } from 'test/fixtures/asset.stub';
|
||||
import { userStub } from 'test/fixtures/user.stub';
|
||||
import { newAlbumRepositoryMock } from 'test/repositories/album.repository.mock';
|
||||
|
@ -83,7 +84,7 @@ describe(StorageTemplateService.name, () => {
|
|||
newConfig: {
|
||||
storageTemplate: {
|
||||
template:
|
||||
'{{y}}{{M}}{{W}}{{d}}{{h}}{{m}}{{s}}{{filename}}{{ext}}{{filetype}}{{filetypefull}}{{assetId}}{{album}}',
|
||||
'{{y}}{{M}}{{W}}{{d}}{{h}}{{m}}{{s}}{{filename}}{{ext}}{{filetype}}{{filetypefull}}{{assetId}}{{#if album}}{{album}}{{else}}other{{/if}}',
|
||||
},
|
||||
} as SystemConfig,
|
||||
oldConfig: {} as SystemConfig,
|
||||
|
@ -163,6 +164,47 @@ describe(StorageTemplateService.name, () => {
|
|||
originalPath: newMotionPicturePath,
|
||||
});
|
||||
});
|
||||
it('Should use handlebar if condition for album', async () => {
|
||||
const asset = assetStub.image;
|
||||
const user = userStub.user1;
|
||||
const album = albumStub.oneAsset;
|
||||
const config = structuredClone(defaults);
|
||||
config.storageTemplate.template = '{{y}}/{{#if album}}{{album}}{{else}}other/{{MM}}{{/if}}/{{filename}}';
|
||||
SystemConfigCore.create(systemMock, loggerMock).config$.next(config);
|
||||
|
||||
userMock.get.mockResolvedValue(user);
|
||||
assetMock.getByIds.mockResolvedValueOnce([asset]);
|
||||
albumMock.getByAssetId.mockResolvedValueOnce([album]);
|
||||
|
||||
expect(await sut.handleMigrationSingle({ id: asset.id })).toBe(JobStatus.SUCCESS);
|
||||
|
||||
expect(moveMock.create).toHaveBeenCalledWith({
|
||||
entityId: asset.id,
|
||||
newPath: `upload/library/${user.id}/${asset.fileCreatedAt.getFullYear()}/${album.albumName}/${asset.originalFileName}`,
|
||||
oldPath: asset.originalPath,
|
||||
pathType: AssetPathType.ORIGINAL,
|
||||
});
|
||||
});
|
||||
it('Should use handlebar else condition for album', async () => {
|
||||
const asset = assetStub.image;
|
||||
const user = userStub.user1;
|
||||
const config = structuredClone(defaults);
|
||||
config.storageTemplate.template = '{{y}}/{{#if album}}{{album}}{{else}}other//{{MM}}{{/if}}/{{filename}}';
|
||||
SystemConfigCore.create(systemMock, loggerMock).config$.next(config);
|
||||
|
||||
userMock.get.mockResolvedValue(user);
|
||||
assetMock.getByIds.mockResolvedValueOnce([asset]);
|
||||
|
||||
expect(await sut.handleMigrationSingle({ id: asset.id })).toBe(JobStatus.SUCCESS);
|
||||
|
||||
const month = (asset.fileCreatedAt.getMonth() + 1).toString().padStart(2, '0');
|
||||
expect(moveMock.create).toHaveBeenCalledWith({
|
||||
entityId: asset.id,
|
||||
newPath: `upload/library/${user.id}/${asset.fileCreatedAt.getFullYear()}/other/${month}/${asset.originalFileName}`,
|
||||
oldPath: asset.originalPath,
|
||||
pathType: AssetPathType.ORIGINAL,
|
||||
});
|
||||
});
|
||||
it('should migrate previously failed move from original path when it still exists', async () => {
|
||||
userMock.get.mockResolvedValue(userStub.user1);
|
||||
const previousFailedNewPath = `upload/library/${userStub.user1.id}/2023/Feb/${assetStub.image.id}.jpg`;
|
||||
|
|
|
@ -308,7 +308,7 @@ export class StorageTemplateService {
|
|||
filetypefull: asset.type == AssetType.IMAGE ? 'IMAGE' : 'VIDEO',
|
||||
assetId: asset.id,
|
||||
//just throw into the root if it doesn't belong to an album
|
||||
album: (albumName && sanitize(albumName.replaceAll(/\.+/g, ''))) || '.',
|
||||
album: (albumName && sanitize(albumName.replaceAll(/\.+/g, ''))) || '',
|
||||
};
|
||||
|
||||
const systemTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
||||
|
@ -329,6 +329,6 @@ export class StorageTemplateService {
|
|||
substitutions[token] = dt.toFormat(token);
|
||||
}
|
||||
|
||||
return template(substitutions);
|
||||
return template(substitutions).replaceAll(/\/{2,}/gm, '/');
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue