refactor: job names (#19949)

This commit is contained in:
Jason Rasmussen 2025-07-15 18:39:00 -04:00 committed by GitHub
parent e73abe0762
commit bcb968e3d1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
37 changed files with 334 additions and 343 deletions

View File

@ -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 {

View File

@ -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;

View File

@ -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'] },
});
});

View File

@ -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;
}

View File

@ -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' } }]);
});
});

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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);

View File

@ -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();

View File

@ -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 },
},
]);

View File

@ -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;

View File

@ -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: [],
},
{

View File

@ -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;
}

View File

@ -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 } },
]);
});
});

View File

@ -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;

View File

@ -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] },
});
});

View File

@ -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`);

View File

@ -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();
}

View File

@ -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 },
},
]);

View File

@ -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);
}

View File

@ -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 },
});
});

View File

@ -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;

View File

@ -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();

View File

@ -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;

View File

@ -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) {

View File

@ -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;

View File

@ -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

View 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 },
},
]);

View File

@ -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,

View File

@ -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 },
});
});

View File

@ -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);

View File

@ -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 } }]);
});
});

View File

@ -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) {

View File

@ -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 });
}
}
});

View File

@ -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 };

View File

@ -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 () => {

View File

@ -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 () => {