refactor(server): jobs and processors (#1787)

* refactor: jobs and processors

* refactor: storage migration processor

* fix: tests

* fix: code warning

* chore: ignore coverage from infra

* fix: sync move asset logic between job core and asset core

* refactor: move error handling inside of catch

* refactor(server): job core into dedicated service calls

* refactor: smart info

* fix: tests

* chore: smart info tests

* refactor: use asset repository

* refactor: thumbnail processor

* chore: coverage reqs
This commit is contained in:
Jason Rasmussen
2023-02-25 09:12:03 -05:00
committed by GitHub
parent 71d8567f18
commit 6c7679714b
108 changed files with 1645 additions and 1072 deletions

View File

@@ -1,13 +1,7 @@
import { AssetEntity, AssetType, ExifEntity } from '@app/infra';
import {
IExifExtractionProcessor,
IReverseGeocodingProcessor,
IVideoLengthExtractionProcessor,
QueueName,
JobName,
} from '@app/domain';
import { IReverseGeocodingJob, IAssetUploadedJob, QueueName, JobName, IAssetRepository } from '@app/domain';
import { Process, Processor } from '@nestjs/bull';
import { Logger } from '@nestjs/common';
import { Inject, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { InjectRepository } from '@nestjs/typeorm';
import { Job } from 'bull';
@@ -19,7 +13,6 @@ import geocoder, { InitOptions } from 'local-reverse-geocoder';
import { getName } from 'i18n-iso-countries';
import fs from 'node:fs';
import { ExifDateTime, exiftool, Tags } from 'exiftool-vendored';
import { IsNull, Not } from 'typeorm';
interface ImmichTags extends Tags {
ContentIdentifier?: string;
@@ -79,9 +72,7 @@ export class MetadataExtractionProcessor {
private logger = new Logger(MetadataExtractionProcessor.name);
private isGeocodeInitialized = false;
constructor(
@InjectRepository(AssetEntity)
private assetRepository: Repository<AssetEntity>,
@Inject(IAssetRepository) private assetRepository: IAssetRepository,
@InjectRepository(ExifEntity)
private exifRepository: Repository<ExifEntity>,
@@ -141,7 +132,7 @@ export class MetadataExtractionProcessor {
}
@Process(JobName.EXIF_EXTRACTION)
async extractExifInfo(job: Job<IExifExtractionProcessor>) {
async extractExifInfo(job: Job<IAssetUploadedJob>) {
try {
const { asset, fileName }: { asset: AssetEntity; fileName: string } = job.data;
const exifData = await exiftool.read<ImmichTags>(asset.originalPath).catch((e) => {
@@ -190,22 +181,14 @@ export class MetadataExtractionProcessor {
});
if (newExif.livePhotoCID && !asset.livePhotoVideoId) {
const motionAsset = await this.assetRepository.findOne({
where: {
id: Not(asset.id),
type: AssetType.VIDEO,
exifInfo: {
livePhotoCID: newExif.livePhotoCID,
},
},
relations: {
exifInfo: true,
},
});
const motionAsset = await this.assetRepository.findLivePhotoMatch(
newExif.livePhotoCID,
AssetType.VIDEO,
asset.id,
);
if (motionAsset) {
await this.assetRepository.update(asset.id, { livePhotoVideoId: motionAsset.id });
await this.assetRepository.update(motionAsset.id, { isVisible: false });
await this.assetRepository.save({ id: asset.id, livePhotoVideoId: motionAsset.id });
await this.assetRepository.save({ id: motionAsset.id, isVisible: false });
}
}
@@ -249,7 +232,7 @@ export class MetadataExtractionProcessor {
}
@Process({ name: JobName.REVERSE_GEOCODING })
async reverseGeocoding(job: Job<IReverseGeocodingProcessor>) {
async reverseGeocoding(job: Job<IReverseGeocodingJob>) {
if (this.isGeocodeInitialized) {
const { latitude, longitude } = job.data;
const { country, state, city } = await this.reverseGeocodeExif(latitude, longitude);
@@ -258,7 +241,7 @@ export class MetadataExtractionProcessor {
}
@Process({ name: JobName.EXTRACT_VIDEO_METADATA, concurrency: 2 })
async extractVideoMetadata(job: Job<IVideoLengthExtractionProcessor>) {
async extractVideoMetadata(job: Job<IAssetUploadedJob>) {
const { asset, fileName } = job.data;
if (!asset.isVisible) {
@@ -309,20 +292,14 @@ export class MetadataExtractionProcessor {
newExif.livePhotoCID = exifData?.ContentIdentifier || null;
if (newExif.livePhotoCID) {
const photoAsset = await this.assetRepository.findOne({
where: {
id: Not(asset.id),
type: AssetType.IMAGE,
livePhotoVideoId: IsNull(),
exifInfo: {
livePhotoCID: newExif.livePhotoCID,
},
},
});
const photoAsset = await this.assetRepository.findLivePhotoMatch(
newExif.livePhotoCID,
AssetType.IMAGE,
asset.id,
);
if (photoAsset) {
await this.assetRepository.update(photoAsset.id, { livePhotoVideoId: asset.id });
await this.assetRepository.update(asset.id, { isVisible: false });
await this.assetRepository.save({ id: photoAsset.id, livePhotoVideoId: asset.id });
await this.assetRepository.save({ id: asset.id, isVisible: false });
}
}
@@ -378,7 +355,7 @@ export class MetadataExtractionProcessor {
}
await this.exifRepository.upsert(newExif, { conflictPaths: ['assetId'] });
await this.assetRepository.update({ id: asset.id }, { duration: durationString, fileCreatedAt });
await this.assetRepository.save({ id: asset.id, duration: durationString, fileCreatedAt });
} catch (err) {
``;
// do nothing