mirror of
https://github.com/KevinMidboe/immich.git
synced 2025-12-08 04:09:07 +00:00
refactor: job names (#1343)
* refactor: job names * refactor: remove jobId
This commit is contained in:
@@ -10,7 +10,7 @@ import {
|
||||
StreamableFile,
|
||||
} from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { createHash, randomUUID } from 'node:crypto';
|
||||
import { createHash } from 'node:crypto';
|
||||
import { QueryFailedError, Repository } from 'typeorm';
|
||||
import { AuthUserDto } from '../../decorators/auth-user.decorator';
|
||||
import { AssetEntity, AssetType, SharedLinkType } from '@app/infra';
|
||||
@@ -43,13 +43,7 @@ import { CheckExistingAssetsResponseDto } from './response-dto/check-existing-as
|
||||
import { UpdateAssetDto } from './dto/update-asset.dto';
|
||||
import { AssetFileUploadResponseDto } from './response-dto/asset-file-upload-response.dto';
|
||||
import { BackgroundTaskService } from '../../modules/background-task/background-task.service';
|
||||
import {
|
||||
assetUploadedProcessorName,
|
||||
IAssetUploadedJob,
|
||||
IVideoTranscodeJob,
|
||||
mp4ConversionProcessorName,
|
||||
QueueNameEnum,
|
||||
} from '@app/job';
|
||||
import { IAssetUploadedJob, IVideoTranscodeJob, QueueName, JobName } from '@app/job';
|
||||
import { InjectQueue } from '@nestjs/bull';
|
||||
import { Queue } from 'bull';
|
||||
import { DownloadService } from '../../modules/download/download.service';
|
||||
@@ -80,16 +74,16 @@ export class AssetService {
|
||||
|
||||
private backgroundTaskService: BackgroundTaskService,
|
||||
|
||||
@InjectQueue(QueueNameEnum.ASSET_UPLOADED)
|
||||
@InjectQueue(QueueName.ASSET_UPLOADED)
|
||||
private assetUploadedQueue: Queue<IAssetUploadedJob>,
|
||||
|
||||
@InjectQueue(QueueNameEnum.VIDEO_CONVERSION)
|
||||
@InjectQueue(QueueName.VIDEO_CONVERSION)
|
||||
private videoConversionQueue: Queue<IVideoTranscodeJob>,
|
||||
|
||||
private downloadService: DownloadService,
|
||||
|
||||
private storageService: StorageService,
|
||||
@Inject(ISharedLinkRepository) private sharedLinkRepository: ISharedLinkRepository,
|
||||
@Inject(ISharedLinkRepository) sharedLinkRepository: ISharedLinkRepository,
|
||||
) {
|
||||
this.shareCore = new ShareCore(sharedLinkRepository);
|
||||
}
|
||||
@@ -128,11 +122,7 @@ export class AssetService {
|
||||
|
||||
await this.storageService.moveAsset(livePhotoAssetEntity, originalAssetData.originalname);
|
||||
|
||||
await this.videoConversionQueue.add(
|
||||
mp4ConversionProcessorName,
|
||||
{ asset: livePhotoAssetEntity },
|
||||
{ jobId: randomUUID() },
|
||||
);
|
||||
await this.videoConversionQueue.add(JobName.MP4_CONVERSION, { asset: livePhotoAssetEntity });
|
||||
}
|
||||
|
||||
const assetEntity = await this.createUserAsset(
|
||||
@@ -157,7 +147,7 @@ export class AssetService {
|
||||
const movedAsset = await this.storageService.moveAsset(assetEntity, originalAssetData.originalname);
|
||||
|
||||
await this.assetUploadedQueue.add(
|
||||
assetUploadedProcessorName,
|
||||
JobName.ASSET_UPLOADED,
|
||||
{ asset: movedAsset, fileName: originalAssetData.originalname },
|
||||
{ jobId: movedAsset.id },
|
||||
);
|
||||
|
||||
@@ -1,19 +1,8 @@
|
||||
import {
|
||||
exifExtractionProcessorName,
|
||||
generateJPEGThumbnailProcessorName,
|
||||
IMetadataExtractionJob,
|
||||
IThumbnailGenerationJob,
|
||||
IVideoTranscodeJob,
|
||||
MachineLearningJobNameEnum,
|
||||
QueueNameEnum,
|
||||
templateMigrationProcessorName,
|
||||
videoMetadataExtractionProcessorName,
|
||||
} from '@app/job';
|
||||
import { IMetadataExtractionJob, IThumbnailGenerationJob, IVideoTranscodeJob, QueueName, JobName } from '@app/job';
|
||||
import { InjectQueue } from '@nestjs/bull';
|
||||
import { Queue } from 'bull';
|
||||
import { BadRequestException, Inject, Injectable } from '@nestjs/common';
|
||||
import { AllJobStatusResponseDto } from './response-dto/all-job-status-response.dto';
|
||||
import { randomUUID } from 'crypto';
|
||||
import { IAssetRepository } from '../asset/asset-repository';
|
||||
import { AssetType } from '@app/infra';
|
||||
import { GetJobDto, JobId } from './dto/get-job.dto';
|
||||
@@ -24,20 +13,20 @@ import { StorageService } from '@app/storage';
|
||||
@Injectable()
|
||||
export class JobService {
|
||||
constructor(
|
||||
@InjectQueue(QueueNameEnum.THUMBNAIL_GENERATION)
|
||||
@InjectQueue(QueueName.THUMBNAIL_GENERATION)
|
||||
private thumbnailGeneratorQueue: Queue<IThumbnailGenerationJob>,
|
||||
|
||||
@InjectQueue(QueueNameEnum.METADATA_EXTRACTION)
|
||||
@InjectQueue(QueueName.METADATA_EXTRACTION)
|
||||
private metadataExtractionQueue: Queue<IMetadataExtractionJob>,
|
||||
|
||||
@InjectQueue(QueueNameEnum.VIDEO_CONVERSION)
|
||||
@InjectQueue(QueueName.VIDEO_CONVERSION)
|
||||
private videoConversionQueue: Queue<IVideoTranscodeJob>,
|
||||
|
||||
@InjectQueue(QueueNameEnum.MACHINE_LEARNING)
|
||||
@InjectQueue(QueueName.MACHINE_LEARNING)
|
||||
private machineLearningQueue: Queue<IMachineLearningJob>,
|
||||
|
||||
@InjectQueue(QueueNameEnum.STORAGE_MIGRATION)
|
||||
private storageMigrationQueue: Queue,
|
||||
@InjectQueue(QueueName.CONFIG)
|
||||
private configQueue: Queue,
|
||||
|
||||
@Inject(IAssetRepository)
|
||||
private _assetRepository: IAssetRepository,
|
||||
@@ -47,7 +36,7 @@ export class JobService {
|
||||
this.thumbnailGeneratorQueue.empty();
|
||||
this.metadataExtractionQueue.empty();
|
||||
this.videoConversionQueue.empty();
|
||||
this.storageMigrationQueue.empty();
|
||||
this.configQueue.empty();
|
||||
}
|
||||
|
||||
async startJob(jobDto: GetJobDto): Promise<number> {
|
||||
@@ -72,7 +61,7 @@ export class JobService {
|
||||
const metadataExtractionJobCount = await this.metadataExtractionQueue.getJobCounts();
|
||||
const videoConversionJobCount = await this.videoConversionQueue.getJobCounts();
|
||||
const machineLearningJobCount = await this.machineLearningQueue.getJobCounts();
|
||||
const storageMigrationJobCount = await this.storageMigrationQueue.getJobCounts();
|
||||
const storageMigrationJobCount = await this.configQueue.getJobCounts();
|
||||
|
||||
const response = new AllJobStatusResponseDto();
|
||||
response.isThumbnailGenerationActive = Boolean(thumbnailGeneratorJobCount.waiting);
|
||||
@@ -108,8 +97,8 @@ export class JobService {
|
||||
}
|
||||
|
||||
if (query.jobId === JobId.STORAGE_TEMPLATE_MIGRATION) {
|
||||
response.isActive = Boolean((await this.storageMigrationQueue.getJobCounts()).waiting);
|
||||
response.queueCount = await this.storageMigrationQueue.getJobCounts();
|
||||
response.isActive = Boolean((await this.configQueue.getJobCounts()).waiting);
|
||||
response.queueCount = await this.configQueue.getJobCounts();
|
||||
}
|
||||
|
||||
return response;
|
||||
@@ -130,7 +119,7 @@ export class JobService {
|
||||
this.machineLearningQueue.empty();
|
||||
return 0;
|
||||
case JobId.STORAGE_TEMPLATE_MIGRATION:
|
||||
this.storageMigrationQueue.empty();
|
||||
this.configQueue.empty();
|
||||
return 0;
|
||||
default:
|
||||
throw new BadRequestException('Invalid job id');
|
||||
@@ -147,7 +136,7 @@ export class JobService {
|
||||
const assetsWithNoThumbnail = await this._assetRepository.getAssetWithNoThumbnail();
|
||||
|
||||
for (const asset of assetsWithNoThumbnail) {
|
||||
await this.thumbnailGeneratorQueue.add(generateJPEGThumbnailProcessorName, { asset }, { jobId: randomUUID() });
|
||||
await this.thumbnailGeneratorQueue.add(JobName.GENERATE_JPEG_THUMBNAIL, { asset });
|
||||
}
|
||||
|
||||
return assetsWithNoThumbnail.length;
|
||||
@@ -163,17 +152,9 @@ export class JobService {
|
||||
const assetsWithNoExif = await this._assetRepository.getAssetWithNoEXIF();
|
||||
for (const asset of assetsWithNoExif) {
|
||||
if (asset.type === AssetType.VIDEO) {
|
||||
await this.metadataExtractionQueue.add(
|
||||
videoMetadataExtractionProcessorName,
|
||||
{ asset, fileName: asset.id },
|
||||
{ jobId: randomUUID() },
|
||||
);
|
||||
await this.metadataExtractionQueue.add(JobName.EXTRACT_VIDEO_METADATA, { asset, fileName: asset.id });
|
||||
} else {
|
||||
await this.metadataExtractionQueue.add(
|
||||
exifExtractionProcessorName,
|
||||
{ asset, fileName: asset.id },
|
||||
{ jobId: randomUUID() },
|
||||
);
|
||||
await this.metadataExtractionQueue.add(JobName.EXIF_EXTRACTION, { asset, fileName: asset.id });
|
||||
}
|
||||
}
|
||||
return assetsWithNoExif.length;
|
||||
@@ -189,25 +170,21 @@ export class JobService {
|
||||
const assetWithNoSmartInfo = await this._assetRepository.getAssetWithNoSmartInfo();
|
||||
|
||||
for (const asset of assetWithNoSmartInfo) {
|
||||
await this.machineLearningQueue.add(MachineLearningJobNameEnum.IMAGE_TAGGING, { asset }, { jobId: randomUUID() });
|
||||
await this.machineLearningQueue.add(
|
||||
MachineLearningJobNameEnum.OBJECT_DETECTION,
|
||||
{ asset },
|
||||
{ jobId: randomUUID() },
|
||||
);
|
||||
await this.machineLearningQueue.add(JobName.IMAGE_TAGGING, { asset });
|
||||
await this.machineLearningQueue.add(JobName.OBJECT_DETECTION, { asset });
|
||||
}
|
||||
|
||||
return assetWithNoSmartInfo.length;
|
||||
}
|
||||
|
||||
async runStorageMigration() {
|
||||
const jobCount = await this.storageMigrationQueue.getJobCounts();
|
||||
const jobCount = await this.configQueue.getJobCounts();
|
||||
|
||||
if (jobCount.active > 0) {
|
||||
throw new BadRequestException('Storage migration job is already running');
|
||||
}
|
||||
|
||||
await this.storageMigrationQueue.add(templateMigrationProcessorName, {}, { jobId: randomUUID() });
|
||||
await this.configQueue.add(JobName.TEMPLATE_MIGRATION, {});
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { QueueNameEnum, updateTemplateProcessorName } from '@app/job';
|
||||
import { JobName, QueueName } from '@app/job';
|
||||
import {
|
||||
supportedDayTokens,
|
||||
supportedHourTokens,
|
||||
@@ -11,7 +11,6 @@ import {
|
||||
import { InjectQueue } from '@nestjs/bull';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Queue } from 'bull';
|
||||
import { randomUUID } from 'crypto';
|
||||
import { ImmichConfigService } from 'libs/immich-config/src';
|
||||
import { mapConfig, SystemConfigDto } from './dto/system-config.dto';
|
||||
import { SystemConfigTemplateStorageOptionDto } from './response-dto/system-config-template-storage-option.dto';
|
||||
@@ -20,8 +19,7 @@ import { SystemConfigTemplateStorageOptionDto } from './response-dto/system-conf
|
||||
export class SystemConfigService {
|
||||
constructor(
|
||||
private immichConfigService: ImmichConfigService,
|
||||
@InjectQueue(QueueNameEnum.STORAGE_MIGRATION)
|
||||
private storageMigrationQueue: Queue,
|
||||
@InjectQueue(QueueName.CONFIG) private configQueue: Queue,
|
||||
) {}
|
||||
|
||||
public async getConfig(): Promise<SystemConfigDto> {
|
||||
@@ -36,7 +34,7 @@ export class SystemConfigService {
|
||||
|
||||
public async updateConfig(dto: SystemConfigDto): Promise<SystemConfigDto> {
|
||||
const config = await this.immichConfigService.updateConfig(dto);
|
||||
this.storageMigrationQueue.add(updateTemplateProcessorName, {}, { jobId: randomUUID() });
|
||||
this.configQueue.add(JobName.CONFIG_CHANGE, {});
|
||||
return mapConfig(config);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
import { BullModule } from '@nestjs/bull';
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { AssetEntity, ExifEntity, SmartInfoEntity } from '@app/infra';
|
||||
import { QueueName } from '@app/job';
|
||||
import { BackgroundTaskProcessor } from './background-task.processor';
|
||||
import { BackgroundTaskService } from './background-task.service';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
BullModule.registerQueue({
|
||||
name: 'background-task',
|
||||
}),
|
||||
TypeOrmModule.forFeature([AssetEntity, ExifEntity, SmartInfoEntity]),
|
||||
],
|
||||
imports: [BullModule.registerQueue({ name: QueueName.BACKGROUND_TASK })],
|
||||
providers: [BackgroundTaskService, BackgroundTaskProcessor],
|
||||
exports: [BackgroundTaskService, BullModule],
|
||||
})
|
||||
|
||||
@@ -1,23 +1,12 @@
|
||||
import { Process, Processor } from '@nestjs/bull';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { AssetEntity, SmartInfoEntity } from '@app/infra';
|
||||
import { Job } from 'bull';
|
||||
import { AssetResponseDto } from '../../api-v1/asset/response-dto/asset-response.dto';
|
||||
import { assetUtils } from '@app/common/utils';
|
||||
import { Process, Processor } from '@nestjs/bull';
|
||||
import { Job } from 'bull';
|
||||
import { JobName, QueueName } from '@app/job';
|
||||
import { AssetResponseDto } from '../../api-v1/asset/response-dto/asset-response.dto';
|
||||
|
||||
@Processor('background-task')
|
||||
@Processor(QueueName.BACKGROUND_TASK)
|
||||
export class BackgroundTaskProcessor {
|
||||
constructor(
|
||||
@InjectRepository(AssetEntity)
|
||||
private assetRepository: Repository<AssetEntity>,
|
||||
|
||||
@InjectRepository(SmartInfoEntity)
|
||||
private smartInfoRepository: Repository<SmartInfoEntity>,
|
||||
) {}
|
||||
|
||||
// TODO: Should probably use constants / Interfaces for Queue names / data
|
||||
@Process('delete-file-on-disk')
|
||||
@Process(JobName.DELETE_FILE_ON_DISK)
|
||||
async deleteFileOnDisk(job: Job<{ assets: AssetResponseDto[] }>) {
|
||||
const { assets } = job.data;
|
||||
|
||||
|
||||
@@ -1,23 +1,17 @@
|
||||
import { InjectQueue } from '@nestjs/bull/dist/decorators';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Queue } from 'bull';
|
||||
import { randomUUID } from 'node:crypto';
|
||||
import { JobName, QueueName } from '@app/job';
|
||||
import { AssetResponseDto } from '../../api-v1/asset/response-dto/asset-response.dto';
|
||||
|
||||
@Injectable()
|
||||
export class BackgroundTaskService {
|
||||
constructor(
|
||||
@InjectQueue('background-task')
|
||||
@InjectQueue(QueueName.BACKGROUND_TASK)
|
||||
private backgroundTaskQueue: Queue,
|
||||
) {}
|
||||
|
||||
async deleteFileOnDisk(assets: AssetResponseDto[]) {
|
||||
await this.backgroundTaskQueue.add(
|
||||
'delete-file-on-disk',
|
||||
{
|
||||
assets,
|
||||
},
|
||||
{ jobId: randomUUID() },
|
||||
);
|
||||
await this.backgroundTaskQueue.add(JobName.DELETE_FILE_ON_DISK, { assets });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,18 +5,7 @@ import { IsNull, Not, Repository } from 'typeorm';
|
||||
import { AssetEntity, AssetType, ExifEntity, UserEntity } from '@app/infra';
|
||||
import { InjectQueue } from '@nestjs/bull';
|
||||
import { Queue } from 'bull';
|
||||
import { randomUUID } from 'crypto';
|
||||
import {
|
||||
userDeletionProcessorName,
|
||||
exifExtractionProcessorName,
|
||||
generateWEBPThumbnailProcessorName,
|
||||
IMetadataExtractionJob,
|
||||
IVideoTranscodeJob,
|
||||
mp4ConversionProcessorName,
|
||||
QueueNameEnum,
|
||||
reverseGeocodingProcessorName,
|
||||
videoMetadataExtractionProcessorName,
|
||||
} from '@app/job';
|
||||
import { IMetadataExtractionJob, IVideoTranscodeJob, QueueName, JobName } from '@app/job';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { IUserDeletionJob } from '@app/job/interfaces/user-deletion.interface';
|
||||
import { userUtils } from '@app/common';
|
||||
@@ -33,16 +22,16 @@ export class ScheduleTasksService {
|
||||
@InjectRepository(ExifEntity)
|
||||
private exifRepository: Repository<ExifEntity>,
|
||||
|
||||
@InjectQueue(QueueNameEnum.THUMBNAIL_GENERATION)
|
||||
@InjectQueue(QueueName.THUMBNAIL_GENERATION)
|
||||
private thumbnailGeneratorQueue: Queue,
|
||||
|
||||
@InjectQueue(QueueNameEnum.VIDEO_CONVERSION)
|
||||
@InjectQueue(QueueName.VIDEO_CONVERSION)
|
||||
private videoConversionQueue: Queue<IVideoTranscodeJob>,
|
||||
|
||||
@InjectQueue(QueueNameEnum.METADATA_EXTRACTION)
|
||||
@InjectQueue(QueueName.METADATA_EXTRACTION)
|
||||
private metadataExtractionQueue: Queue<IMetadataExtractionJob>,
|
||||
|
||||
@InjectQueue(QueueNameEnum.USER_DELETION)
|
||||
@InjectQueue(QueueName.USER_DELETION)
|
||||
private userDeletionQueue: Queue<IUserDeletionJob>,
|
||||
|
||||
private configService: ConfigService,
|
||||
@@ -62,11 +51,7 @@ export class ScheduleTasksService {
|
||||
}
|
||||
|
||||
for (const asset of assets) {
|
||||
await this.thumbnailGeneratorQueue.add(
|
||||
generateWEBPThumbnailProcessorName,
|
||||
{ asset: asset },
|
||||
{ jobId: randomUUID() },
|
||||
);
|
||||
await this.thumbnailGeneratorQueue.add(JobName.GENERATE_WEBP_THUMBNAIL, { asset: asset });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +69,7 @@ export class ScheduleTasksService {
|
||||
});
|
||||
|
||||
for (const asset of assets) {
|
||||
await this.videoConversionQueue.add(mp4ConversionProcessorName, { asset }, { jobId: randomUUID() });
|
||||
await this.videoConversionQueue.add(JobName.MP4_CONVERSION, { asset });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,10 +88,9 @@ export class ScheduleTasksService {
|
||||
|
||||
for (const exif of exifInfo) {
|
||||
await this.metadataExtractionQueue.add(
|
||||
reverseGeocodingProcessorName,
|
||||
JobName.REVERSE_GEOCODING,
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
{ exifId: exif.id, latitude: exif.latitude!, longitude: exif.longitude! },
|
||||
{ jobId: randomUUID() },
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -122,17 +106,9 @@ export class ScheduleTasksService {
|
||||
|
||||
for (const asset of exifAssets) {
|
||||
if (asset.type === AssetType.VIDEO) {
|
||||
await this.metadataExtractionQueue.add(
|
||||
videoMetadataExtractionProcessorName,
|
||||
{ asset, fileName: asset.id },
|
||||
{ jobId: randomUUID() },
|
||||
);
|
||||
await this.metadataExtractionQueue.add(JobName.EXTRACT_VIDEO_METADATA, { asset, fileName: asset.id });
|
||||
} else {
|
||||
await this.metadataExtractionQueue.add(
|
||||
exifExtractionProcessorName,
|
||||
{ asset, fileName: asset.id },
|
||||
{ jobId: randomUUID() },
|
||||
);
|
||||
await this.metadataExtractionQueue.add(JobName.EXIF_EXTRACTION, { asset, fileName: asset.id });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -142,7 +118,7 @@ export class ScheduleTasksService {
|
||||
const usersToDelete = await this.userRepository.find({ withDeleted: true, where: { deletedAt: Not(IsNull()) } });
|
||||
for (const user of usersToDelete) {
|
||||
if (userUtils.isReadyForDeletion(user)) {
|
||||
await this.userDeletionQueue.add(userDeletionProcessorName, { user: user }, { jobId: randomUUID() });
|
||||
await this.userDeletionQueue.add(JobName.USER_DELETION, { user });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user