mirror of
https://github.com/immich-app/immich.git
synced 2025-05-24 01:12:58 -04:00
refactor: more job query stuff (#17658)
This commit is contained in:
parent
85c2d36d99
commit
1bbfacfc09
@ -183,7 +183,7 @@ from
|
|||||||
where
|
where
|
||||||
"assets"."id" = $1
|
"assets"."id" = $1
|
||||||
|
|
||||||
-- AssetJobRepository.getAlbumThumbnailFile
|
-- AssetJobRepository.getAlbumThumbnailFiles
|
||||||
select
|
select
|
||||||
"asset_files"."id",
|
"asset_files"."id",
|
||||||
"asset_files"."path",
|
"asset_files"."path",
|
||||||
@ -192,6 +192,32 @@ from
|
|||||||
"asset_files"
|
"asset_files"
|
||||||
where
|
where
|
||||||
"asset_files"."assetId" = $1
|
"asset_files"."assetId" = $1
|
||||||
|
and "asset_files"."type" = $2
|
||||||
|
|
||||||
|
-- AssetJobRepository.getForClipEncoding
|
||||||
|
select
|
||||||
|
"assets"."id",
|
||||||
|
"assets"."isVisible",
|
||||||
|
(
|
||||||
|
select
|
||||||
|
coalesce(json_agg(agg), '[]')
|
||||||
|
from
|
||||||
|
(
|
||||||
|
select
|
||||||
|
"asset_files"."id",
|
||||||
|
"asset_files"."path",
|
||||||
|
"asset_files"."type"
|
||||||
|
from
|
||||||
|
"asset_files"
|
||||||
|
where
|
||||||
|
"asset_files"."assetId" = "assets"."id"
|
||||||
|
and "asset_files"."type" = $1
|
||||||
|
) as agg
|
||||||
|
) as "files"
|
||||||
|
from
|
||||||
|
"assets"
|
||||||
|
where
|
||||||
|
"assets"."id" = $2
|
||||||
|
|
||||||
-- AssetJobRepository.getForStorageTemplateJob
|
-- AssetJobRepository.getForStorageTemplateJob
|
||||||
select
|
select
|
||||||
|
@ -117,9 +117,24 @@ export class AssetJobRepository {
|
|||||||
.executeTakeFirst();
|
.executeTakeFirst();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GenerateSql({ params: [DummyValue.UUID, AssetFileType.THUMBNAIL] })
|
||||||
|
getAlbumThumbnailFiles(id: string, fileType?: AssetFileType) {
|
||||||
|
return this.db
|
||||||
|
.selectFrom('asset_files')
|
||||||
|
.select(columns.assetFiles)
|
||||||
|
.where('asset_files.assetId', '=', id)
|
||||||
|
.$if(!!fileType, (qb) => qb.where('asset_files.type', '=', fileType!))
|
||||||
|
.execute();
|
||||||
|
}
|
||||||
|
|
||||||
@GenerateSql({ params: [DummyValue.UUID] })
|
@GenerateSql({ params: [DummyValue.UUID] })
|
||||||
getAlbumThumbnailFile(id: string) {
|
getForClipEncoding(id: string) {
|
||||||
return this.db.selectFrom('asset_files').select(columns.assetFiles).where('asset_files.assetId', '=', id).execute();
|
return this.db
|
||||||
|
.selectFrom('assets')
|
||||||
|
.select(['assets.id', 'assets.isVisible'])
|
||||||
|
.select((eb) => withFiles(eb, AssetFileType.PREVIEW))
|
||||||
|
.where('assets.id', '=', id)
|
||||||
|
.executeTakeFirst();
|
||||||
}
|
}
|
||||||
|
|
||||||
private storageTemplateAssetQuery() {
|
private storageTemplateAssetQuery() {
|
||||||
|
@ -412,11 +412,12 @@ describe(NotificationService.name, () => {
|
|||||||
});
|
});
|
||||||
mocks.systemMetadata.get.mockResolvedValue({ server: {} });
|
mocks.systemMetadata.get.mockResolvedValue({ server: {} });
|
||||||
mocks.notification.renderEmail.mockResolvedValue({ html: '', text: '' });
|
mocks.notification.renderEmail.mockResolvedValue({ html: '', text: '' });
|
||||||
mocks.assetJob.getAlbumThumbnailFile.mockResolvedValue([]);
|
mocks.assetJob.getAlbumThumbnailFiles.mockResolvedValue([]);
|
||||||
|
|
||||||
await expect(sut.handleAlbumInvite({ id: '', recipientId: '' })).resolves.toBe(JobStatus.SUCCESS);
|
await expect(sut.handleAlbumInvite({ id: '', recipientId: '' })).resolves.toBe(JobStatus.SUCCESS);
|
||||||
expect(mocks.assetJob.getAlbumThumbnailFile).toHaveBeenCalledWith(
|
expect(mocks.assetJob.getAlbumThumbnailFiles).toHaveBeenCalledWith(
|
||||||
albumStub.emptyWithValidThumbnail.albumThumbnailAssetId,
|
albumStub.emptyWithValidThumbnail.albumThumbnailAssetId,
|
||||||
|
AssetFileType.THUMBNAIL,
|
||||||
);
|
);
|
||||||
expect(mocks.job.queue).toHaveBeenCalledWith({
|
expect(mocks.job.queue).toHaveBeenCalledWith({
|
||||||
name: JobName.SEND_EMAIL,
|
name: JobName.SEND_EMAIL,
|
||||||
@ -440,13 +441,14 @@ describe(NotificationService.name, () => {
|
|||||||
});
|
});
|
||||||
mocks.systemMetadata.get.mockResolvedValue({ server: {} });
|
mocks.systemMetadata.get.mockResolvedValue({ server: {} });
|
||||||
mocks.notification.renderEmail.mockResolvedValue({ html: '', text: '' });
|
mocks.notification.renderEmail.mockResolvedValue({ html: '', text: '' });
|
||||||
mocks.assetJob.getAlbumThumbnailFile.mockResolvedValue([
|
mocks.assetJob.getAlbumThumbnailFiles.mockResolvedValue([
|
||||||
{ id: '1', type: AssetFileType.THUMBNAIL, path: 'path-to-thumb.jpg' },
|
{ id: '1', type: AssetFileType.THUMBNAIL, path: 'path-to-thumb.jpg' },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await expect(sut.handleAlbumInvite({ id: '', recipientId: '' })).resolves.toBe(JobStatus.SUCCESS);
|
await expect(sut.handleAlbumInvite({ id: '', recipientId: '' })).resolves.toBe(JobStatus.SUCCESS);
|
||||||
expect(mocks.assetJob.getAlbumThumbnailFile).toHaveBeenCalledWith(
|
expect(mocks.assetJob.getAlbumThumbnailFiles).toHaveBeenCalledWith(
|
||||||
albumStub.emptyWithValidThumbnail.albumThumbnailAssetId,
|
albumStub.emptyWithValidThumbnail.albumThumbnailAssetId,
|
||||||
|
AssetFileType.THUMBNAIL,
|
||||||
);
|
);
|
||||||
expect(mocks.job.queue).toHaveBeenCalledWith({
|
expect(mocks.job.queue).toHaveBeenCalledWith({
|
||||||
name: JobName.SEND_EMAIL,
|
name: JobName.SEND_EMAIL,
|
||||||
@ -470,11 +472,12 @@ describe(NotificationService.name, () => {
|
|||||||
});
|
});
|
||||||
mocks.systemMetadata.get.mockResolvedValue({ server: {} });
|
mocks.systemMetadata.get.mockResolvedValue({ server: {} });
|
||||||
mocks.notification.renderEmail.mockResolvedValue({ html: '', text: '' });
|
mocks.notification.renderEmail.mockResolvedValue({ html: '', text: '' });
|
||||||
mocks.assetJob.getAlbumThumbnailFile.mockResolvedValue(assetStub.image.files);
|
mocks.assetJob.getAlbumThumbnailFiles.mockResolvedValue([assetStub.image.files[2]]);
|
||||||
|
|
||||||
await expect(sut.handleAlbumInvite({ id: '', recipientId: '' })).resolves.toBe(JobStatus.SUCCESS);
|
await expect(sut.handleAlbumInvite({ id: '', recipientId: '' })).resolves.toBe(JobStatus.SUCCESS);
|
||||||
expect(mocks.assetJob.getAlbumThumbnailFile).toHaveBeenCalledWith(
|
expect(mocks.assetJob.getAlbumThumbnailFiles).toHaveBeenCalledWith(
|
||||||
albumStub.emptyWithValidThumbnail.albumThumbnailAssetId,
|
albumStub.emptyWithValidThumbnail.albumThumbnailAssetId,
|
||||||
|
AssetFileType.THUMBNAIL,
|
||||||
);
|
);
|
||||||
expect(mocks.job.queue).toHaveBeenCalledWith({
|
expect(mocks.job.queue).toHaveBeenCalledWith({
|
||||||
name: JobName.SEND_EMAIL,
|
name: JobName.SEND_EMAIL,
|
||||||
@ -506,7 +509,7 @@ describe(NotificationService.name, () => {
|
|||||||
});
|
});
|
||||||
mocks.user.get.mockResolvedValueOnce(userStub.user1);
|
mocks.user.get.mockResolvedValueOnce(userStub.user1);
|
||||||
mocks.notification.renderEmail.mockResolvedValue({ html: '', text: '' });
|
mocks.notification.renderEmail.mockResolvedValue({ html: '', text: '' });
|
||||||
mocks.assetJob.getAlbumThumbnailFile.mockResolvedValue([]);
|
mocks.assetJob.getAlbumThumbnailFiles.mockResolvedValue([]);
|
||||||
|
|
||||||
await sut.handleAlbumUpdate({ id: '', recipientIds: [userStub.user1.id] });
|
await sut.handleAlbumUpdate({ id: '', recipientIds: [userStub.user1.id] });
|
||||||
expect(mocks.user.get).toHaveBeenCalledWith(userStub.user1.id, { withDeleted: false });
|
expect(mocks.user.get).toHaveBeenCalledWith(userStub.user1.id, { withDeleted: false });
|
||||||
@ -528,7 +531,7 @@ describe(NotificationService.name, () => {
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
mocks.notification.renderEmail.mockResolvedValue({ html: '', text: '' });
|
mocks.notification.renderEmail.mockResolvedValue({ html: '', text: '' });
|
||||||
mocks.assetJob.getAlbumThumbnailFile.mockResolvedValue([]);
|
mocks.assetJob.getAlbumThumbnailFiles.mockResolvedValue([]);
|
||||||
|
|
||||||
await sut.handleAlbumUpdate({ id: '', recipientIds: [userStub.user1.id] });
|
await sut.handleAlbumUpdate({ id: '', recipientIds: [userStub.user1.id] });
|
||||||
expect(mocks.user.get).toHaveBeenCalledWith(userStub.user1.id, { withDeleted: false });
|
expect(mocks.user.get).toHaveBeenCalledWith(userStub.user1.id, { withDeleted: false });
|
||||||
@ -550,7 +553,7 @@ describe(NotificationService.name, () => {
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
mocks.notification.renderEmail.mockResolvedValue({ html: '', text: '' });
|
mocks.notification.renderEmail.mockResolvedValue({ html: '', text: '' });
|
||||||
mocks.assetJob.getAlbumThumbnailFile.mockResolvedValue([]);
|
mocks.assetJob.getAlbumThumbnailFiles.mockResolvedValue([]);
|
||||||
|
|
||||||
await sut.handleAlbumUpdate({ id: '', recipientIds: [userStub.user1.id] });
|
await sut.handleAlbumUpdate({ id: '', recipientIds: [userStub.user1.id] });
|
||||||
expect(mocks.user.get).toHaveBeenCalledWith(userStub.user1.id, { withDeleted: false });
|
expect(mocks.user.get).toHaveBeenCalledWith(userStub.user1.id, { withDeleted: false });
|
||||||
@ -564,7 +567,7 @@ describe(NotificationService.name, () => {
|
|||||||
});
|
});
|
||||||
mocks.user.get.mockResolvedValue(userStub.user1);
|
mocks.user.get.mockResolvedValue(userStub.user1);
|
||||||
mocks.notification.renderEmail.mockResolvedValue({ html: '', text: '' });
|
mocks.notification.renderEmail.mockResolvedValue({ html: '', text: '' });
|
||||||
mocks.assetJob.getAlbumThumbnailFile.mockResolvedValue([]);
|
mocks.assetJob.getAlbumThumbnailFiles.mockResolvedValue([]);
|
||||||
|
|
||||||
await sut.handleAlbumUpdate({ id: '', recipientIds: [userStub.user1.id] });
|
await sut.handleAlbumUpdate({ id: '', recipientIds: [userStub.user1.id] });
|
||||||
expect(mocks.user.get).toHaveBeenCalledWith(userStub.user1.id, { withDeleted: false });
|
expect(mocks.user.get).toHaveBeenCalledWith(userStub.user1.id, { withDeleted: false });
|
||||||
|
@ -6,7 +6,6 @@ import { ArgOf } from 'src/repositories/event.repository';
|
|||||||
import { EmailTemplate } from 'src/repositories/notification.repository';
|
import { EmailTemplate } from 'src/repositories/notification.repository';
|
||||||
import { BaseService } from 'src/services/base.service';
|
import { BaseService } from 'src/services/base.service';
|
||||||
import { EmailImageAttachment, IEntityJob, INotifyAlbumUpdateJob, JobItem, JobOf } from 'src/types';
|
import { EmailImageAttachment, IEntityJob, INotifyAlbumUpdateJob, JobItem, JobOf } from 'src/types';
|
||||||
import { getAssetFile } from 'src/utils/asset.util';
|
|
||||||
import { getFilenameExtension } from 'src/utils/file';
|
import { getFilenameExtension } from 'src/utils/file';
|
||||||
import { getExternalDomain } from 'src/utils/misc';
|
import { getExternalDomain } from 'src/utils/misc';
|
||||||
import { isEqualObject } from 'src/utils/object';
|
import { isEqualObject } from 'src/utils/object';
|
||||||
@ -398,19 +397,18 @@ export class NotificationService extends BaseService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const albumThumbnailFiles = await this.assetJobRepository.getAlbumThumbnailFile(album.albumThumbnailAssetId);
|
const albumThumbnailFiles = await this.assetJobRepository.getAlbumThumbnailFiles(
|
||||||
if (albumThumbnailFiles.length === 0) {
|
album.albumThumbnailAssetId,
|
||||||
return;
|
AssetFileType.THUMBNAIL,
|
||||||
}
|
);
|
||||||
|
|
||||||
const thumbnailFile = getAssetFile(albumThumbnailFiles, AssetFileType.THUMBNAIL);
|
if (albumThumbnailFiles.length !== 1) {
|
||||||
if (!thumbnailFile) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
filename: `album-thumbnail${getFilenameExtension(thumbnailFile.path)}`,
|
filename: `album-thumbnail${getFilenameExtension(albumThumbnailFiles[0].path)}`,
|
||||||
path: thumbnailFile.path,
|
path: albumThumbnailFiles[0].path,
|
||||||
cid: 'album-thumbnail',
|
cid: 'album-thumbnail',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -264,7 +264,7 @@ describe(SmartInfoService.name, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should skip assets without a resize path', async () => {
|
it('should skip assets without a resize path', async () => {
|
||||||
mocks.asset.getByIds.mockResolvedValue([assetStub.noResizePath]);
|
mocks.assetJob.getForClipEncoding.mockResolvedValue({ ...assetStub.noResizePath, files: [] });
|
||||||
|
|
||||||
expect(await sut.handleEncodeClip({ id: assetStub.noResizePath.id })).toEqual(JobStatus.FAILED);
|
expect(await sut.handleEncodeClip({ id: assetStub.noResizePath.id })).toEqual(JobStatus.FAILED);
|
||||||
|
|
||||||
@ -274,6 +274,7 @@ describe(SmartInfoService.name, () => {
|
|||||||
|
|
||||||
it('should save the returned objects', async () => {
|
it('should save the returned objects', async () => {
|
||||||
mocks.machineLearning.encodeImage.mockResolvedValue('[0.01, 0.02, 0.03]');
|
mocks.machineLearning.encodeImage.mockResolvedValue('[0.01, 0.02, 0.03]');
|
||||||
|
mocks.assetJob.getForClipEncoding.mockResolvedValue({ ...assetStub.image, files: [assetStub.image.files[1]] });
|
||||||
|
|
||||||
expect(await sut.handleEncodeClip({ id: assetStub.image.id })).toEqual(JobStatus.SUCCESS);
|
expect(await sut.handleEncodeClip({ id: assetStub.image.id })).toEqual(JobStatus.SUCCESS);
|
||||||
|
|
||||||
@ -286,7 +287,10 @@ describe(SmartInfoService.name, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should skip invisible assets', async () => {
|
it('should skip invisible assets', async () => {
|
||||||
mocks.asset.getByIds.mockResolvedValue([assetStub.livePhotoMotionAsset]);
|
mocks.assetJob.getForClipEncoding.mockResolvedValue({
|
||||||
|
...assetStub.livePhotoMotionAsset,
|
||||||
|
files: [assetStub.image.files[1]],
|
||||||
|
});
|
||||||
|
|
||||||
expect(await sut.handleEncodeClip({ id: assetStub.livePhotoMotionAsset.id })).toEqual(JobStatus.SKIPPED);
|
expect(await sut.handleEncodeClip({ id: assetStub.livePhotoMotionAsset.id })).toEqual(JobStatus.SKIPPED);
|
||||||
|
|
||||||
@ -295,7 +299,7 @@ describe(SmartInfoService.name, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should fail if asset could not be found', async () => {
|
it('should fail if asset could not be found', async () => {
|
||||||
mocks.asset.getByIds.mockResolvedValue([]);
|
mocks.assetJob.getForClipEncoding.mockResolvedValue(void 0);
|
||||||
|
|
||||||
expect(await sut.handleEncodeClip({ id: assetStub.image.id })).toEqual(JobStatus.FAILED);
|
expect(await sut.handleEncodeClip({ id: assetStub.image.id })).toEqual(JobStatus.FAILED);
|
||||||
|
|
||||||
@ -306,6 +310,7 @@ describe(SmartInfoService.name, () => {
|
|||||||
it('should wait for database', async () => {
|
it('should wait for database', async () => {
|
||||||
mocks.machineLearning.encodeImage.mockResolvedValue('[0.01, 0.02, 0.03]');
|
mocks.machineLearning.encodeImage.mockResolvedValue('[0.01, 0.02, 0.03]');
|
||||||
mocks.database.isBusy.mockReturnValue(true);
|
mocks.database.isBusy.mockReturnValue(true);
|
||||||
|
mocks.assetJob.getForClipEncoding.mockResolvedValue({ ...assetStub.image, files: [assetStub.image.files[1]] });
|
||||||
|
|
||||||
expect(await sut.handleEncodeClip({ id: assetStub.image.id })).toEqual(JobStatus.SUCCESS);
|
expect(await sut.handleEncodeClip({ id: assetStub.image.id })).toEqual(JobStatus.SUCCESS);
|
||||||
|
|
||||||
|
@ -2,12 +2,11 @@ import { Injectable } from '@nestjs/common';
|
|||||||
import { SystemConfig } from 'src/config';
|
import { SystemConfig } from 'src/config';
|
||||||
import { JOBS_ASSET_PAGINATION_SIZE } from 'src/constants';
|
import { JOBS_ASSET_PAGINATION_SIZE } from 'src/constants';
|
||||||
import { OnEvent, OnJob } from 'src/decorators';
|
import { OnEvent, OnJob } from 'src/decorators';
|
||||||
import { AssetFileType, DatabaseLock, ImmichWorker, JobName, JobStatus, QueueName } from 'src/enum';
|
import { DatabaseLock, ImmichWorker, JobName, JobStatus, QueueName } from 'src/enum';
|
||||||
import { WithoutProperty } from 'src/repositories/asset.repository';
|
import { WithoutProperty } from 'src/repositories/asset.repository';
|
||||||
import { ArgOf } from 'src/repositories/event.repository';
|
import { ArgOf } from 'src/repositories/event.repository';
|
||||||
import { BaseService } from 'src/services/base.service';
|
import { BaseService } from 'src/services/base.service';
|
||||||
import { JobOf } from 'src/types';
|
import { JobOf } from 'src/types';
|
||||||
import { getAssetFile } from 'src/utils/asset.util';
|
|
||||||
import { getCLIPModelInfo, isSmartSearchEnabled } from 'src/utils/misc';
|
import { getCLIPModelInfo, isSmartSearchEnabled } from 'src/utils/misc';
|
||||||
import { usePagination } from 'src/utils/pagination';
|
import { usePagination } from 'src/utils/pagination';
|
||||||
|
|
||||||
@ -107,8 +106,8 @@ export class SmartInfoService extends BaseService {
|
|||||||
return JobStatus.SKIPPED;
|
return JobStatus.SKIPPED;
|
||||||
}
|
}
|
||||||
|
|
||||||
const [asset] = await this.assetRepository.getByIds([id], { files: true });
|
const asset = await this.assetJobRepository.getForClipEncoding(id);
|
||||||
if (!asset) {
|
if (!asset || asset.files.length !== 1) {
|
||||||
return JobStatus.FAILED;
|
return JobStatus.FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,14 +115,9 @@ export class SmartInfoService extends BaseService {
|
|||||||
return JobStatus.SKIPPED;
|
return JobStatus.SKIPPED;
|
||||||
}
|
}
|
||||||
|
|
||||||
const previewFile = getAssetFile(asset.files, AssetFileType.PREVIEW);
|
|
||||||
if (!previewFile) {
|
|
||||||
return JobStatus.FAILED;
|
|
||||||
}
|
|
||||||
|
|
||||||
const embedding = await this.machineLearningRepository.encodeImage(
|
const embedding = await this.machineLearningRepository.encodeImage(
|
||||||
machineLearning.urls,
|
machineLearning.urls,
|
||||||
previewFile.path,
|
asset.files[0].path,
|
||||||
machineLearning.clip,
|
machineLearning.clip,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user