mirror of
https://github.com/immich-app/immich.git
synced 2025-07-31 15:08:44 -04:00
refactor: job names (#19949)
This commit is contained in:
parent
e73abe0762
commit
bcb968e3d1
@ -441,94 +441,75 @@ export enum QueueName {
|
||||
}
|
||||
|
||||
export enum JobName {
|
||||
//backups
|
||||
BackupDatabase = 'database-backup',
|
||||
AssetDelete = 'AssetDelete',
|
||||
AssetDeleteCheck = 'AssetDeleteCheck',
|
||||
AssetDetectFacesQueueAll = 'AssetDetectFacesQueueAll',
|
||||
AssetDetectFaces = 'AssetDetectFaces',
|
||||
AssetDetectDuplicatesQueueAll = 'AssetDetectDuplicatesQueueAll',
|
||||
AssetDetectDuplicates = 'AssetDetectDuplicates',
|
||||
AssetEncodeVideoQueueAll = 'AssetEncodeVideoQueueAll',
|
||||
AssetEncodeVideo = 'AssetEncodeVideo',
|
||||
AssetEmptyTrash = 'AssetEmptyTrash',
|
||||
AssetExtractMetadataQueueAll = 'AssetExtractMetadataQueueAll',
|
||||
AssetExtractMetadata = 'AssetExtractMetadata',
|
||||
AssetFileMigration = 'AssetFileMigration',
|
||||
AssetGenerateThumbnailsQueueAll = 'AssetGenerateThumbnailsQueueAll',
|
||||
AssetGenerateThumbnails = 'AssetGenerateThumbnails',
|
||||
|
||||
// conversion
|
||||
QueueVideoConversion = 'queue-video-conversion',
|
||||
VideoConversation = 'video-conversion',
|
||||
AuditLogCleanup = 'AuditLogCleanup',
|
||||
|
||||
// thumbnails
|
||||
QueueGenerateThumbnails = 'queue-generate-thumbnails',
|
||||
GenerateThumbnails = 'generate-thumbnails',
|
||||
GeneratePersonThumbnail = 'generate-person-thumbnail',
|
||||
DatabaseBackup = 'DatabaseBackup',
|
||||
|
||||
// metadata
|
||||
QueueMetadataExtraction = 'queue-metadata-extraction',
|
||||
MetadataExtraction = 'metadata-extraction',
|
||||
FacialRecognitionQueueAll = 'FacialRecognitionQueueAll',
|
||||
FacialRecognition = 'FacialRecognition',
|
||||
|
||||
// user
|
||||
UserDeletion = 'user-deletion',
|
||||
UserDeleteCheck = 'user-delete-check',
|
||||
userSyncUsage = 'user-sync-usage',
|
||||
FileDelete = 'FileDelete',
|
||||
FileMigrationQueueAll = 'FileMigrationQueueAll',
|
||||
|
||||
// asset
|
||||
AssetDeletion = 'asset-deletion',
|
||||
AssetDeletionCheck = 'asset-deletion-check',
|
||||
LibraryDeleteCheck = 'LibraryDeleteCheck',
|
||||
LibraryDelete = 'LibraryDelete',
|
||||
LibraryRemoveAsset = 'LibraryRemoveAsset',
|
||||
LibrarySyncAssetsQueueAll = 'LibraryScanAssetsQueueAll',
|
||||
LibrarySyncAssets = 'LibrarySyncAssets',
|
||||
LibrarySyncFilesQueueAll = 'LibrarySyncFilesQueueAll',
|
||||
LibrarySyncFiles = 'LibrarySyncFiles',
|
||||
LibraryScanQueueAll = 'LibraryScanQueueAll',
|
||||
|
||||
// storage template
|
||||
StorageTemplateMigration = 'storage-template-migration',
|
||||
StorageTemplateMigrationSingle = 'storage-template-migration-single',
|
||||
MemoryCleanup = 'MemoryCleanup',
|
||||
MemoryGenerate = 'MemoryGenerate',
|
||||
|
||||
// tags
|
||||
TagCleanup = 'tag-cleanup',
|
||||
NotificationsCleanup = 'NotificationsCleanup',
|
||||
|
||||
// migration
|
||||
QueueMigration = 'queue-migration',
|
||||
MigrateAsset = 'migrate-asset',
|
||||
MigratePerson = 'migrate-person',
|
||||
NotifyUserSignup = 'NotifyUserSignup',
|
||||
NotifyAlbumInvite = 'NotifyAlbumInvite',
|
||||
NotifyAlbumUpdate = 'NotifyAlbumUpdate',
|
||||
|
||||
// facial recognition
|
||||
PersonCleanup = 'person-cleanup',
|
||||
QueueFaceDetection = 'queue-face-detection',
|
||||
FaceDetection = 'face-detection',
|
||||
QueueFacialRecognition = 'queue-facial-recognition',
|
||||
FacialRecognition = 'facial-recognition',
|
||||
UserDelete = 'UserDelete',
|
||||
UserDeleteCheck = 'UserDeleteCheck',
|
||||
UserSyncUsage = 'UserSyncUsage',
|
||||
|
||||
// library management
|
||||
LibraryQueueSyncFiles = 'library-queue-sync-files',
|
||||
LibraryQueueSyncAssets = 'library-queue-sync-assets',
|
||||
LibrarySyncFiles = 'library-sync-files',
|
||||
LibrarySyncAssets = 'library-sync-assets',
|
||||
LibraryAssetRemoval = 'handle-library-file-deletion',
|
||||
LibraryDelete = 'library-delete',
|
||||
LibraryQueueScanAll = 'library-queue-scan-all',
|
||||
LibraryQueueCleanup = 'library-queue-cleanup',
|
||||
PersonCleanup = 'PersonCleanup',
|
||||
PersonFileMigration = 'PersonFileMigration',
|
||||
PersonGenerateThumbnail = 'PersonGenerateThumbnail',
|
||||
|
||||
// cleanup
|
||||
DeleteFiles = 'delete-files',
|
||||
CleanOldAuditLogs = 'clean-old-audit-logs',
|
||||
CleanOldSessionTokens = 'clean-old-session-tokens',
|
||||
SessionCleanup = 'SessionCleanup',
|
||||
|
||||
// memories
|
||||
MemoriesCleanup = 'memories-cleanup',
|
||||
MemoriesCreate = 'memories-create',
|
||||
SendMail = 'SendMail',
|
||||
|
||||
// smart search
|
||||
QueueSmartSearch = 'queue-smart-search',
|
||||
SmartSearch = 'smart-search',
|
||||
SidecarQueueAll = 'SidecarQueueAll',
|
||||
SidecarDiscovery = 'SidecarDiscovery',
|
||||
SidecarSync = 'SidecarSync',
|
||||
SidecarWrite = 'SidecarWrite',
|
||||
|
||||
QueueTrashEmpty = 'queue-trash-empty',
|
||||
SmartSearchQueueAll = 'SmartSearchQueueAll',
|
||||
SmartSearch = 'SmartSearch',
|
||||
|
||||
// duplicate detection
|
||||
QueueDuplicateDetection = 'queue-duplicate-detection',
|
||||
DuplicateDetection = 'duplicate-detection',
|
||||
StorageTemplateMigration = 'StorageTemplateMigration',
|
||||
StorageTemplateMigrationSingle = 'StorageTemplateMigrationSingle',
|
||||
|
||||
// XMP sidecars
|
||||
QueueSidecar = 'queue-sidecar',
|
||||
SidecarDiscovery = 'sidecar-discovery',
|
||||
SidecarSync = 'sidecar-sync',
|
||||
SidecarWrite = 'sidecar-write',
|
||||
TagCleanup = 'TagCleanup',
|
||||
|
||||
// Notification
|
||||
NotifySignup = 'notify-signup',
|
||||
NotifyAlbumInvite = 'notify-album-invite',
|
||||
NotifyAlbumUpdate = 'notify-album-update',
|
||||
NotificationsCleanup = 'notifications-cleanup',
|
||||
SendMail = 'notification-send-email',
|
||||
|
||||
// Version check
|
||||
VersionCheck = 'version-check',
|
||||
VersionCheck = 'VersionCheck',
|
||||
}
|
||||
|
||||
export enum JobCommand {
|
||||
|
@ -214,11 +214,11 @@ export class JobRepository {
|
||||
case JobName.StorageTemplateMigrationSingle: {
|
||||
return { jobId: item.data.id };
|
||||
}
|
||||
case JobName.GeneratePersonThumbnail: {
|
||||
case JobName.PersonGenerateThumbnail: {
|
||||
return { priority: 1 };
|
||||
}
|
||||
case JobName.QueueFacialRecognition: {
|
||||
return { jobId: JobName.QueueFacialRecognition };
|
||||
case JobName.FacialRecognitionQueueAll: {
|
||||
return { jobId: JobName.FacialRecognitionQueueAll };
|
||||
}
|
||||
default: {
|
||||
return null;
|
||||
|
@ -384,7 +384,7 @@ describe(AssetMediaService.name, () => {
|
||||
});
|
||||
|
||||
expect(mocks.job.queue).toHaveBeenCalledWith({
|
||||
name: JobName.DeleteFiles,
|
||||
name: JobName.FileDelete,
|
||||
data: { files: ['fake_path/asset_1.jpeg', undefined] },
|
||||
});
|
||||
expect(mocks.user.updateUsage).not.toHaveBeenCalled();
|
||||
@ -409,7 +409,7 @@ describe(AssetMediaService.name, () => {
|
||||
);
|
||||
|
||||
expect(mocks.job.queue).toHaveBeenCalledWith({
|
||||
name: JobName.DeleteFiles,
|
||||
name: JobName.FileDelete,
|
||||
data: { files: ['fake_path/asset_1.jpeg', undefined] },
|
||||
});
|
||||
expect(mocks.user.updateUsage).not.toHaveBeenCalled();
|
||||
@ -815,7 +815,7 @@ describe(AssetMediaService.name, () => {
|
||||
expect(mocks.asset.create).not.toHaveBeenCalled();
|
||||
expect(mocks.asset.updateAll).not.toHaveBeenCalled();
|
||||
expect(mocks.job.queue).toHaveBeenCalledWith({
|
||||
name: JobName.DeleteFiles,
|
||||
name: JobName.FileDelete,
|
||||
data: { files: [updatedFile.originalPath, undefined] },
|
||||
});
|
||||
expect(mocks.user.updateUsage).not.toHaveBeenCalled();
|
||||
@ -912,7 +912,7 @@ describe(AssetMediaService.name, () => {
|
||||
await sut.onUploadError(request, file);
|
||||
|
||||
expect(mocks.job.queue).toHaveBeenCalledWith({
|
||||
name: JobName.DeleteFiles,
|
||||
name: JobName.FileDelete,
|
||||
data: { files: ['upload/upload/user-id/ra/nd/random-uuid.jpg'] },
|
||||
});
|
||||
});
|
||||
|
@ -121,7 +121,7 @@ export class AssetMediaService extends BaseService {
|
||||
const uploadFolder = this.getUploadFolder(asRequest(request, file));
|
||||
const uploadPath = `${uploadFolder}/${uploadFilename}`;
|
||||
|
||||
await this.jobRepository.queue({ name: JobName.DeleteFiles, data: { files: [uploadPath] } });
|
||||
await this.jobRepository.queue({ name: JobName.FileDelete, data: { files: [uploadPath] } });
|
||||
}
|
||||
|
||||
async uploadAsset(
|
||||
@ -312,7 +312,7 @@ export class AssetMediaService extends BaseService {
|
||||
): Promise<AssetMediaResponseDto> {
|
||||
// clean up files
|
||||
await this.jobRepository.queue({
|
||||
name: JobName.DeleteFiles,
|
||||
name: JobName.FileDelete,
|
||||
data: { files: [file.originalPath, sidecarFile?.originalPath] },
|
||||
});
|
||||
|
||||
@ -365,7 +365,7 @@ export class AssetMediaService extends BaseService {
|
||||
await this.storageRepository.utimes(file.originalPath, new Date(), new Date(dto.fileModifiedAt));
|
||||
await this.assetRepository.upsertExif({ assetId, fileSizeInByte: file.size });
|
||||
await this.jobRepository.queue({
|
||||
name: JobName.MetadataExtraction,
|
||||
name: JobName.AssetExtractMetadata,
|
||||
data: { id: assetId, source: 'upload' },
|
||||
});
|
||||
}
|
||||
@ -394,7 +394,7 @@ export class AssetMediaService extends BaseService {
|
||||
|
||||
const { size } = await this.storageRepository.stat(created.originalPath);
|
||||
await this.assetRepository.upsertExif({ assetId: created.id, fileSizeInByte: size });
|
||||
await this.jobRepository.queue({ name: JobName.MetadataExtraction, data: { id: created.id, source: 'copy' } });
|
||||
await this.jobRepository.queue({ name: JobName.AssetExtractMetadata, data: { id: created.id, source: 'copy' } });
|
||||
return created;
|
||||
}
|
||||
|
||||
@ -427,7 +427,7 @@ export class AssetMediaService extends BaseService {
|
||||
}
|
||||
await this.storageRepository.utimes(file.originalPath, new Date(), new Date(dto.fileModifiedAt));
|
||||
await this.assetRepository.upsertExif({ assetId: asset.id, fileSizeInByte: file.size });
|
||||
await this.jobRepository.queue({ name: JobName.MetadataExtraction, data: { id: asset.id, source: 'upload' } });
|
||||
await this.jobRepository.queue({ name: JobName.AssetExtractMetadata, data: { id: asset.id, source: 'upload' } });
|
||||
|
||||
return asset;
|
||||
}
|
||||
|
@ -522,7 +522,7 @@ describe(AssetService.name, () => {
|
||||
|
||||
expect(mocks.assetJob.streamForDeletedJob).toHaveBeenCalledWith(new Date());
|
||||
expect(mocks.job.queueAll).toHaveBeenCalledWith([
|
||||
{ name: JobName.AssetDeletion, data: { id: asset.id, deleteOnDisk: true } },
|
||||
{ name: JobName.AssetDelete, data: { id: asset.id, deleteOnDisk: true } },
|
||||
]);
|
||||
});
|
||||
|
||||
@ -536,7 +536,7 @@ describe(AssetService.name, () => {
|
||||
|
||||
expect(mocks.assetJob.streamForDeletedJob).toHaveBeenCalledWith(DateTime.now().minus({ days: 7 }).toJSDate());
|
||||
expect(mocks.job.queueAll).toHaveBeenCalledWith([
|
||||
{ name: JobName.AssetDeletion, data: { id: asset.id, deleteOnDisk: true } },
|
||||
{ name: JobName.AssetDelete, data: { id: asset.id, deleteOnDisk: true } },
|
||||
]);
|
||||
});
|
||||
});
|
||||
@ -552,7 +552,7 @@ describe(AssetService.name, () => {
|
||||
expect(mocks.job.queue.mock.calls).toEqual([
|
||||
[
|
||||
{
|
||||
name: JobName.DeleteFiles,
|
||||
name: JobName.FileDelete,
|
||||
data: {
|
||||
files: [
|
||||
'/uploads/user-id/webp/path.ext',
|
||||
@ -606,7 +606,7 @@ describe(AssetService.name, () => {
|
||||
expect(mocks.job.queue.mock.calls).toEqual([
|
||||
[
|
||||
{
|
||||
name: JobName.AssetDeletion,
|
||||
name: JobName.AssetDelete,
|
||||
data: {
|
||||
id: assetStub.livePhotoMotionAsset.id,
|
||||
deleteOnDisk: true,
|
||||
@ -615,7 +615,7 @@ describe(AssetService.name, () => {
|
||||
],
|
||||
[
|
||||
{
|
||||
name: JobName.DeleteFiles,
|
||||
name: JobName.FileDelete,
|
||||
data: {
|
||||
files: [
|
||||
'/uploads/user-id/webp/path.ext',
|
||||
@ -643,7 +643,7 @@ describe(AssetService.name, () => {
|
||||
expect(mocks.job.queue.mock.calls).toEqual([
|
||||
[
|
||||
{
|
||||
name: JobName.DeleteFiles,
|
||||
name: JobName.FileDelete,
|
||||
data: {
|
||||
files: [
|
||||
'/uploads/user-id/webp/path.ext',
|
||||
@ -679,7 +679,7 @@ describe(AssetService.name, () => {
|
||||
|
||||
await sut.run(authStub.admin, { assetIds: ['asset-1'], name: AssetJobName.REFRESH_FACES });
|
||||
|
||||
expect(mocks.job.queueAll).toHaveBeenCalledWith([{ name: JobName.FaceDetection, data: { id: 'asset-1' } }]);
|
||||
expect(mocks.job.queueAll).toHaveBeenCalledWith([{ name: JobName.AssetDetectFaces, data: { id: 'asset-1' } }]);
|
||||
});
|
||||
|
||||
it('should run the refresh metadata job', async () => {
|
||||
@ -687,7 +687,9 @@ describe(AssetService.name, () => {
|
||||
|
||||
await sut.run(authStub.admin, { assetIds: ['asset-1'], name: AssetJobName.REFRESH_METADATA });
|
||||
|
||||
expect(mocks.job.queueAll).toHaveBeenCalledWith([{ name: JobName.MetadataExtraction, data: { id: 'asset-1' } }]);
|
||||
expect(mocks.job.queueAll).toHaveBeenCalledWith([
|
||||
{ name: JobName.AssetExtractMetadata, data: { id: 'asset-1' } },
|
||||
]);
|
||||
});
|
||||
|
||||
it('should run the refresh thumbnails job', async () => {
|
||||
@ -695,7 +697,9 @@ describe(AssetService.name, () => {
|
||||
|
||||
await sut.run(authStub.admin, { assetIds: ['asset-1'], name: AssetJobName.REGENERATE_THUMBNAIL });
|
||||
|
||||
expect(mocks.job.queueAll).toHaveBeenCalledWith([{ name: JobName.GenerateThumbnails, data: { id: 'asset-1' } }]);
|
||||
expect(mocks.job.queueAll).toHaveBeenCalledWith([
|
||||
{ name: JobName.AssetGenerateThumbnails, data: { id: 'asset-1' } },
|
||||
]);
|
||||
});
|
||||
|
||||
it('should run the transcode video', async () => {
|
||||
@ -703,7 +707,7 @@ describe(AssetService.name, () => {
|
||||
|
||||
await sut.run(authStub.admin, { assetIds: ['asset-1'], name: AssetJobName.TRANSCODE_VIDEO });
|
||||
|
||||
expect(mocks.job.queueAll).toHaveBeenCalledWith([{ name: JobName.VideoConversation, data: { id: 'asset-1' } }]);
|
||||
expect(mocks.job.queueAll).toHaveBeenCalledWith([{ name: JobName.AssetEncodeVideo, data: { id: 'asset-1' } }]);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -145,7 +145,7 @@ export class AssetService extends BaseService {
|
||||
}
|
||||
}
|
||||
|
||||
@OnJob({ name: JobName.AssetDeletionCheck, queue: QueueName.BackgroundTask })
|
||||
@OnJob({ name: JobName.AssetDeleteCheck, queue: QueueName.BackgroundTask })
|
||||
async handleAssetDeletionCheck(): Promise<JobStatus> {
|
||||
const config = await this.getConfig({ withCache: false });
|
||||
const trashedDays = config.trash.enabled ? config.trash.days : 0;
|
||||
@ -158,7 +158,7 @@ export class AssetService extends BaseService {
|
||||
if (chunk.length > 0) {
|
||||
await this.jobRepository.queueAll(
|
||||
chunk.map(({ id, isOffline }) => ({
|
||||
name: JobName.AssetDeletion,
|
||||
name: JobName.AssetDelete,
|
||||
data: { id, deleteOnDisk: !isOffline },
|
||||
})),
|
||||
);
|
||||
@ -179,8 +179,8 @@ export class AssetService extends BaseService {
|
||||
return JobStatus.Success;
|
||||
}
|
||||
|
||||
@OnJob({ name: JobName.AssetDeletion, queue: QueueName.BackgroundTask })
|
||||
async handleAssetDeletion(job: JobOf<JobName.AssetDeletion>): Promise<JobStatus> {
|
||||
@OnJob({ name: JobName.AssetDelete, queue: QueueName.BackgroundTask })
|
||||
async handleAssetDeletion(job: JobOf<JobName.AssetDelete>): Promise<JobStatus> {
|
||||
const { id, deleteOnDisk } = job;
|
||||
|
||||
const asset = await this.assetJobRepository.getForAssetDeletion(id);
|
||||
@ -215,7 +215,7 @@ export class AssetService extends BaseService {
|
||||
const count = await this.assetRepository.getLivePhotoCount(asset.livePhotoVideoId);
|
||||
if (count === 0) {
|
||||
await this.jobRepository.queue({
|
||||
name: JobName.AssetDeletion,
|
||||
name: JobName.AssetDelete,
|
||||
data: { id: asset.livePhotoVideoId, deleteOnDisk },
|
||||
});
|
||||
}
|
||||
@ -228,7 +228,7 @@ export class AssetService extends BaseService {
|
||||
files.push(asset.sidecarPath, asset.originalPath);
|
||||
}
|
||||
|
||||
await this.jobRepository.queue({ name: JobName.DeleteFiles, data: { files } });
|
||||
await this.jobRepository.queue({ name: JobName.FileDelete, data: { files } });
|
||||
|
||||
return JobStatus.Success;
|
||||
}
|
||||
@ -255,22 +255,22 @@ export class AssetService extends BaseService {
|
||||
for (const id of dto.assetIds) {
|
||||
switch (dto.name) {
|
||||
case AssetJobName.REFRESH_FACES: {
|
||||
jobs.push({ name: JobName.FaceDetection, data: { id } });
|
||||
jobs.push({ name: JobName.AssetDetectFaces, data: { id } });
|
||||
break;
|
||||
}
|
||||
|
||||
case AssetJobName.REFRESH_METADATA: {
|
||||
jobs.push({ name: JobName.MetadataExtraction, data: { id } });
|
||||
jobs.push({ name: JobName.AssetExtractMetadata, data: { id } });
|
||||
break;
|
||||
}
|
||||
|
||||
case AssetJobName.REGENERATE_THUMBNAIL: {
|
||||
jobs.push({ name: JobName.GenerateThumbnails, data: { id } });
|
||||
jobs.push({ name: JobName.AssetGenerateThumbnails, data: { id } });
|
||||
break;
|
||||
}
|
||||
|
||||
case AssetJobName.TRANSCODE_VIDEO: {
|
||||
jobs.push({ name: JobName.VideoConversation, data: { id } });
|
||||
jobs.push({ name: JobName.AssetEncodeVideo, data: { id } });
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ import { BaseService } from 'src/services/base.service';
|
||||
|
||||
@Injectable()
|
||||
export class AuditService extends BaseService {
|
||||
@OnJob({ name: JobName.CleanOldAuditLogs, queue: QueueName.BackgroundTask })
|
||||
@OnJob({ name: JobName.AuditLogCleanup, queue: QueueName.BackgroundTask })
|
||||
async handleCleanup(): Promise<JobStatus> {
|
||||
await this.auditRepository.removeBefore(DateTime.now().minus(AUDIT_LOG_MAX_DURATION).toJSDate());
|
||||
return JobStatus.Success;
|
||||
|
@ -330,7 +330,7 @@ export class AuthService extends BaseService {
|
||||
await this.userRepository.update(user.id, { profileImagePath, profileChangedAt: new Date() });
|
||||
|
||||
if (oldPath) {
|
||||
await this.jobRepository.queue({ name: JobName.DeleteFiles, data: { files: [oldPath] } });
|
||||
await this.jobRepository.queue({ name: JobName.FileDelete, data: { files: [oldPath] } });
|
||||
}
|
||||
} catch (error: Error | any) {
|
||||
this.logger.warn(`Unable to sync oauth profile picture: ${error}`, error?.stack);
|
||||
|
@ -26,7 +26,7 @@ export class BackupService extends BaseService {
|
||||
this.cronRepository.create({
|
||||
name: 'backupDatabase',
|
||||
expression: database.cronExpression,
|
||||
onTick: () => handlePromiseError(this.jobRepository.queue({ name: JobName.BackupDatabase }), this.logger),
|
||||
onTick: () => handlePromiseError(this.jobRepository.queue({ name: JobName.DatabaseBackup }), this.logger),
|
||||
start: database.enabled,
|
||||
});
|
||||
}
|
||||
@ -68,7 +68,7 @@ export class BackupService extends BaseService {
|
||||
this.logger.debug(`Database Backup Cleanup Finished, deleted ${toDelete.length} backups`);
|
||||
}
|
||||
|
||||
@OnJob({ name: JobName.BackupDatabase, queue: QueueName.BackupDatabase })
|
||||
@OnJob({ name: JobName.DatabaseBackup, queue: QueueName.BackupDatabase })
|
||||
async handleBackupDatabase(): Promise<JobStatus> {
|
||||
this.logger.debug(`Database Backup Started`);
|
||||
const { database } = this.configRepository.getEnv();
|
||||
|
@ -108,7 +108,7 @@ describe(SearchService.name, () => {
|
||||
expect(mocks.assetJob.streamForSearchDuplicates).toHaveBeenCalledWith(undefined);
|
||||
expect(mocks.job.queueAll).toHaveBeenCalledWith([
|
||||
{
|
||||
name: JobName.DuplicateDetection,
|
||||
name: JobName.AssetDetectDuplicates,
|
||||
data: { id: assetStub.image.id },
|
||||
},
|
||||
]);
|
||||
@ -122,7 +122,7 @@ describe(SearchService.name, () => {
|
||||
expect(mocks.assetJob.streamForSearchDuplicates).toHaveBeenCalledWith(true);
|
||||
expect(mocks.job.queueAll).toHaveBeenCalledWith([
|
||||
{
|
||||
name: JobName.DuplicateDetection,
|
||||
name: JobName.AssetDetectDuplicates,
|
||||
data: { id: assetStub.image.id },
|
||||
},
|
||||
]);
|
||||
|
@ -29,8 +29,8 @@ export class DuplicateService extends BaseService {
|
||||
await this.duplicateRepository.deleteAll(auth.user.id, dto.ids);
|
||||
}
|
||||
|
||||
@OnJob({ name: JobName.QueueDuplicateDetection, queue: QueueName.DuplicateDetection })
|
||||
async handleQueueSearchDuplicates({ force }: JobOf<JobName.QueueDuplicateDetection>): Promise<JobStatus> {
|
||||
@OnJob({ name: JobName.AssetDetectDuplicatesQueueAll, queue: QueueName.DuplicateDetection })
|
||||
async handleQueueSearchDuplicates({ force }: JobOf<JobName.AssetDetectDuplicatesQueueAll>): Promise<JobStatus> {
|
||||
const { machineLearning } = await this.getConfig({ withCache: false });
|
||||
if (!isDuplicateDetectionEnabled(machineLearning)) {
|
||||
return JobStatus.Skipped;
|
||||
@ -44,7 +44,7 @@ export class DuplicateService extends BaseService {
|
||||
|
||||
const assets = this.assetJobRepository.streamForSearchDuplicates(force);
|
||||
for await (const asset of assets) {
|
||||
jobs.push({ name: JobName.DuplicateDetection, data: { id: asset.id } });
|
||||
jobs.push({ name: JobName.AssetDetectDuplicates, data: { id: asset.id } });
|
||||
if (jobs.length >= JOBS_ASSET_PAGINATION_SIZE) {
|
||||
await queueAll();
|
||||
}
|
||||
@ -55,8 +55,8 @@ export class DuplicateService extends BaseService {
|
||||
return JobStatus.Success;
|
||||
}
|
||||
|
||||
@OnJob({ name: JobName.DuplicateDetection, queue: QueueName.DuplicateDetection })
|
||||
async handleSearchDuplicates({ id }: JobOf<JobName.DuplicateDetection>): Promise<JobStatus> {
|
||||
@OnJob({ name: JobName.AssetDetectDuplicates, queue: QueueName.DuplicateDetection })
|
||||
async handleSearchDuplicates({ id }: JobOf<JobName.AssetDetectDuplicates>): Promise<JobStatus> {
|
||||
const { machineLearning } = await this.getConfig({ withCache: true });
|
||||
if (!isDuplicateDetectionEnabled(machineLearning)) {
|
||||
return JobStatus.Skipped;
|
||||
|
@ -37,16 +37,16 @@ describe(JobService.name, () => {
|
||||
await sut.handleNightlyJobs();
|
||||
|
||||
expect(mocks.job.queueAll).toHaveBeenCalledWith([
|
||||
{ name: JobName.AssetDeletionCheck },
|
||||
{ name: JobName.AssetDeleteCheck },
|
||||
{ name: JobName.UserDeleteCheck },
|
||||
{ name: JobName.PersonCleanup },
|
||||
{ name: JobName.MemoriesCleanup },
|
||||
{ name: JobName.CleanOldSessionTokens },
|
||||
{ name: JobName.CleanOldAuditLogs },
|
||||
{ name: JobName.MemoriesCreate },
|
||||
{ name: JobName.userSyncUsage },
|
||||
{ name: JobName.QueueGenerateThumbnails, data: { force: false } },
|
||||
{ name: JobName.QueueFacialRecognition, data: { force: false, nightly: true } },
|
||||
{ name: JobName.MemoryCleanup },
|
||||
{ name: JobName.SessionCleanup },
|
||||
{ name: JobName.AuditLogCleanup },
|
||||
{ name: JobName.MemoryGenerate },
|
||||
{ name: JobName.UserSyncUsage },
|
||||
{ name: JobName.AssetGenerateThumbnailsQueueAll, data: { force: false } },
|
||||
{ name: JobName.FacialRecognitionQueueAll, data: { force: false, nightly: true } },
|
||||
]);
|
||||
});
|
||||
});
|
||||
@ -136,7 +136,7 @@ describe(JobService.name, () => {
|
||||
|
||||
await sut.handleCommand(QueueName.VideoConversion, { command: JobCommand.Start, force: false });
|
||||
|
||||
expect(mocks.job.queue).toHaveBeenCalledWith({ name: JobName.QueueVideoConversion, data: { force: false } });
|
||||
expect(mocks.job.queue).toHaveBeenCalledWith({ name: JobName.AssetEncodeVideoQueueAll, data: { force: false } });
|
||||
});
|
||||
|
||||
it('should handle a start storage template migration command', async () => {
|
||||
@ -152,7 +152,7 @@ describe(JobService.name, () => {
|
||||
|
||||
await sut.handleCommand(QueueName.SmartSearch, { command: JobCommand.Start, force: false });
|
||||
|
||||
expect(mocks.job.queue).toHaveBeenCalledWith({ name: JobName.QueueSmartSearch, data: { force: false } });
|
||||
expect(mocks.job.queue).toHaveBeenCalledWith({ name: JobName.SmartSearchQueueAll, data: { force: false } });
|
||||
});
|
||||
|
||||
it('should handle a start metadata extraction command', async () => {
|
||||
@ -160,7 +160,10 @@ describe(JobService.name, () => {
|
||||
|
||||
await sut.handleCommand(QueueName.MetadataExtraction, { command: JobCommand.Start, force: false });
|
||||
|
||||
expect(mocks.job.queue).toHaveBeenCalledWith({ name: JobName.QueueMetadataExtraction, data: { force: false } });
|
||||
expect(mocks.job.queue).toHaveBeenCalledWith({
|
||||
name: JobName.AssetExtractMetadataQueueAll,
|
||||
data: { force: false },
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle a start sidecar command', async () => {
|
||||
@ -168,7 +171,7 @@ describe(JobService.name, () => {
|
||||
|
||||
await sut.handleCommand(QueueName.Sidecar, { command: JobCommand.Start, force: false });
|
||||
|
||||
expect(mocks.job.queue).toHaveBeenCalledWith({ name: JobName.QueueSidecar, data: { force: false } });
|
||||
expect(mocks.job.queue).toHaveBeenCalledWith({ name: JobName.SidecarQueueAll, data: { force: false } });
|
||||
});
|
||||
|
||||
it('should handle a start thumbnail generation command', async () => {
|
||||
@ -176,7 +179,10 @@ describe(JobService.name, () => {
|
||||
|
||||
await sut.handleCommand(QueueName.ThumbnailGeneration, { command: JobCommand.Start, force: false });
|
||||
|
||||
expect(mocks.job.queue).toHaveBeenCalledWith({ name: JobName.QueueGenerateThumbnails, data: { force: false } });
|
||||
expect(mocks.job.queue).toHaveBeenCalledWith({
|
||||
name: JobName.AssetGenerateThumbnailsQueueAll,
|
||||
data: { force: false },
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle a start face detection command', async () => {
|
||||
@ -184,7 +190,7 @@ describe(JobService.name, () => {
|
||||
|
||||
await sut.handleCommand(QueueName.FaceDetection, { command: JobCommand.Start, force: false });
|
||||
|
||||
expect(mocks.job.queue).toHaveBeenCalledWith({ name: JobName.QueueFaceDetection, data: { force: false } });
|
||||
expect(mocks.job.queue).toHaveBeenCalledWith({ name: JobName.AssetDetectFacesQueueAll, data: { force: false } });
|
||||
});
|
||||
|
||||
it('should handle a start facial recognition command', async () => {
|
||||
@ -192,7 +198,7 @@ describe(JobService.name, () => {
|
||||
|
||||
await sut.handleCommand(QueueName.FacialRecognition, { command: JobCommand.Start, force: false });
|
||||
|
||||
expect(mocks.job.queue).toHaveBeenCalledWith({ name: JobName.QueueFacialRecognition, data: { force: false } });
|
||||
expect(mocks.job.queue).toHaveBeenCalledWith({ name: JobName.FacialRecognitionQueueAll, data: { force: false } });
|
||||
});
|
||||
|
||||
it('should handle a start backup database command', async () => {
|
||||
@ -200,7 +206,7 @@ describe(JobService.name, () => {
|
||||
|
||||
await sut.handleCommand(QueueName.BackupDatabase, { command: JobCommand.Start, force: false });
|
||||
|
||||
expect(mocks.job.queue).toHaveBeenCalledWith({ name: JobName.BackupDatabase, data: { force: false } });
|
||||
expect(mocks.job.queue).toHaveBeenCalledWith({ name: JobName.DatabaseBackup, data: { force: false } });
|
||||
});
|
||||
|
||||
it('should throw a bad request when an invalid queue is used', async () => {
|
||||
@ -220,55 +226,55 @@ describe(JobService.name, () => {
|
||||
mocks.job.run.mockResolvedValue(JobStatus.Success);
|
||||
|
||||
await sut.onJobStart(QueueName.BackgroundTask, {
|
||||
name: JobName.DeleteFiles,
|
||||
name: JobName.FileDelete,
|
||||
data: { files: ['path/to/file'] },
|
||||
});
|
||||
|
||||
expect(mocks.telemetry.jobs.addToGauge).toHaveBeenCalledWith('immich.queues.background_task.active', 1);
|
||||
expect(mocks.telemetry.jobs.addToGauge).toHaveBeenCalledWith('immich.queues.background_task.active', -1);
|
||||
expect(mocks.telemetry.jobs.addToCounter).toHaveBeenCalledWith('immich.jobs.delete_files.success', 1);
|
||||
expect(mocks.telemetry.jobs.addToCounter).toHaveBeenCalledWith('immich.jobs.file_delete.success', 1);
|
||||
expect(mocks.logger.error).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
const tests: Array<{ item: JobItem; jobs: JobName[]; stub?: any }> = [
|
||||
{
|
||||
item: { name: JobName.SidecarSync, data: { id: 'asset-1' } },
|
||||
jobs: [JobName.MetadataExtraction],
|
||||
jobs: [JobName.AssetExtractMetadata],
|
||||
},
|
||||
{
|
||||
item: { name: JobName.SidecarDiscovery, data: { id: 'asset-1' } },
|
||||
jobs: [JobName.MetadataExtraction],
|
||||
jobs: [JobName.AssetExtractMetadata],
|
||||
},
|
||||
{
|
||||
item: { name: JobName.StorageTemplateMigrationSingle, data: { id: 'asset-1', source: 'upload' } },
|
||||
jobs: [JobName.GenerateThumbnails],
|
||||
jobs: [JobName.AssetGenerateThumbnails],
|
||||
},
|
||||
{
|
||||
item: { name: JobName.StorageTemplateMigrationSingle, data: { id: 'asset-1' } },
|
||||
jobs: [],
|
||||
},
|
||||
{
|
||||
item: { name: JobName.GeneratePersonThumbnail, data: { id: 'asset-1' } },
|
||||
item: { name: JobName.PersonGenerateThumbnail, data: { id: 'asset-1' } },
|
||||
jobs: [],
|
||||
},
|
||||
{
|
||||
item: { name: JobName.GenerateThumbnails, data: { id: 'asset-1' } },
|
||||
item: { name: JobName.AssetGenerateThumbnails, data: { id: 'asset-1' } },
|
||||
jobs: [],
|
||||
stub: [assetStub.image],
|
||||
},
|
||||
{
|
||||
item: { name: JobName.GenerateThumbnails, data: { id: 'asset-1' } },
|
||||
item: { name: JobName.AssetGenerateThumbnails, data: { id: 'asset-1' } },
|
||||
jobs: [],
|
||||
stub: [assetStub.video],
|
||||
},
|
||||
{
|
||||
item: { name: JobName.GenerateThumbnails, data: { id: 'asset-1', source: 'upload' } },
|
||||
jobs: [JobName.SmartSearch, JobName.FaceDetection],
|
||||
item: { name: JobName.AssetGenerateThumbnails, data: { id: 'asset-1', source: 'upload' } },
|
||||
jobs: [JobName.SmartSearch, JobName.AssetDetectFaces],
|
||||
stub: [assetStub.livePhotoStillAsset],
|
||||
},
|
||||
{
|
||||
item: { name: JobName.GenerateThumbnails, data: { id: 'asset-1', source: 'upload' } },
|
||||
jobs: [JobName.SmartSearch, JobName.FaceDetection, JobName.VideoConversation],
|
||||
item: { name: JobName.AssetGenerateThumbnails, data: { id: 'asset-1', source: 'upload' } },
|
||||
jobs: [JobName.SmartSearch, JobName.AssetDetectFaces, JobName.AssetEncodeVideo],
|
||||
stub: [assetStub.video],
|
||||
},
|
||||
{
|
||||
@ -276,7 +282,7 @@ describe(JobService.name, () => {
|
||||
jobs: [],
|
||||
},
|
||||
{
|
||||
item: { name: JobName.FaceDetection, data: { id: 'asset-1' } },
|
||||
item: { name: JobName.AssetDetectFaces, data: { id: 'asset-1' } },
|
||||
jobs: [],
|
||||
},
|
||||
{
|
||||
|
@ -40,15 +40,15 @@ const asJobItem = (dto: JobCreateDto): JobItem => {
|
||||
}
|
||||
|
||||
case ManualJobName.MemoryCleanup: {
|
||||
return { name: JobName.MemoriesCleanup };
|
||||
return { name: JobName.MemoryCleanup };
|
||||
}
|
||||
|
||||
case ManualJobName.MemoryCreate: {
|
||||
return { name: JobName.MemoriesCreate };
|
||||
return { name: JobName.MemoryGenerate };
|
||||
}
|
||||
|
||||
case ManualJobName.BackupDatabase: {
|
||||
return { name: JobName.BackupDatabase };
|
||||
return { name: JobName.DatabaseBackup };
|
||||
}
|
||||
|
||||
default: {
|
||||
@ -190,7 +190,7 @@ export class JobService extends BaseService {
|
||||
|
||||
switch (name) {
|
||||
case QueueName.VideoConversion: {
|
||||
return this.jobRepository.queue({ name: JobName.QueueVideoConversion, data: { force } });
|
||||
return this.jobRepository.queue({ name: JobName.AssetEncodeVideoQueueAll, data: { force } });
|
||||
}
|
||||
|
||||
case QueueName.StorageTemplateMigration: {
|
||||
@ -198,43 +198,43 @@ export class JobService extends BaseService {
|
||||
}
|
||||
|
||||
case QueueName.Migration: {
|
||||
return this.jobRepository.queue({ name: JobName.QueueMigration });
|
||||
return this.jobRepository.queue({ name: JobName.FileMigrationQueueAll });
|
||||
}
|
||||
|
||||
case QueueName.SmartSearch: {
|
||||
return this.jobRepository.queue({ name: JobName.QueueSmartSearch, data: { force } });
|
||||
return this.jobRepository.queue({ name: JobName.SmartSearchQueueAll, data: { force } });
|
||||
}
|
||||
|
||||
case QueueName.DuplicateDetection: {
|
||||
return this.jobRepository.queue({ name: JobName.QueueDuplicateDetection, data: { force } });
|
||||
return this.jobRepository.queue({ name: JobName.AssetDetectDuplicatesQueueAll, data: { force } });
|
||||
}
|
||||
|
||||
case QueueName.MetadataExtraction: {
|
||||
return this.jobRepository.queue({ name: JobName.QueueMetadataExtraction, data: { force } });
|
||||
return this.jobRepository.queue({ name: JobName.AssetExtractMetadataQueueAll, data: { force } });
|
||||
}
|
||||
|
||||
case QueueName.Sidecar: {
|
||||
return this.jobRepository.queue({ name: JobName.QueueSidecar, data: { force } });
|
||||
return this.jobRepository.queue({ name: JobName.SidecarQueueAll, data: { force } });
|
||||
}
|
||||
|
||||
case QueueName.ThumbnailGeneration: {
|
||||
return this.jobRepository.queue({ name: JobName.QueueGenerateThumbnails, data: { force } });
|
||||
return this.jobRepository.queue({ name: JobName.AssetGenerateThumbnailsQueueAll, data: { force } });
|
||||
}
|
||||
|
||||
case QueueName.FaceDetection: {
|
||||
return this.jobRepository.queue({ name: JobName.QueueFaceDetection, data: { force } });
|
||||
return this.jobRepository.queue({ name: JobName.AssetDetectFacesQueueAll, data: { force } });
|
||||
}
|
||||
|
||||
case QueueName.FacialRecognition: {
|
||||
return this.jobRepository.queue({ name: JobName.QueueFacialRecognition, data: { force } });
|
||||
return this.jobRepository.queue({ name: JobName.FacialRecognitionQueueAll, data: { force } });
|
||||
}
|
||||
|
||||
case QueueName.Library: {
|
||||
return this.jobRepository.queue({ name: JobName.LibraryQueueScanAll, data: { force } });
|
||||
return this.jobRepository.queue({ name: JobName.LibraryScanQueueAll, data: { force } });
|
||||
}
|
||||
|
||||
case QueueName.BackupDatabase: {
|
||||
return this.jobRepository.queue({ name: JobName.BackupDatabase, data: { force } });
|
||||
return this.jobRepository.queue({ name: JobName.DatabaseBackup, data: { force } });
|
||||
}
|
||||
|
||||
default: {
|
||||
@ -249,7 +249,7 @@ export class JobService extends BaseService {
|
||||
this.telemetryRepository.jobs.addToGauge(queueMetric, 1);
|
||||
try {
|
||||
const status = await this.jobRepository.run(job);
|
||||
const jobMetric = `immich.jobs.${job.name.replaceAll('-', '_')}.${status}`;
|
||||
const jobMetric = `immich.jobs.${snakeCase(job.name)}.${status}`;
|
||||
this.telemetryRepository.jobs.addToCounter(jobMetric, 1);
|
||||
if (status === JobStatus.Success || status == JobStatus.Skipped) {
|
||||
await this.onDone(job);
|
||||
@ -276,29 +276,29 @@ export class JobService extends BaseService {
|
||||
|
||||
if (config.nightlyTasks.databaseCleanup) {
|
||||
jobs.push(
|
||||
{ name: JobName.AssetDeletionCheck },
|
||||
{ name: JobName.AssetDeleteCheck },
|
||||
{ name: JobName.UserDeleteCheck },
|
||||
{ name: JobName.PersonCleanup },
|
||||
{ name: JobName.MemoriesCleanup },
|
||||
{ name: JobName.CleanOldSessionTokens },
|
||||
{ name: JobName.CleanOldAuditLogs },
|
||||
{ name: JobName.MemoryCleanup },
|
||||
{ name: JobName.SessionCleanup },
|
||||
{ name: JobName.AuditLogCleanup },
|
||||
);
|
||||
}
|
||||
|
||||
if (config.nightlyTasks.generateMemories) {
|
||||
jobs.push({ name: JobName.MemoriesCreate });
|
||||
jobs.push({ name: JobName.MemoryGenerate });
|
||||
}
|
||||
|
||||
if (config.nightlyTasks.syncQuotaUsage) {
|
||||
jobs.push({ name: JobName.userSyncUsage });
|
||||
jobs.push({ name: JobName.UserSyncUsage });
|
||||
}
|
||||
|
||||
if (config.nightlyTasks.missingThumbnails) {
|
||||
jobs.push({ name: JobName.QueueGenerateThumbnails, data: { force: false } });
|
||||
jobs.push({ name: JobName.AssetGenerateThumbnailsQueueAll, data: { force: false } });
|
||||
}
|
||||
|
||||
if (config.nightlyTasks.clusterNewFaces) {
|
||||
jobs.push({ name: JobName.QueueFacialRecognition, data: { force: false, nightly: true } });
|
||||
jobs.push({ name: JobName.FacialRecognitionQueueAll, data: { force: false, nightly: true } });
|
||||
}
|
||||
|
||||
await this.jobRepository.queueAll(jobs);
|
||||
@ -311,13 +311,13 @@ export class JobService extends BaseService {
|
||||
switch (item.name) {
|
||||
case JobName.SidecarSync:
|
||||
case JobName.SidecarDiscovery: {
|
||||
await this.jobRepository.queue({ name: JobName.MetadataExtraction, data: item.data });
|
||||
await this.jobRepository.queue({ name: JobName.AssetExtractMetadata, data: item.data });
|
||||
break;
|
||||
}
|
||||
|
||||
case JobName.SidecarWrite: {
|
||||
await this.jobRepository.queue({
|
||||
name: JobName.MetadataExtraction,
|
||||
name: JobName.AssetExtractMetadata,
|
||||
data: { id: item.data.id, source: 'sidecar-write' },
|
||||
});
|
||||
break;
|
||||
@ -325,12 +325,12 @@ export class JobService extends BaseService {
|
||||
|
||||
case JobName.StorageTemplateMigrationSingle: {
|
||||
if (item.data.source === 'upload' || item.data.source === 'copy') {
|
||||
await this.jobRepository.queue({ name: JobName.GenerateThumbnails, data: item.data });
|
||||
await this.jobRepository.queue({ name: JobName.AssetGenerateThumbnails, data: item.data });
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case JobName.GeneratePersonThumbnail: {
|
||||
case JobName.PersonGenerateThumbnail: {
|
||||
const { id } = item.data;
|
||||
const person = await this.personRepository.getById(id);
|
||||
if (person) {
|
||||
@ -339,7 +339,7 @@ export class JobService extends BaseService {
|
||||
break;
|
||||
}
|
||||
|
||||
case JobName.GenerateThumbnails: {
|
||||
case JobName.AssetGenerateThumbnails: {
|
||||
if (!item.data.notify && item.data.source !== 'upload') {
|
||||
break;
|
||||
}
|
||||
@ -352,11 +352,11 @@ export class JobService extends BaseService {
|
||||
|
||||
const jobs: JobItem[] = [
|
||||
{ name: JobName.SmartSearch, data: item.data },
|
||||
{ name: JobName.FaceDetection, data: item.data },
|
||||
{ name: JobName.AssetDetectFaces, data: item.data },
|
||||
];
|
||||
|
||||
if (asset.type === AssetType.Video) {
|
||||
jobs.push({ name: JobName.VideoConversation, data: item.data });
|
||||
jobs.push({ name: JobName.AssetEncodeVideo, data: item.data });
|
||||
}
|
||||
|
||||
await this.jobRepository.queueAll(jobs);
|
||||
@ -419,12 +419,12 @@ export class JobService extends BaseService {
|
||||
|
||||
case JobName.SmartSearch: {
|
||||
if (item.data.source === 'upload') {
|
||||
await this.jobRepository.queue({ name: JobName.DuplicateDetection, data: item.data });
|
||||
await this.jobRepository.queue({ name: JobName.AssetDetectDuplicates, data: item.data });
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case JobName.UserDeletion: {
|
||||
case JobName.UserDelete: {
|
||||
this.eventRepository.clientBroadcast('on_user_delete', item.data.id);
|
||||
break;
|
||||
}
|
||||
|
@ -1010,7 +1010,7 @@ describe(LibraryService.name, () => {
|
||||
await sut.watchAll();
|
||||
|
||||
expect(mocks.job.queue).toHaveBeenCalledWith({
|
||||
name: JobName.LibraryAssetRemoval,
|
||||
name: JobName.LibraryRemoveAsset,
|
||||
data: {
|
||||
libraryId: library.id,
|
||||
paths: [assetStub.image.originalPath],
|
||||
@ -1131,11 +1131,11 @@ describe(LibraryService.name, () => {
|
||||
|
||||
expect(mocks.job.queue).toHaveBeenCalledTimes(2);
|
||||
expect(mocks.job.queue).toHaveBeenCalledWith({
|
||||
name: JobName.LibraryQueueSyncFiles,
|
||||
name: JobName.LibrarySyncFilesQueueAll,
|
||||
data: { id: library.id },
|
||||
});
|
||||
expect(mocks.job.queue).toHaveBeenCalledWith({
|
||||
name: JobName.LibraryQueueSyncAssets,
|
||||
name: JobName.LibrarySyncAssetsQueueAll,
|
||||
data: { id: library.id },
|
||||
});
|
||||
});
|
||||
@ -1150,11 +1150,11 @@ describe(LibraryService.name, () => {
|
||||
await expect(sut.handleQueueScanAll()).resolves.toBe(JobStatus.Success);
|
||||
|
||||
expect(mocks.job.queue).toHaveBeenCalledWith({
|
||||
name: JobName.LibraryQueueCleanup,
|
||||
name: JobName.LibraryDeleteCheck,
|
||||
data: {},
|
||||
});
|
||||
expect(mocks.job.queueAll).toHaveBeenCalledWith([
|
||||
{ name: JobName.LibraryQueueSyncFiles, data: { id: library.id } },
|
||||
{ name: JobName.LibrarySyncFilesQueueAll, data: { id: library.id } },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
@ -47,7 +47,7 @@ export class LibraryService extends BaseService {
|
||||
this.cronRepository.create({
|
||||
name: CronJob.LibraryScan,
|
||||
expression: scan.cronExpression,
|
||||
onTick: () => handlePromiseError(this.jobRepository.queue({ name: JobName.LibraryQueueScanAll }), this.logger),
|
||||
onTick: () => handlePromiseError(this.jobRepository.queue({ name: JobName.LibraryScanQueueAll }), this.logger),
|
||||
start: scan.enabled,
|
||||
});
|
||||
}
|
||||
@ -113,7 +113,7 @@ export class LibraryService extends BaseService {
|
||||
const deletionHandler = async (path: string) => {
|
||||
this.logger.debug(`File unlink event received for ${path} in library ${library.id}}`);
|
||||
await this.jobRepository.queue({
|
||||
name: JobName.LibraryAssetRemoval,
|
||||
name: JobName.LibraryRemoveAsset,
|
||||
data: { libraryId: library.id, paths: [path] },
|
||||
});
|
||||
};
|
||||
@ -198,7 +198,7 @@ export class LibraryService extends BaseService {
|
||||
return libraries.map((library) => mapLibrary(library));
|
||||
}
|
||||
|
||||
@OnJob({ name: JobName.LibraryQueueCleanup, queue: QueueName.Library })
|
||||
@OnJob({ name: JobName.LibraryDeleteCheck, queue: QueueName.Library })
|
||||
async handleQueueCleanup(): Promise<JobStatus> {
|
||||
this.logger.log('Checking for any libraries pending deletion...');
|
||||
const pendingDeletions = await this.libraryRepository.getAllDeleted();
|
||||
@ -355,7 +355,7 @@ export class LibraryService extends BaseService {
|
||||
assetsFound = true;
|
||||
this.logger.debug(`Queueing deletion of ${chunk.length} asset(s) in library ${libraryId}`);
|
||||
await this.jobRepository.queueAll(
|
||||
chunk.map((id) => ({ name: JobName.AssetDeletion, data: { id, deleteOnDisk: false } })),
|
||||
chunk.map((id) => ({ name: JobName.AssetDelete, data: { id, deleteOnDisk: false } })),
|
||||
);
|
||||
chunk = [];
|
||||
}
|
||||
@ -422,30 +422,30 @@ export class LibraryService extends BaseService {
|
||||
this.logger.log(`Starting to scan library ${id}`);
|
||||
|
||||
await this.jobRepository.queue({
|
||||
name: JobName.LibraryQueueSyncFiles,
|
||||
name: JobName.LibrarySyncFilesQueueAll,
|
||||
data: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
|
||||
await this.jobRepository.queue({ name: JobName.LibraryQueueSyncAssets, data: { id } });
|
||||
await this.jobRepository.queue({ name: JobName.LibrarySyncAssetsQueueAll, data: { id } });
|
||||
}
|
||||
|
||||
async queueScanAll() {
|
||||
await this.jobRepository.queue({ name: JobName.LibraryQueueScanAll, data: {} });
|
||||
await this.jobRepository.queue({ name: JobName.LibraryScanQueueAll, data: {} });
|
||||
}
|
||||
|
||||
@OnJob({ name: JobName.LibraryQueueScanAll, queue: QueueName.Library })
|
||||
@OnJob({ name: JobName.LibraryScanQueueAll, queue: QueueName.Library })
|
||||
async handleQueueScanAll(): Promise<JobStatus> {
|
||||
this.logger.log(`Initiating scan of all external libraries...`);
|
||||
|
||||
await this.jobRepository.queue({ name: JobName.LibraryQueueCleanup, data: {} });
|
||||
await this.jobRepository.queue({ name: JobName.LibraryDeleteCheck, data: {} });
|
||||
|
||||
const libraries = await this.libraryRepository.getAll(true);
|
||||
|
||||
await this.jobRepository.queueAll(
|
||||
libraries.map((library) => ({
|
||||
name: JobName.LibraryQueueSyncFiles,
|
||||
name: JobName.LibrarySyncFilesQueueAll,
|
||||
data: {
|
||||
id: library.id,
|
||||
},
|
||||
@ -453,7 +453,7 @@ export class LibraryService extends BaseService {
|
||||
);
|
||||
await this.jobRepository.queueAll(
|
||||
libraries.map((library) => ({
|
||||
name: JobName.LibraryQueueSyncAssets,
|
||||
name: JobName.LibrarySyncAssetsQueueAll,
|
||||
data: {
|
||||
id: library.id,
|
||||
},
|
||||
@ -598,8 +598,8 @@ export class LibraryService extends BaseService {
|
||||
return AssetSyncResult.DO_NOTHING;
|
||||
}
|
||||
|
||||
@OnJob({ name: JobName.LibraryQueueSyncFiles, queue: QueueName.Library })
|
||||
async handleQueueSyncFiles(job: JobOf<JobName.LibraryQueueSyncFiles>): Promise<JobStatus> {
|
||||
@OnJob({ name: JobName.LibrarySyncFilesQueueAll, queue: QueueName.Library })
|
||||
async handleQueueSyncFiles(job: JobOf<JobName.LibrarySyncFilesQueueAll>): Promise<JobStatus> {
|
||||
const library = await this.libraryRepository.get(job.id);
|
||||
if (!library) {
|
||||
this.logger.debug(`Library ${job.id} not found, skipping refresh`);
|
||||
@ -668,8 +668,8 @@ export class LibraryService extends BaseService {
|
||||
return JobStatus.Success;
|
||||
}
|
||||
|
||||
@OnJob({ name: JobName.LibraryAssetRemoval, queue: QueueName.Library })
|
||||
async handleAssetRemoval(job: JobOf<JobName.LibraryAssetRemoval>): Promise<JobStatus> {
|
||||
@OnJob({ name: JobName.LibraryRemoveAsset, queue: QueueName.Library })
|
||||
async handleAssetRemoval(job: JobOf<JobName.LibraryRemoveAsset>): Promise<JobStatus> {
|
||||
// This is only for handling file unlink events via the file watcher
|
||||
this.logger.verbose(`Deleting asset(s) ${job.paths} from library ${job.libraryId}`);
|
||||
for (const assetPath of job.paths) {
|
||||
@ -682,8 +682,8 @@ export class LibraryService extends BaseService {
|
||||
return JobStatus.Success;
|
||||
}
|
||||
|
||||
@OnJob({ name: JobName.LibraryQueueSyncAssets, queue: QueueName.Library })
|
||||
async handleQueueSyncAssets(job: JobOf<JobName.LibraryQueueSyncAssets>): Promise<JobStatus> {
|
||||
@OnJob({ name: JobName.LibrarySyncAssetsQueueAll, queue: QueueName.Library })
|
||||
async handleQueueSyncAssets(job: JobOf<JobName.LibrarySyncAssetsQueueAll>): Promise<JobStatus> {
|
||||
const library = await this.libraryRepository.get(job.id);
|
||||
if (!library) {
|
||||
return JobStatus.Skipped;
|
||||
|
@ -49,7 +49,7 @@ describe(MediaService.name, () => {
|
||||
expect(mocks.assetJob.streamForThumbnailJob).toHaveBeenCalledWith(true);
|
||||
expect(mocks.job.queueAll).toHaveBeenCalledWith([
|
||||
{
|
||||
name: JobName.GenerateThumbnails,
|
||||
name: JobName.AssetGenerateThumbnails,
|
||||
data: { id: assetStub.image.id },
|
||||
},
|
||||
]);
|
||||
@ -57,7 +57,7 @@ describe(MediaService.name, () => {
|
||||
expect(mocks.person.getAll).toHaveBeenCalledWith(undefined);
|
||||
expect(mocks.job.queueAll).toHaveBeenCalledWith([
|
||||
{
|
||||
name: JobName.GeneratePersonThumbnail,
|
||||
name: JobName.PersonGenerateThumbnail,
|
||||
data: { id: personStub.newThumbnail.id },
|
||||
},
|
||||
]);
|
||||
@ -72,7 +72,7 @@ describe(MediaService.name, () => {
|
||||
expect(mocks.assetJob.streamForThumbnailJob).toHaveBeenCalledWith(true);
|
||||
expect(mocks.job.queueAll).toHaveBeenCalledWith([
|
||||
{
|
||||
name: JobName.GenerateThumbnails,
|
||||
name: JobName.AssetGenerateThumbnails,
|
||||
data: { id: assetStub.trashed.id },
|
||||
},
|
||||
]);
|
||||
@ -87,7 +87,7 @@ describe(MediaService.name, () => {
|
||||
expect(mocks.assetJob.streamForThumbnailJob).toHaveBeenCalledWith(true);
|
||||
expect(mocks.job.queueAll).toHaveBeenCalledWith([
|
||||
{
|
||||
name: JobName.GenerateThumbnails,
|
||||
name: JobName.AssetGenerateThumbnails,
|
||||
data: { id: assetStub.archived.id },
|
||||
},
|
||||
]);
|
||||
@ -106,7 +106,7 @@ describe(MediaService.name, () => {
|
||||
expect(mocks.person.update).toHaveBeenCalledTimes(1);
|
||||
expect(mocks.job.queueAll).toHaveBeenCalledWith([
|
||||
{
|
||||
name: JobName.GeneratePersonThumbnail,
|
||||
name: JobName.PersonGenerateThumbnail,
|
||||
data: {
|
||||
id: personStub.newThumbnail.id,
|
||||
},
|
||||
@ -122,7 +122,7 @@ describe(MediaService.name, () => {
|
||||
expect(mocks.assetJob.streamForThumbnailJob).toHaveBeenCalledWith(false);
|
||||
expect(mocks.job.queueAll).toHaveBeenCalledWith([
|
||||
{
|
||||
name: JobName.GenerateThumbnails,
|
||||
name: JobName.AssetGenerateThumbnails,
|
||||
data: { id: assetStub.image.id },
|
||||
},
|
||||
]);
|
||||
@ -138,7 +138,7 @@ describe(MediaService.name, () => {
|
||||
expect(mocks.assetJob.streamForThumbnailJob).toHaveBeenCalledWith(false);
|
||||
expect(mocks.job.queueAll).toHaveBeenCalledWith([
|
||||
{
|
||||
name: JobName.GenerateThumbnails,
|
||||
name: JobName.AssetGenerateThumbnails,
|
||||
data: { id: assetStub.image.id },
|
||||
},
|
||||
]);
|
||||
@ -154,7 +154,7 @@ describe(MediaService.name, () => {
|
||||
expect(mocks.assetJob.streamForThumbnailJob).toHaveBeenCalledWith(false);
|
||||
expect(mocks.job.queueAll).toHaveBeenCalledWith([
|
||||
{
|
||||
name: JobName.GenerateThumbnails,
|
||||
name: JobName.AssetGenerateThumbnails,
|
||||
data: { id: assetStub.image.id },
|
||||
},
|
||||
]);
|
||||
@ -173,10 +173,10 @@ describe(MediaService.name, () => {
|
||||
|
||||
expect(mocks.storage.removeEmptyDirs).toHaveBeenCalledTimes(2);
|
||||
expect(mocks.job.queueAll).toHaveBeenCalledWith([
|
||||
{ name: JobName.MigrateAsset, data: { id: assetStub.image.id } },
|
||||
{ name: JobName.AssetFileMigration, data: { id: assetStub.image.id } },
|
||||
]);
|
||||
expect(mocks.job.queueAll).toHaveBeenCalledWith([
|
||||
{ name: JobName.MigratePerson, data: { id: personStub.withName.id } },
|
||||
{ name: JobName.PersonFileMigration, data: { id: personStub.withName.id } },
|
||||
]);
|
||||
});
|
||||
});
|
||||
@ -1243,7 +1243,7 @@ describe(MediaService.name, () => {
|
||||
expect(mocks.assetJob.streamForVideoConversion).toHaveBeenCalledWith(true);
|
||||
expect(mocks.job.queueAll).toHaveBeenCalledWith([
|
||||
{
|
||||
name: JobName.VideoConversation,
|
||||
name: JobName.AssetEncodeVideo,
|
||||
data: { id: assetStub.video.id },
|
||||
},
|
||||
]);
|
||||
@ -1257,7 +1257,7 @@ describe(MediaService.name, () => {
|
||||
expect(mocks.assetJob.streamForVideoConversion).toHaveBeenCalledWith(void 0);
|
||||
expect(mocks.job.queueAll).toHaveBeenCalledWith([
|
||||
{
|
||||
name: JobName.VideoConversation,
|
||||
name: JobName.AssetEncodeVideo,
|
||||
data: { id: assetStub.video.id },
|
||||
},
|
||||
]);
|
||||
@ -1616,7 +1616,7 @@ describe(MediaService.name, () => {
|
||||
|
||||
expect(mocks.media.transcode).not.toHaveBeenCalled();
|
||||
expect(mocks.job.queue).toHaveBeenCalledWith({
|
||||
name: JobName.DeleteFiles,
|
||||
name: JobName.FileDelete,
|
||||
data: { files: [asset.encodedVideoPath] },
|
||||
});
|
||||
});
|
||||
|
@ -57,8 +57,8 @@ export class MediaService extends BaseService {
|
||||
this.videoInterfaces = { dri, mali };
|
||||
}
|
||||
|
||||
@OnJob({ name: JobName.QueueGenerateThumbnails, queue: QueueName.ThumbnailGeneration })
|
||||
async handleQueueGenerateThumbnails({ force }: JobOf<JobName.QueueGenerateThumbnails>): Promise<JobStatus> {
|
||||
@OnJob({ name: JobName.AssetGenerateThumbnailsQueueAll, queue: QueueName.ThumbnailGeneration })
|
||||
async handleQueueGenerateThumbnails({ force }: JobOf<JobName.AssetGenerateThumbnailsQueueAll>): Promise<JobStatus> {
|
||||
let jobs: JobItem[] = [];
|
||||
|
||||
const queueAll = async () => {
|
||||
@ -70,7 +70,7 @@ export class MediaService extends BaseService {
|
||||
const { previewFile, thumbnailFile } = getAssetFiles(asset.files);
|
||||
|
||||
if (!previewFile || !thumbnailFile || !asset.thumbhash || force) {
|
||||
jobs.push({ name: JobName.GenerateThumbnails, data: { id: asset.id } });
|
||||
jobs.push({ name: JobName.AssetGenerateThumbnails, data: { id: asset.id } });
|
||||
}
|
||||
|
||||
if (jobs.length >= JOBS_ASSET_PAGINATION_SIZE) {
|
||||
@ -92,7 +92,7 @@ export class MediaService extends BaseService {
|
||||
await this.personRepository.update({ id: person.id, faceAssetId: face.id });
|
||||
}
|
||||
|
||||
jobs.push({ name: JobName.GeneratePersonThumbnail, data: { id: person.id } });
|
||||
jobs.push({ name: JobName.PersonGenerateThumbnail, data: { id: person.id } });
|
||||
if (jobs.length >= JOBS_ASSET_PAGINATION_SIZE) {
|
||||
await queueAll();
|
||||
}
|
||||
@ -103,7 +103,7 @@ export class MediaService extends BaseService {
|
||||
return JobStatus.Success;
|
||||
}
|
||||
|
||||
@OnJob({ name: JobName.QueueMigration, queue: QueueName.Migration })
|
||||
@OnJob({ name: JobName.FileMigrationQueueAll, queue: QueueName.Migration })
|
||||
async handleQueueMigration(): Promise<JobStatus> {
|
||||
const { active, waiting } = await this.jobRepository.getJobCounts(QueueName.Migration);
|
||||
if (active === 1 && waiting === 0) {
|
||||
@ -114,7 +114,7 @@ export class MediaService extends BaseService {
|
||||
let jobs: JobItem[] = [];
|
||||
const assets = this.assetJobRepository.streamForMigrationJob();
|
||||
for await (const asset of assets) {
|
||||
jobs.push({ name: JobName.MigrateAsset, data: { id: asset.id } });
|
||||
jobs.push({ name: JobName.AssetFileMigration, data: { id: asset.id } });
|
||||
if (jobs.length >= JOBS_ASSET_PAGINATION_SIZE) {
|
||||
await this.jobRepository.queueAll(jobs);
|
||||
jobs = [];
|
||||
@ -125,7 +125,7 @@ export class MediaService extends BaseService {
|
||||
jobs = [];
|
||||
|
||||
for await (const person of this.personRepository.getAll()) {
|
||||
jobs.push({ name: JobName.MigratePerson, data: { id: person.id } });
|
||||
jobs.push({ name: JobName.PersonFileMigration, data: { id: person.id } });
|
||||
|
||||
if (jobs.length === JOBS_ASSET_PAGINATION_SIZE) {
|
||||
await this.jobRepository.queueAll(jobs);
|
||||
@ -138,8 +138,8 @@ export class MediaService extends BaseService {
|
||||
return JobStatus.Success;
|
||||
}
|
||||
|
||||
@OnJob({ name: JobName.MigrateAsset, queue: QueueName.Migration })
|
||||
async handleAssetMigration({ id }: JobOf<JobName.MigrateAsset>): Promise<JobStatus> {
|
||||
@OnJob({ name: JobName.AssetFileMigration, queue: QueueName.Migration })
|
||||
async handleAssetMigration({ id }: JobOf<JobName.AssetFileMigration>): Promise<JobStatus> {
|
||||
const { image } = await this.getConfig({ withCache: true });
|
||||
const asset = await this.assetJobRepository.getForMigrationJob(id);
|
||||
if (!asset) {
|
||||
@ -154,8 +154,8 @@ export class MediaService extends BaseService {
|
||||
return JobStatus.Success;
|
||||
}
|
||||
|
||||
@OnJob({ name: JobName.GenerateThumbnails, queue: QueueName.ThumbnailGeneration })
|
||||
async handleGenerateThumbnails({ id }: JobOf<JobName.GenerateThumbnails>): Promise<JobStatus> {
|
||||
@OnJob({ name: JobName.AssetGenerateThumbnails, queue: QueueName.ThumbnailGeneration })
|
||||
async handleGenerateThumbnails({ id }: JobOf<JobName.AssetGenerateThumbnails>): Promise<JobStatus> {
|
||||
const asset = await this.assetJobRepository.getForGenerateThumbnailJob(id);
|
||||
if (!asset) {
|
||||
this.logger.warn(`Thumbnail generation failed for asset ${id}: not found`);
|
||||
@ -317,8 +317,8 @@ export class MediaService extends BaseService {
|
||||
return { previewPath, thumbnailPath, fullsizePath, thumbhash: outputs[0] as Buffer };
|
||||
}
|
||||
|
||||
@OnJob({ name: JobName.GeneratePersonThumbnail, queue: QueueName.ThumbnailGeneration })
|
||||
async handleGeneratePersonThumbnail({ id }: JobOf<JobName.GeneratePersonThumbnail>): Promise<JobStatus> {
|
||||
@OnJob({ name: JobName.PersonGenerateThumbnail, queue: QueueName.ThumbnailGeneration })
|
||||
async handleGeneratePersonThumbnail({ id }: JobOf<JobName.PersonGenerateThumbnail>): Promise<JobStatus> {
|
||||
const { machineLearning, metadata, image } = await this.getConfig({ withCache: true });
|
||||
if (!isFacialRecognitionEnabled(machineLearning) && !isFaceImportEnabled(metadata)) {
|
||||
return JobStatus.Skipped;
|
||||
@ -443,13 +443,13 @@ export class MediaService extends BaseService {
|
||||
return { previewPath, thumbnailPath, thumbhash };
|
||||
}
|
||||
|
||||
@OnJob({ name: JobName.QueueVideoConversion, queue: QueueName.VideoConversion })
|
||||
async handleQueueVideoConversion(job: JobOf<JobName.QueueVideoConversion>): Promise<JobStatus> {
|
||||
@OnJob({ name: JobName.AssetEncodeVideoQueueAll, queue: QueueName.VideoConversion })
|
||||
async handleQueueVideoConversion(job: JobOf<JobName.AssetEncodeVideoQueueAll>): Promise<JobStatus> {
|
||||
const { force } = job;
|
||||
|
||||
let queue: { name: JobName.VideoConversation; data: { id: string } }[] = [];
|
||||
let queue: { name: JobName.AssetEncodeVideo; data: { id: string } }[] = [];
|
||||
for await (const asset of this.assetJobRepository.streamForVideoConversion(force)) {
|
||||
queue.push({ name: JobName.VideoConversation, data: { id: asset.id } });
|
||||
queue.push({ name: JobName.AssetEncodeVideo, data: { id: asset.id } });
|
||||
|
||||
if (queue.length >= JOBS_ASSET_PAGINATION_SIZE) {
|
||||
await this.jobRepository.queueAll(queue);
|
||||
@ -462,8 +462,8 @@ export class MediaService extends BaseService {
|
||||
return JobStatus.Success;
|
||||
}
|
||||
|
||||
@OnJob({ name: JobName.VideoConversation, queue: QueueName.VideoConversion })
|
||||
async handleVideoConversion({ id }: JobOf<JobName.VideoConversation>): Promise<JobStatus> {
|
||||
@OnJob({ name: JobName.AssetEncodeVideo, queue: QueueName.VideoConversion })
|
||||
async handleVideoConversion({ id }: JobOf<JobName.AssetEncodeVideo>): Promise<JobStatus> {
|
||||
const asset = await this.assetJobRepository.getForVideoConversion(id);
|
||||
if (!asset) {
|
||||
return JobStatus.Failed;
|
||||
@ -492,7 +492,7 @@ export class MediaService extends BaseService {
|
||||
if (target === TranscodeTarget.None && !this.isRemuxRequired(ffmpeg, format)) {
|
||||
if (asset.encodedVideoPath) {
|
||||
this.logger.log(`Transcoded video exists for asset ${asset.id}, but is no longer required. Deleting...`);
|
||||
await this.jobRepository.queue({ name: JobName.DeleteFiles, data: { files: [asset.encodedVideoPath] } });
|
||||
await this.jobRepository.queue({ name: JobName.FileDelete, data: { files: [asset.encodedVideoPath] } });
|
||||
await this.assetRepository.update({ id: asset.id, encodedVideoPath: null });
|
||||
} else {
|
||||
this.logger.verbose(`Asset ${asset.id} does not require transcoding based on current policy, skipping`);
|
||||
|
@ -12,7 +12,7 @@ const DAYS = 3;
|
||||
|
||||
@Injectable()
|
||||
export class MemoryService extends BaseService {
|
||||
@OnJob({ name: JobName.MemoriesCreate, queue: QueueName.BackgroundTask })
|
||||
@OnJob({ name: JobName.MemoryGenerate, queue: QueueName.BackgroundTask })
|
||||
async onMemoriesCreate() {
|
||||
const users = await this.userRepository.getList({ withDeleted: false });
|
||||
const usersIds = await Promise.all(
|
||||
@ -72,7 +72,7 @@ export class MemoryService extends BaseService {
|
||||
);
|
||||
}
|
||||
|
||||
@OnJob({ name: JobName.MemoriesCleanup, queue: QueueName.BackgroundTask })
|
||||
@OnJob({ name: JobName.MemoryCleanup, queue: QueueName.BackgroundTask })
|
||||
async onMemoriesCleanup() {
|
||||
await this.memoryRepository.cleanup();
|
||||
}
|
||||
|
@ -106,7 +106,7 @@ describe(MetadataService.name, () => {
|
||||
expect(mocks.assetJob.streamForMetadataExtraction).toHaveBeenCalledWith(false);
|
||||
expect(mocks.job.queueAll).toHaveBeenCalledWith([
|
||||
{
|
||||
name: JobName.MetadataExtraction,
|
||||
name: JobName.AssetExtractMetadata,
|
||||
data: { id: assetStub.image.id },
|
||||
},
|
||||
]);
|
||||
@ -119,7 +119,7 @@ describe(MetadataService.name, () => {
|
||||
expect(mocks.assetJob.streamForMetadataExtraction).toHaveBeenCalledWith(true);
|
||||
expect(mocks.job.queueAll).toHaveBeenCalledWith([
|
||||
{
|
||||
name: JobName.MetadataExtraction,
|
||||
name: JobName.AssetExtractMetadata,
|
||||
data: { id: assetStub.image.id },
|
||||
},
|
||||
]);
|
||||
@ -599,7 +599,7 @@ describe(MetadataService.name, () => {
|
||||
});
|
||||
expect(mocks.asset.update).toHaveBeenCalledTimes(3);
|
||||
expect(mocks.job.queue).toHaveBeenCalledExactlyOnceWith({
|
||||
name: JobName.VideoConversation,
|
||||
name: JobName.AssetEncodeVideo,
|
||||
data: { id: assetStub.livePhotoMotionAsset.id },
|
||||
});
|
||||
});
|
||||
@ -657,7 +657,7 @@ describe(MetadataService.name, () => {
|
||||
});
|
||||
expect(mocks.asset.update).toHaveBeenCalledTimes(3);
|
||||
expect(mocks.job.queue).toHaveBeenCalledExactlyOnceWith({
|
||||
name: JobName.VideoConversation,
|
||||
name: JobName.AssetEncodeVideo,
|
||||
data: { id: assetStub.livePhotoMotionAsset.id },
|
||||
});
|
||||
});
|
||||
@ -715,7 +715,7 @@ describe(MetadataService.name, () => {
|
||||
});
|
||||
expect(mocks.asset.update).toHaveBeenCalledTimes(3);
|
||||
expect(mocks.job.queue).toHaveBeenCalledExactlyOnceWith({
|
||||
name: JobName.VideoConversation,
|
||||
name: JobName.AssetEncodeVideo,
|
||||
data: { id: assetStub.livePhotoMotionAsset.id },
|
||||
});
|
||||
});
|
||||
@ -737,7 +737,7 @@ describe(MetadataService.name, () => {
|
||||
|
||||
await sut.handleMetadataExtraction({ id: assetStub.livePhotoWithOriginalFileName.id });
|
||||
expect(mocks.job.queue).toHaveBeenNthCalledWith(1, {
|
||||
name: JobName.AssetDeletion,
|
||||
name: JobName.AssetDelete,
|
||||
data: { id: assetStub.livePhotoWithOriginalFileName.livePhotoVideoId, deleteOnDisk: true },
|
||||
});
|
||||
});
|
||||
@ -1116,7 +1116,7 @@ describe(MetadataService.name, () => {
|
||||
]);
|
||||
expect(mocks.job.queueAll).toHaveBeenCalledWith([
|
||||
{
|
||||
name: JobName.GeneratePersonThumbnail,
|
||||
name: JobName.PersonGenerateThumbnail,
|
||||
data: { id: personStub.withName.id },
|
||||
},
|
||||
]);
|
||||
@ -1244,7 +1244,7 @@ describe(MetadataService.name, () => {
|
||||
]);
|
||||
expect(mocks.job.queueAll).toHaveBeenCalledWith([
|
||||
{
|
||||
name: JobName.GeneratePersonThumbnail,
|
||||
name: JobName.PersonGenerateThumbnail,
|
||||
data: { id: personStub.withName.id },
|
||||
},
|
||||
]);
|
||||
|
@ -193,13 +193,13 @@ export class MetadataService extends BaseService {
|
||||
await this.eventRepository.emit('AssetHide', { assetId: motionAsset.id, userId: motionAsset.ownerId });
|
||||
}
|
||||
|
||||
@OnJob({ name: JobName.QueueMetadataExtraction, queue: QueueName.MetadataExtraction })
|
||||
async handleQueueMetadataExtraction(job: JobOf<JobName.QueueMetadataExtraction>): Promise<JobStatus> {
|
||||
@OnJob({ name: JobName.AssetExtractMetadataQueueAll, queue: QueueName.MetadataExtraction })
|
||||
async handleQueueMetadataExtraction(job: JobOf<JobName.AssetExtractMetadataQueueAll>): Promise<JobStatus> {
|
||||
const { force } = job;
|
||||
|
||||
let queue: { name: JobName.MetadataExtraction; data: { id: string } }[] = [];
|
||||
let queue: { name: JobName.AssetExtractMetadata; data: { id: string } }[] = [];
|
||||
for await (const asset of this.assetJobRepository.streamForMetadataExtraction(force)) {
|
||||
queue.push({ name: JobName.MetadataExtraction, data: { id: asset.id } });
|
||||
queue.push({ name: JobName.AssetExtractMetadata, data: { id: asset.id } });
|
||||
|
||||
if (queue.length >= JOBS_ASSET_PAGINATION_SIZE) {
|
||||
await this.jobRepository.queueAll(queue);
|
||||
@ -211,8 +211,8 @@ export class MetadataService extends BaseService {
|
||||
return JobStatus.Success;
|
||||
}
|
||||
|
||||
@OnJob({ name: JobName.MetadataExtraction, queue: QueueName.MetadataExtraction })
|
||||
async handleMetadataExtraction(data: JobOf<JobName.MetadataExtraction>) {
|
||||
@OnJob({ name: JobName.AssetExtractMetadata, queue: QueueName.MetadataExtraction })
|
||||
async handleMetadataExtraction(data: JobOf<JobName.AssetExtractMetadata>) {
|
||||
const [{ metadata, reverseGeocoding }, asset] = await Promise.all([
|
||||
this.getConfig({ withCache: true }),
|
||||
this.assetJobRepository.getForMetadataExtraction(data.id),
|
||||
@ -320,8 +320,8 @@ export class MetadataService extends BaseService {
|
||||
});
|
||||
}
|
||||
|
||||
@OnJob({ name: JobName.QueueSidecar, queue: QueueName.Sidecar })
|
||||
async handleQueueSidecar({ force }: JobOf<JobName.QueueSidecar>): Promise<JobStatus> {
|
||||
@OnJob({ name: JobName.SidecarQueueAll, queue: QueueName.Sidecar })
|
||||
async handleQueueSidecar({ force }: JobOf<JobName.SidecarQueueAll>): Promise<JobStatus> {
|
||||
let jobs: JobItem[] = [];
|
||||
const queueAll = async () => {
|
||||
await this.jobRepository.queueAll(jobs);
|
||||
@ -597,7 +597,7 @@ export class MetadataService extends BaseService {
|
||||
// note asset.livePhotoVideoId is not motionAsset.id yet
|
||||
if (asset.livePhotoVideoId) {
|
||||
await this.jobRepository.queue({
|
||||
name: JobName.AssetDeletion,
|
||||
name: JobName.AssetDelete,
|
||||
data: { id: asset.livePhotoVideoId, deleteOnDisk: true },
|
||||
});
|
||||
this.logger.log(`Removed old motion photo video asset (${asset.livePhotoVideoId})`);
|
||||
@ -612,7 +612,7 @@ export class MetadataService extends BaseService {
|
||||
this.logger.log(`Wrote motion photo video to ${motionAsset.originalPath}`);
|
||||
|
||||
await this.handleMetadataExtraction({ id: motionAsset.id });
|
||||
await this.jobRepository.queue({ name: JobName.VideoConversation, data: { id: motionAsset.id } });
|
||||
await this.jobRepository.queue({ name: JobName.AssetEncodeVideo, data: { id: motionAsset.id } });
|
||||
}
|
||||
|
||||
this.logger.debug(`Finished motion photo video extraction for asset ${asset.id}: ${asset.originalPath}`);
|
||||
@ -753,7 +753,7 @@ export class MetadataService extends BaseService {
|
||||
if (missing.length > 0) {
|
||||
this.logger.debugFn(() => `Creating missing persons: ${missing.map((p) => `${p.name}/${p.id}`)}`);
|
||||
const newPersonIds = await this.personRepository.createAll(missing);
|
||||
const jobs = newPersonIds.map((id) => ({ name: JobName.GeneratePersonThumbnail, data: { id } }) as const);
|
||||
const jobs = newPersonIds.map((id) => ({ name: JobName.PersonGenerateThumbnail, data: { id } }) as const);
|
||||
await this.jobRepository.queueAll(jobs);
|
||||
}
|
||||
|
||||
|
@ -131,7 +131,7 @@ describe(NotificationService.name, () => {
|
||||
it('should queue the generate thumbnail job', async () => {
|
||||
await sut.onAssetShow({ assetId: 'asset-id', userId: 'user-id' });
|
||||
expect(mocks.job.queue).toHaveBeenCalledWith({
|
||||
name: JobName.GenerateThumbnails,
|
||||
name: JobName.AssetGenerateThumbnails,
|
||||
data: { id: 'asset-id', notify: true },
|
||||
});
|
||||
});
|
||||
@ -146,7 +146,7 @@ describe(NotificationService.name, () => {
|
||||
it('should queue notify signup event if notify is true', async () => {
|
||||
await sut.onUserSignup({ id: '', notify: true });
|
||||
expect(mocks.job.queue).toHaveBeenCalledWith({
|
||||
name: JobName.NotifySignup,
|
||||
name: JobName.NotifyUserSignup,
|
||||
data: { id: '', tempPassword: undefined },
|
||||
});
|
||||
});
|
||||
|
@ -87,7 +87,7 @@ export class NotificationService extends BaseService {
|
||||
this.logger.error(`Unable to run job handler (${job.name}): ${error}`, error?.stack, JSON.stringify(job.data));
|
||||
|
||||
switch (job.name) {
|
||||
case JobName.BackupDatabase: {
|
||||
case JobName.DatabaseBackup: {
|
||||
const errorMessage = error instanceof Error ? error.message : error;
|
||||
const item = await this.notificationRepository.create({
|
||||
userId: admin.id,
|
||||
@ -135,7 +135,7 @@ export class NotificationService extends BaseService {
|
||||
|
||||
@OnEvent({ name: 'AssetShow' })
|
||||
async onAssetShow({ assetId }: ArgOf<'AssetShow'>) {
|
||||
await this.jobRepository.queue({ name: JobName.GenerateThumbnails, data: { id: assetId, notify: true } });
|
||||
await this.jobRepository.queue({ name: JobName.AssetGenerateThumbnails, data: { id: assetId, notify: true } });
|
||||
}
|
||||
|
||||
@OnEvent({ name: 'AssetTrash' })
|
||||
@ -193,7 +193,7 @@ export class NotificationService extends BaseService {
|
||||
@OnEvent({ name: 'UserSignup' })
|
||||
async onUserSignup({ notify, id, tempPassword }: ArgOf<'UserSignup'>) {
|
||||
if (notify) {
|
||||
await this.jobRepository.queue({ name: JobName.NotifySignup, data: { id, tempPassword } });
|
||||
await this.jobRepository.queue({ name: JobName.NotifyUserSignup, data: { id, tempPassword } });
|
||||
}
|
||||
}
|
||||
|
||||
@ -313,8 +313,8 @@ export class NotificationService extends BaseService {
|
||||
return { name, html: templateResponse };
|
||||
}
|
||||
|
||||
@OnJob({ name: JobName.NotifySignup, queue: QueueName.Notification })
|
||||
async handleUserSignup({ id, tempPassword }: JobOf<JobName.NotifySignup>) {
|
||||
@OnJob({ name: JobName.NotifyUserSignup, queue: QueueName.Notification })
|
||||
async handleUserSignup({ id, tempPassword }: JobOf<JobName.NotifyUserSignup>) {
|
||||
const user = await this.userRepository.get(id, { withDeleted: false });
|
||||
if (!user) {
|
||||
return JobStatus.Skipped;
|
||||
|
@ -276,7 +276,7 @@ describe(PersonService.name, () => {
|
||||
},
|
||||
]);
|
||||
expect(mocks.job.queue).toHaveBeenCalledWith({
|
||||
name: JobName.GeneratePersonThumbnail,
|
||||
name: JobName.PersonGenerateThumbnail,
|
||||
data: { id: 'person-1' },
|
||||
});
|
||||
expect(mocks.access.person.checkOwnerAccess).toHaveBeenCalledWith(authStub.admin.user.id, new Set(['person-1']));
|
||||
@ -337,7 +337,7 @@ describe(PersonService.name, () => {
|
||||
|
||||
expect(mocks.job.queueAll).toHaveBeenCalledWith([
|
||||
{
|
||||
name: JobName.GeneratePersonThumbnail,
|
||||
name: JobName.PersonGenerateThumbnail,
|
||||
data: { id: personStub.newThumbnail.id },
|
||||
},
|
||||
]);
|
||||
@ -373,7 +373,7 @@ describe(PersonService.name, () => {
|
||||
await sut.createNewFeaturePhoto([personStub.newThumbnail.id]);
|
||||
expect(mocks.job.queueAll).toHaveBeenCalledWith([
|
||||
{
|
||||
name: JobName.GeneratePersonThumbnail,
|
||||
name: JobName.PersonGenerateThumbnail,
|
||||
data: { id: personStub.newThumbnail.id },
|
||||
},
|
||||
]);
|
||||
@ -462,7 +462,7 @@ describe(PersonService.name, () => {
|
||||
expect(mocks.person.vacuum).not.toHaveBeenCalled();
|
||||
expect(mocks.job.queueAll).toHaveBeenCalledWith([
|
||||
{
|
||||
name: JobName.FaceDetection,
|
||||
name: JobName.AssetDetectFaces,
|
||||
data: { id: assetStub.image.id },
|
||||
},
|
||||
]);
|
||||
@ -481,7 +481,7 @@ describe(PersonService.name, () => {
|
||||
expect(mocks.assetJob.streamForDetectFacesJob).toHaveBeenCalledWith(true);
|
||||
expect(mocks.job.queueAll).toHaveBeenCalledWith([
|
||||
{
|
||||
name: JobName.FaceDetection,
|
||||
name: JobName.AssetDetectFaces,
|
||||
data: { id: assetStub.image.id },
|
||||
},
|
||||
]);
|
||||
@ -499,7 +499,7 @@ describe(PersonService.name, () => {
|
||||
expect(mocks.assetJob.streamForDetectFacesJob).toHaveBeenCalledWith(undefined);
|
||||
expect(mocks.job.queueAll).toHaveBeenCalledWith([
|
||||
{
|
||||
name: JobName.FaceDetection,
|
||||
name: JobName.AssetDetectFaces,
|
||||
data: { id: assetStub.image.id },
|
||||
},
|
||||
]);
|
||||
@ -518,7 +518,7 @@ describe(PersonService.name, () => {
|
||||
expect(mocks.assetJob.streamForDetectFacesJob).toHaveBeenCalledWith(true);
|
||||
expect(mocks.job.queueAll).toHaveBeenCalledWith([
|
||||
{
|
||||
name: JobName.FaceDetection,
|
||||
name: JobName.AssetDetectFaces,
|
||||
data: { id: assetStub.image.id },
|
||||
},
|
||||
]);
|
||||
@ -754,7 +754,7 @@ describe(PersonService.name, () => {
|
||||
|
||||
expect(mocks.person.refreshFaces).toHaveBeenCalledWith([face], [], [faceSearch]);
|
||||
expect(mocks.job.queueAll).toHaveBeenCalledWith([
|
||||
{ name: JobName.QueueFacialRecognition, data: { force: false } },
|
||||
{ name: JobName.FacialRecognitionQueueAll, data: { force: false } },
|
||||
{ name: JobName.FacialRecognition, data: { id: faceId } },
|
||||
]);
|
||||
expect(mocks.person.reassignFace).not.toHaveBeenCalled();
|
||||
@ -790,7 +790,7 @@ describe(PersonService.name, () => {
|
||||
|
||||
expect(mocks.person.refreshFaces).toHaveBeenCalledWith([face], [faceStub.primaryFace1.id], [faceSearch]);
|
||||
expect(mocks.job.queueAll).toHaveBeenCalledWith([
|
||||
{ name: JobName.QueueFacialRecognition, data: { force: false } },
|
||||
{ name: JobName.FacialRecognitionQueueAll, data: { force: false } },
|
||||
{ name: JobName.FacialRecognition, data: { id: faceId } },
|
||||
]);
|
||||
expect(mocks.person.reassignFace).not.toHaveBeenCalled();
|
||||
@ -830,7 +830,7 @@ describe(PersonService.name, () => {
|
||||
|
||||
expect(mocks.person.refreshFaces).toHaveBeenCalledWith([face], [], [faceSearch]);
|
||||
expect(mocks.job.queueAll).toHaveBeenCalledWith([
|
||||
{ name: JobName.QueueFacialRecognition, data: { force: false } },
|
||||
{ name: JobName.FacialRecognitionQueueAll, data: { force: false } },
|
||||
{ name: JobName.FacialRecognition, data: { id: faceId } },
|
||||
]);
|
||||
expect(mocks.person.reassignFace).not.toHaveBeenCalled();
|
||||
|
@ -140,7 +140,7 @@ export class PersonService extends BaseService {
|
||||
|
||||
if (assetFace) {
|
||||
await this.personRepository.update({ id: personId, faceAssetId: assetFace.id });
|
||||
jobs.push({ name: JobName.GeneratePersonThumbnail, data: { id: personId } });
|
||||
jobs.push({ name: JobName.PersonGenerateThumbnail, data: { id: personId } });
|
||||
}
|
||||
}
|
||||
|
||||
@ -211,7 +211,7 @@ export class PersonService extends BaseService {
|
||||
});
|
||||
|
||||
if (assetId) {
|
||||
await this.jobRepository.queue({ name: JobName.GeneratePersonThumbnail, data: { id } });
|
||||
await this.jobRepository.queue({ name: JobName.PersonGenerateThumbnail, data: { id } });
|
||||
}
|
||||
|
||||
return mapPerson(person);
|
||||
@ -261,8 +261,8 @@ export class PersonService extends BaseService {
|
||||
return JobStatus.Success;
|
||||
}
|
||||
|
||||
@OnJob({ name: JobName.QueueFaceDetection, queue: QueueName.FaceDetection })
|
||||
async handleQueueDetectFaces({ force }: JobOf<JobName.QueueFaceDetection>): Promise<JobStatus> {
|
||||
@OnJob({ name: JobName.AssetDetectFacesQueueAll, queue: QueueName.FaceDetection })
|
||||
async handleQueueDetectFaces({ force }: JobOf<JobName.AssetDetectFacesQueueAll>): Promise<JobStatus> {
|
||||
const { machineLearning } = await this.getConfig({ withCache: false });
|
||||
if (!isFacialRecognitionEnabled(machineLearning)) {
|
||||
return JobStatus.Skipped;
|
||||
@ -277,7 +277,7 @@ export class PersonService extends BaseService {
|
||||
let jobs: JobItem[] = [];
|
||||
const assets = this.assetJobRepository.streamForDetectFacesJob(force);
|
||||
for await (const asset of assets) {
|
||||
jobs.push({ name: JobName.FaceDetection, data: { id: asset.id } });
|
||||
jobs.push({ name: JobName.AssetDetectFaces, data: { id: asset.id } });
|
||||
|
||||
if (jobs.length >= JOBS_ASSET_PAGINATION_SIZE) {
|
||||
await this.jobRepository.queueAll(jobs);
|
||||
@ -294,8 +294,8 @@ export class PersonService extends BaseService {
|
||||
return JobStatus.Success;
|
||||
}
|
||||
|
||||
@OnJob({ name: JobName.FaceDetection, queue: QueueName.FaceDetection })
|
||||
async handleDetectFaces({ id }: JobOf<JobName.FaceDetection>): Promise<JobStatus> {
|
||||
@OnJob({ name: JobName.AssetDetectFaces, queue: QueueName.FaceDetection })
|
||||
async handleDetectFaces({ id }: JobOf<JobName.AssetDetectFaces>): Promise<JobStatus> {
|
||||
const { machineLearning } = await this.getConfig({ withCache: true });
|
||||
if (!isFacialRecognitionEnabled(machineLearning)) {
|
||||
return JobStatus.Skipped;
|
||||
@ -369,7 +369,7 @@ export class PersonService extends BaseService {
|
||||
if (facesToAdd.length > 0) {
|
||||
this.logger.log(`Detected ${facesToAdd.length} new faces in asset ${id}`);
|
||||
const jobs = facesToAdd.map((face) => ({ name: JobName.FacialRecognition, data: { id: face.id } }) as const);
|
||||
await this.jobRepository.queueAll([{ name: JobName.QueueFacialRecognition, data: { force: false } }, ...jobs]);
|
||||
await this.jobRepository.queueAll([{ name: JobName.FacialRecognitionQueueAll, data: { force: false } }, ...jobs]);
|
||||
} else if (embeddings.length > 0) {
|
||||
this.logger.log(`Added ${embeddings.length} face embeddings for asset ${id}`);
|
||||
}
|
||||
@ -396,8 +396,8 @@ export class PersonService extends BaseService {
|
||||
return intersection / union;
|
||||
}
|
||||
|
||||
@OnJob({ name: JobName.QueueFacialRecognition, queue: QueueName.FacialRecognition })
|
||||
async handleQueueRecognizeFaces({ force, nightly }: JobOf<JobName.QueueFacialRecognition>): Promise<JobStatus> {
|
||||
@OnJob({ name: JobName.FacialRecognitionQueueAll, queue: QueueName.FacialRecognition })
|
||||
async handleQueueRecognizeFaces({ force, nightly }: JobOf<JobName.FacialRecognitionQueueAll>): Promise<JobStatus> {
|
||||
const { machineLearning } = await this.getConfig({ withCache: false });
|
||||
if (!isFacialRecognitionEnabled(machineLearning)) {
|
||||
return JobStatus.Skipped;
|
||||
@ -526,7 +526,7 @@ export class PersonService extends BaseService {
|
||||
if (isCore && !personId) {
|
||||
this.logger.log(`Creating new person for face ${id}`);
|
||||
const newPerson = await this.personRepository.create({ ownerId: face.asset.ownerId, faceAssetId: face.id });
|
||||
await this.jobRepository.queue({ name: JobName.GeneratePersonThumbnail, data: { id: newPerson.id } });
|
||||
await this.jobRepository.queue({ name: JobName.PersonGenerateThumbnail, data: { id: newPerson.id } });
|
||||
personId = newPerson.id;
|
||||
}
|
||||
|
||||
@ -538,8 +538,8 @@ export class PersonService extends BaseService {
|
||||
return JobStatus.Success;
|
||||
}
|
||||
|
||||
@OnJob({ name: JobName.MigratePerson, queue: QueueName.Migration })
|
||||
async handlePersonMigration({ id }: JobOf<JobName.MigratePerson>): Promise<JobStatus> {
|
||||
@OnJob({ name: JobName.PersonFileMigration, queue: QueueName.Migration })
|
||||
async handlePersonMigration({ id }: JobOf<JobName.PersonFileMigration>): Promise<JobStatus> {
|
||||
const person = await this.personRepository.getById(id);
|
||||
if (!person) {
|
||||
return JobStatus.Failed;
|
||||
|
@ -14,7 +14,7 @@ import { BaseService } from 'src/services/base.service';
|
||||
|
||||
@Injectable()
|
||||
export class SessionService extends BaseService {
|
||||
@OnJob({ name: JobName.CleanOldSessionTokens, queue: QueueName.BackgroundTask })
|
||||
@OnJob({ name: JobName.SessionCleanup, queue: QueueName.BackgroundTask })
|
||||
async handleCleanup(): Promise<JobStatus> {
|
||||
const sessions = await this.sessionRepository.cleanup();
|
||||
for (const session of sessions) {
|
||||
|
@ -64,8 +64,8 @@ export class SmartInfoService extends BaseService {
|
||||
});
|
||||
}
|
||||
|
||||
@OnJob({ name: JobName.QueueSmartSearch, queue: QueueName.SmartSearch })
|
||||
async handleQueueEncodeClip({ force }: JobOf<JobName.QueueSmartSearch>): Promise<JobStatus> {
|
||||
@OnJob({ name: JobName.SmartSearchQueueAll, queue: QueueName.SmartSearch })
|
||||
async handleQueueEncodeClip({ force }: JobOf<JobName.SmartSearchQueueAll>): Promise<JobStatus> {
|
||||
const { machineLearning } = await this.getConfig({ withCache: false });
|
||||
if (!isSmartSearchEnabled(machineLearning)) {
|
||||
return JobStatus.Skipped;
|
||||
|
@ -62,8 +62,8 @@ export class StorageService extends BaseService {
|
||||
});
|
||||
}
|
||||
|
||||
@OnJob({ name: JobName.DeleteFiles, queue: QueueName.BackgroundTask })
|
||||
async handleDeleteFiles(job: JobOf<JobName.DeleteFiles>): Promise<JobStatus> {
|
||||
@OnJob({ name: JobName.FileDelete, queue: QueueName.BackgroundTask })
|
||||
async handleDeleteFiles(job: JobOf<JobName.FileDelete>): Promise<JobStatus> {
|
||||
const { files } = job;
|
||||
|
||||
// TODO: one job per file
|
||||
|
@ -77,24 +77,24 @@ describe(TrashService.name, () => {
|
||||
mocks.trash.empty.mockResolvedValue(1);
|
||||
await expect(sut.empty(authStub.user1)).resolves.toEqual({ count: 1 });
|
||||
expect(mocks.trash.empty).toHaveBeenCalledWith('user-id');
|
||||
expect(mocks.job.queue).toHaveBeenCalledWith({ name: JobName.QueueTrashEmpty, data: {} });
|
||||
expect(mocks.job.queue).toHaveBeenCalledWith({ name: JobName.AssetEmptyTrash, data: {} });
|
||||
});
|
||||
});
|
||||
|
||||
describe('onAssetsDelete', () => {
|
||||
it('should queue the empty trash job', async () => {
|
||||
await expect(sut.onAssetsDelete()).resolves.toBeUndefined();
|
||||
expect(mocks.job.queue).toHaveBeenCalledWith({ name: JobName.QueueTrashEmpty, data: {} });
|
||||
expect(mocks.job.queue).toHaveBeenCalledWith({ name: JobName.AssetEmptyTrash, data: {} });
|
||||
});
|
||||
});
|
||||
|
||||
describe('handleQueueEmptyTrash', () => {
|
||||
it('should queue asset delete jobs', async () => {
|
||||
mocks.trash.getDeletedIds.mockReturnValue(makeAssetIdStream(1));
|
||||
await expect(sut.handleQueueEmptyTrash()).resolves.toEqual(JobStatus.Success);
|
||||
await expect(sut.handleEmptyTrash()).resolves.toEqual(JobStatus.Success);
|
||||
expect(mocks.job.queueAll).toHaveBeenCalledWith([
|
||||
{
|
||||
name: JobName.AssetDeletion,
|
||||
name: JobName.AssetDelete,
|
||||
data: { id: 'asset-1', deleteOnDisk: true },
|
||||
},
|
||||
]);
|
||||
|
@ -35,18 +35,18 @@ export class TrashService extends BaseService {
|
||||
async empty(auth: AuthDto): Promise<TrashResponseDto> {
|
||||
const count = await this.trashRepository.empty(auth.user.id);
|
||||
if (count > 0) {
|
||||
await this.jobRepository.queue({ name: JobName.QueueTrashEmpty, data: {} });
|
||||
await this.jobRepository.queue({ name: JobName.AssetEmptyTrash, data: {} });
|
||||
}
|
||||
return { count };
|
||||
}
|
||||
|
||||
@OnEvent({ name: 'AssetDeleteAll' })
|
||||
async onAssetsDelete() {
|
||||
await this.jobRepository.queue({ name: JobName.QueueTrashEmpty, data: {} });
|
||||
await this.jobRepository.queue({ name: JobName.AssetEmptyTrash, data: {} });
|
||||
}
|
||||
|
||||
@OnJob({ name: JobName.QueueTrashEmpty, queue: QueueName.BackgroundTask })
|
||||
async handleQueueEmptyTrash() {
|
||||
@OnJob({ name: JobName.AssetEmptyTrash, queue: QueueName.BackgroundTask })
|
||||
async handleEmptyTrash() {
|
||||
const assets = this.trashRepository.getDeletedIds();
|
||||
|
||||
let count = 0;
|
||||
@ -74,7 +74,7 @@ export class TrashService extends BaseService {
|
||||
this.logger.debug(`Queueing ${ids.length} asset(s) for deletion from the trash`);
|
||||
await this.jobRepository.queueAll(
|
||||
ids.map((assetId) => ({
|
||||
name: JobName.AssetDeletion,
|
||||
name: JobName.AssetDelete,
|
||||
data: {
|
||||
id: assetId,
|
||||
deleteOnDisk: true,
|
||||
|
@ -158,7 +158,7 @@ describe(UserAdminService.name, () => {
|
||||
deletedAt: expect.any(Date),
|
||||
});
|
||||
expect(mocks.job.queue).toHaveBeenCalledWith({
|
||||
name: JobName.UserDeletion,
|
||||
name: JobName.UserDelete,
|
||||
data: { id: userStub.user1.id, force: true },
|
||||
});
|
||||
});
|
||||
|
@ -104,7 +104,7 @@ export class UserAdminService extends BaseService {
|
||||
const user = await this.userRepository.update(id, { status, deletedAt: new Date() });
|
||||
|
||||
if (force) {
|
||||
await this.jobRepository.queue({ name: JobName.UserDeletion, data: { id: user.id, force } });
|
||||
await this.jobRepository.queue({ name: JobName.UserDelete, data: { id: user.id, force } });
|
||||
}
|
||||
|
||||
return mapUserAdmin(user);
|
||||
|
@ -122,7 +122,7 @@ describe(UserService.name, () => {
|
||||
|
||||
await sut.createProfileImage(authStub.admin, file);
|
||||
|
||||
expect(mocks.job.queue.mock.calls).toEqual([[{ name: JobName.DeleteFiles, data: { files } }]]);
|
||||
expect(mocks.job.queue.mock.calls).toEqual([[{ name: JobName.FileDelete, data: { files } }]]);
|
||||
});
|
||||
|
||||
it('should not delete the profile image if it has not been set', async () => {
|
||||
@ -156,7 +156,7 @@ describe(UserService.name, () => {
|
||||
|
||||
await sut.deleteProfileImage(authStub.admin);
|
||||
|
||||
expect(mocks.job.queue.mock.calls).toEqual([[{ name: JobName.DeleteFiles, data: { files } }]]);
|
||||
expect(mocks.job.queue.mock.calls).toEqual([[{ name: JobName.FileDelete, data: { files } }]]);
|
||||
});
|
||||
});
|
||||
|
||||
@ -211,7 +211,7 @@ describe(UserService.name, () => {
|
||||
await sut.handleUserDeleteCheck();
|
||||
|
||||
expect(mocks.user.getDeletedAfter).toHaveBeenCalled();
|
||||
expect(mocks.job.queueAll).toHaveBeenCalledWith([{ name: JobName.UserDeletion, data: { id: user.id } }]);
|
||||
expect(mocks.job.queueAll).toHaveBeenCalledWith([{ name: JobName.UserDelete, data: { id: user.id } }]);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -99,7 +99,7 @@ export class UserService extends BaseService {
|
||||
});
|
||||
|
||||
if (oldpath !== '') {
|
||||
await this.jobRepository.queue({ name: JobName.DeleteFiles, data: { files: [oldpath] } });
|
||||
await this.jobRepository.queue({ name: JobName.FileDelete, data: { files: [oldpath] } });
|
||||
}
|
||||
|
||||
return {
|
||||
@ -115,7 +115,7 @@ export class UserService extends BaseService {
|
||||
throw new BadRequestException("Can't delete a missing profile Image");
|
||||
}
|
||||
await this.userRepository.update(auth.user.id, { profileImagePath: '', profileChangedAt: new Date() });
|
||||
await this.jobRepository.queue({ name: JobName.DeleteFiles, data: { files: [user.profileImagePath] } });
|
||||
await this.jobRepository.queue({ name: JobName.FileDelete, data: { files: [user.profileImagePath] } });
|
||||
}
|
||||
|
||||
async getProfileImage(id: string): Promise<ImmichFileResponse> {
|
||||
@ -213,7 +213,7 @@ export class UserService extends BaseService {
|
||||
};
|
||||
}
|
||||
|
||||
@OnJob({ name: JobName.userSyncUsage, queue: QueueName.BackgroundTask })
|
||||
@OnJob({ name: JobName.UserSyncUsage, queue: QueueName.BackgroundTask })
|
||||
async handleUserSyncUsage(): Promise<JobStatus> {
|
||||
await this.userRepository.syncUsage();
|
||||
return JobStatus.Success;
|
||||
@ -223,12 +223,12 @@ export class UserService extends BaseService {
|
||||
async handleUserDeleteCheck(): Promise<JobStatus> {
|
||||
const config = await this.getConfig({ withCache: false });
|
||||
const users = await this.userRepository.getDeletedAfter(DateTime.now().minus({ days: config.user.deleteDelay }));
|
||||
await this.jobRepository.queueAll(users.map((user) => ({ name: JobName.UserDeletion, data: { id: user.id } })));
|
||||
await this.jobRepository.queueAll(users.map((user) => ({ name: JobName.UserDelete, data: { id: user.id } })));
|
||||
return JobStatus.Success;
|
||||
}
|
||||
|
||||
@OnJob({ name: JobName.UserDeletion, queue: QueueName.BackgroundTask })
|
||||
async handleUserDelete({ id, force }: JobOf<JobName.UserDeletion>): Promise<JobStatus> {
|
||||
@OnJob({ name: JobName.UserDelete, queue: QueueName.BackgroundTask })
|
||||
async handleUserDelete({ id, force }: JobOf<JobName.UserDelete>): Promise<JobStatus> {
|
||||
const config = await this.getConfig({ withCache: false });
|
||||
const user = await this.userRepository.get(id, { withDeleted: true });
|
||||
if (!user) {
|
||||
|
@ -41,7 +41,7 @@ export class VersionService extends BaseService {
|
||||
|
||||
const needsNewMemories = semver.lt(previousVersion, '1.129.0');
|
||||
if (needsNewMemories) {
|
||||
await this.jobRepository.queue({ name: JobName.MemoriesCreate });
|
||||
await this.jobRepository.queue({ name: JobName.MemoryGenerate });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -273,93 +273,93 @@ export interface QueueStatus {
|
||||
|
||||
export type JobItem =
|
||||
// Backups
|
||||
| { name: JobName.BackupDatabase; data?: IBaseJob }
|
||||
| { name: JobName.DatabaseBackup; data?: IBaseJob }
|
||||
|
||||
// Transcoding
|
||||
| { name: JobName.QueueVideoConversion; data: IBaseJob }
|
||||
| { name: JobName.VideoConversation; data: IEntityJob }
|
||||
| { name: JobName.AssetEncodeVideoQueueAll; data: IBaseJob }
|
||||
| { name: JobName.AssetEncodeVideo; data: IEntityJob }
|
||||
|
||||
// Thumbnails
|
||||
| { name: JobName.QueueGenerateThumbnails; data: IBaseJob }
|
||||
| { name: JobName.GenerateThumbnails; data: IEntityJob }
|
||||
| { name: JobName.AssetGenerateThumbnailsQueueAll; data: IBaseJob }
|
||||
| { name: JobName.AssetGenerateThumbnails; data: IEntityJob }
|
||||
|
||||
// User
|
||||
| { name: JobName.UserDeleteCheck; data?: IBaseJob }
|
||||
| { name: JobName.UserDeletion; data: IEntityJob }
|
||||
| { name: JobName.userSyncUsage; data?: IBaseJob }
|
||||
| { name: JobName.UserDelete; data: IEntityJob }
|
||||
| { name: JobName.UserSyncUsage; data?: IBaseJob }
|
||||
|
||||
// Storage Template
|
||||
| { name: JobName.StorageTemplateMigration; data?: IBaseJob }
|
||||
| { name: JobName.StorageTemplateMigrationSingle; data: IEntityJob }
|
||||
|
||||
// Migration
|
||||
| { name: JobName.QueueMigration; data?: IBaseJob }
|
||||
| { name: JobName.MigrateAsset; data: IEntityJob }
|
||||
| { name: JobName.MigratePerson; data: IEntityJob }
|
||||
| { name: JobName.FileMigrationQueueAll; data?: IBaseJob }
|
||||
| { name: JobName.AssetFileMigration; data: IEntityJob }
|
||||
| { name: JobName.PersonFileMigration; data: IEntityJob }
|
||||
|
||||
// Metadata Extraction
|
||||
| { name: JobName.QueueMetadataExtraction; data: IBaseJob }
|
||||
| { name: JobName.MetadataExtraction; data: IEntityJob }
|
||||
| { name: JobName.AssetExtractMetadataQueueAll; data: IBaseJob }
|
||||
| { name: JobName.AssetExtractMetadata; data: IEntityJob }
|
||||
|
||||
// Notifications
|
||||
| { name: JobName.NotificationsCleanup; data?: IBaseJob }
|
||||
|
||||
// Sidecar Scanning
|
||||
| { name: JobName.QueueSidecar; data: IBaseJob }
|
||||
| { name: JobName.SidecarQueueAll; data: IBaseJob }
|
||||
| { name: JobName.SidecarDiscovery; data: IEntityJob }
|
||||
| { name: JobName.SidecarSync; data: IEntityJob }
|
||||
| { name: JobName.SidecarWrite; data: ISidecarWriteJob }
|
||||
|
||||
// Facial Recognition
|
||||
| { name: JobName.QueueFaceDetection; data: IBaseJob }
|
||||
| { name: JobName.FaceDetection; data: IEntityJob }
|
||||
| { name: JobName.QueueFacialRecognition; data: INightlyJob }
|
||||
| { name: JobName.AssetDetectFacesQueueAll; data: IBaseJob }
|
||||
| { name: JobName.AssetDetectFaces; data: IEntityJob }
|
||||
| { name: JobName.FacialRecognitionQueueAll; data: INightlyJob }
|
||||
| { name: JobName.FacialRecognition; data: IDeferrableJob }
|
||||
| { name: JobName.GeneratePersonThumbnail; data: IEntityJob }
|
||||
| { name: JobName.PersonGenerateThumbnail; data: IEntityJob }
|
||||
|
||||
// Smart Search
|
||||
| { name: JobName.QueueSmartSearch; data: IBaseJob }
|
||||
| { name: JobName.SmartSearchQueueAll; data: IBaseJob }
|
||||
| { name: JobName.SmartSearch; data: IEntityJob }
|
||||
| { name: JobName.QueueTrashEmpty; data?: IBaseJob }
|
||||
| { name: JobName.AssetEmptyTrash; data?: IBaseJob }
|
||||
|
||||
// Duplicate Detection
|
||||
| { name: JobName.QueueDuplicateDetection; data: IBaseJob }
|
||||
| { name: JobName.DuplicateDetection; data: IEntityJob }
|
||||
| { name: JobName.AssetDetectDuplicatesQueueAll; data: IBaseJob }
|
||||
| { name: JobName.AssetDetectDuplicates; data: IEntityJob }
|
||||
|
||||
// Memories
|
||||
| { name: JobName.MemoriesCleanup; data?: IBaseJob }
|
||||
| { name: JobName.MemoriesCreate; data?: IBaseJob }
|
||||
| { name: JobName.MemoryCleanup; data?: IBaseJob }
|
||||
| { name: JobName.MemoryGenerate; data?: IBaseJob }
|
||||
|
||||
// Filesystem
|
||||
| { name: JobName.DeleteFiles; data: IDeleteFilesJob }
|
||||
| { name: JobName.FileDelete; data: IDeleteFilesJob }
|
||||
|
||||
// Cleanup
|
||||
| { name: JobName.CleanOldAuditLogs; data?: IBaseJob }
|
||||
| { name: JobName.CleanOldSessionTokens; data?: IBaseJob }
|
||||
| { name: JobName.AuditLogCleanup; data?: IBaseJob }
|
||||
| { name: JobName.SessionCleanup; data?: IBaseJob }
|
||||
|
||||
// Tags
|
||||
| { name: JobName.TagCleanup; data?: IBaseJob }
|
||||
|
||||
// Asset Deletion
|
||||
| { name: JobName.PersonCleanup; data?: IBaseJob }
|
||||
| { name: JobName.AssetDeletion; data: IAssetDeleteJob }
|
||||
| { name: JobName.AssetDeletionCheck; data?: IBaseJob }
|
||||
| { name: JobName.AssetDelete; data: IAssetDeleteJob }
|
||||
| { name: JobName.AssetDeleteCheck; data?: IBaseJob }
|
||||
|
||||
// Library Management
|
||||
| { name: JobName.LibrarySyncFiles; data: ILibraryFileJob }
|
||||
| { name: JobName.LibraryQueueSyncFiles; data: IEntityJob }
|
||||
| { name: JobName.LibraryQueueSyncAssets; data: IEntityJob }
|
||||
| { name: JobName.LibrarySyncFilesQueueAll; data: IEntityJob }
|
||||
| { name: JobName.LibrarySyncAssetsQueueAll; data: IEntityJob }
|
||||
| { name: JobName.LibrarySyncAssets; data: ILibraryBulkIdsJob }
|
||||
| { name: JobName.LibraryAssetRemoval; data: ILibraryFileJob }
|
||||
| { name: JobName.LibraryRemoveAsset; data: ILibraryFileJob }
|
||||
| { name: JobName.LibraryDelete; data: IEntityJob }
|
||||
| { name: JobName.LibraryQueueScanAll; data?: IBaseJob }
|
||||
| { name: JobName.LibraryQueueCleanup; data: IBaseJob }
|
||||
| { name: JobName.LibraryScanQueueAll; data?: IBaseJob }
|
||||
| { name: JobName.LibraryDeleteCheck; data: IBaseJob }
|
||||
|
||||
// Notification
|
||||
| { name: JobName.SendMail; data: IEmailJob }
|
||||
| { name: JobName.NotifyAlbumInvite; data: INotifyAlbumInviteJob }
|
||||
| { name: JobName.NotifyAlbumUpdate; data: INotifyAlbumUpdateJob }
|
||||
| { name: JobName.NotifySignup; data: INotifySignupJob }
|
||||
| { name: JobName.NotifyUserSignup; data: INotifySignupJob }
|
||||
|
||||
// Version check
|
||||
| { name: JobName.VersionCheck; data: IBaseJob };
|
||||
|
@ -150,7 +150,7 @@ describe(UserService.name, () => {
|
||||
const { user } = await ctx.newUser({ deletedAt: DateTime.now().minus({ days: 60 }).toJSDate() });
|
||||
jobMock.queueAll.mockResolvedValue(void 0);
|
||||
await expect(sut.handleUserDeleteCheck()).resolves.toEqual(JobStatus.Success);
|
||||
expect(jobMock.queueAll).toHaveBeenCalledExactlyOnceWith([{ name: JobName.UserDeletion, data: { id: user.id } }]);
|
||||
expect(jobMock.queueAll).toHaveBeenCalledExactlyOnceWith([{ name: JobName.UserDelete, data: { id: user.id } }]);
|
||||
});
|
||||
|
||||
it('should skip a recently deleted user', async () => {
|
||||
|
@ -53,7 +53,7 @@ describe(VersionService.name, () => {
|
||||
await versionHistoryRepo.create({ version: 'v1.128.0' });
|
||||
await sut.onBootstrap();
|
||||
|
||||
expect(jobMock.queue).toHaveBeenCalledWith({ name: JobName.MemoriesCreate });
|
||||
expect(jobMock.queue).toHaveBeenCalledWith({ name: JobName.MemoryGenerate });
|
||||
});
|
||||
|
||||
it('should not queue memory creation when upgrading from 1.129.0', async () => {
|
||||
|
Loading…
x
Reference in New Issue
Block a user