1
0
mirror of https://github.com/immich-app/immich.git synced 2024-12-24 10:37:28 +02:00

Added schedule job to perform reverse geocoding if key is added after backing up assets (#305)

This commit is contained in:
Alex 2022-07-04 15:16:39 -05:00 committed by GitHub
parent e6d30d72fa
commit 357f7d1c31
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 93 additions and 9 deletions

View File

@ -3,11 +3,16 @@ import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AssetEntity } from '@app/database/entities/asset.entity';
import { ScheduleTasksService } from './schedule-tasks.service';
import { thumbnailGeneratorQueueName, videoConversionQueueName } from '@app/job/constants/queue-name.constant';
import {
metadataExtractionQueueName,
thumbnailGeneratorQueueName,
videoConversionQueueName,
} from '@app/job/constants/queue-name.constant';
import { ExifEntity } from '@app/database/entities/exif.entity';
@Module({
imports: [
TypeOrmModule.forFeature([AssetEntity]),
TypeOrmModule.forFeature([AssetEntity, ExifEntity]),
BullModule.registerQueue({
name: videoConversionQueueName,
defaultJobOptions: {
@ -24,6 +29,15 @@ import { thumbnailGeneratorQueueName, videoConversionQueueName } from '@app/job/
removeOnFail: false,
},
}),
BullModule.registerQueue({
name: metadataExtractionQueueName,
defaultJobOptions: {
attempts: 3,
removeOnComplete: true,
removeOnFail: false,
},
}),
],
providers: [ScheduleTasksService],
})

View File

@ -1,14 +1,23 @@
import { Injectable, Logger } from '@nestjs/common';
import { Cron, CronExpression } from '@nestjs/schedule';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { IsNull, Not, Repository } from 'typeorm';
import { AssetEntity, AssetType } from '@app/database/entities/asset.entity';
import { InjectQueue } from '@nestjs/bull';
import { Queue } from 'bull';
import { randomUUID } from 'crypto';
import { generateWEBPThumbnailProcessorName, mp4ConversionProcessorName } from '@app/job/constants/job-name.constant';
import { thumbnailGeneratorQueueName, videoConversionQueueName } from '@app/job/constants/queue-name.constant';
import { IVideoTranscodeJob } from '@app/job/interfaces/video-transcode.interface';
import { ExifEntity } from '@app/database/entities/exif.entity';
import {
IMetadataExtractionJob,
IVideoTranscodeJob,
metadataExtractionQueueName,
thumbnailGeneratorQueueName,
videoConversionQueueName,
generateWEBPThumbnailProcessorName,
mp4ConversionProcessorName,
reverseGeocodingProcessorName,
} from '@app/job';
import { ConfigService } from '@nestjs/config';
@Injectable()
export class ScheduleTasksService {
@ -16,17 +25,23 @@ export class ScheduleTasksService {
@InjectRepository(AssetEntity)
private assetRepository: Repository<AssetEntity>,
@InjectRepository(ExifEntity)
private exifRepository: Repository<ExifEntity>,
@InjectQueue(thumbnailGeneratorQueueName)
private thumbnailGeneratorQueue: Queue,
@InjectQueue(videoConversionQueueName)
private videoConversionQueue: Queue<IVideoTranscodeJob>,
@InjectQueue(metadataExtractionQueueName)
private metadataExtractionQueue: Queue<IMetadataExtractionJob>,
private configService: ConfigService,
) {}
@Cron(CronExpression.EVERY_DAY_AT_MIDNIGHT)
async webpConversion() {
Logger.log('Starting Schedule Webp Conversion Tasks', 'CronjobWebpGenerator');
const assets = await this.assetRepository.find({
where: {
webpPath: '',
@ -64,4 +79,23 @@ export class ScheduleTasksService {
await this.videoConversionQueue.add(mp4ConversionProcessorName, { asset }, { jobId: randomUUID() });
}
}
@Cron(CronExpression.EVERY_5_SECONDS)
async reverseGeocoding() {
const isMapboxEnable = this.configService.get('ENABLE_MAPBOX');
if (isMapboxEnable) {
const exifInfo = await this.exifRepository.find({
where: {
city: IsNull(),
longitude: Not(IsNull()),
latitude: Not(IsNull()),
},
});
for (const exif of exifInfo) {
await this.metadataExtractionQueue.add(reverseGeocodingProcessorName, { exif }, { jobId: randomUUID() });
}
}
}
}

View File

@ -21,6 +21,8 @@ import {
objectDetectionProcessorName,
videoMetadataExtractionProcessorName,
metadataExtractionQueueName,
reverseGeocodingProcessorName,
IReverseGeocodingProcessor,
} from '@app/job';
@Processor(metadataExtractionQueueName)
@ -98,6 +100,28 @@ export class MetadataExtractionProcessor {
}
}
@Process({ name: reverseGeocodingProcessorName })
async reverseGeocoding(job: Job<IReverseGeocodingProcessor>) {
const { exif } = job.data;
if (this.geocodingClient) {
const geoCodeInfo: MapiResponse = await this.geocodingClient
.reverseGeocode({
query: [Number(exif.longitude), Number(exif.latitude)],
types: ['country', 'region', 'place'],
})
.send();
const res: [] = geoCodeInfo.body['features'];
const city = res.filter((geoInfo) => geoInfo['place_type'][0] == 'place')[0]['text'];
const state = res.filter((geoInfo) => geoInfo['place_type'][0] == 'region')[0]['text'];
const country = res.filter((geoInfo) => geoInfo['place_type'][0] == 'country')[0]['text'];
await this.exifRepository.update({ id: exif.id }, { city, state, country });
}
}
@Process({ name: imageTaggingProcessorName, concurrency: 2 })
async tagImage(job: Job) {
const { asset }: { asset: AssetEntity } = job.data;

View File

@ -19,5 +19,6 @@ export const generateWEBPThumbnailProcessorName = 'generate-webp-thumbnail';
*/
export const exifExtractionProcessorName = 'exif-extraction';
export const videoMetadataExtractionProcessorName = 'extract-video-metadata';
export const reverseGeocodingProcessorName = 'reverse-geocoding';
export const objectDetectionProcessorName = 'detect-object';
export const imageTaggingProcessorName = 'tag-image';

View File

@ -1,4 +1,5 @@
import { AssetEntity } from '@app/database/entities/asset.entity';
import { ExifEntity } from '@app/database/entities/exif.entity';
export interface IExifExtractionProcessor {
/**
@ -24,4 +25,14 @@ export interface IVideoLengthExtractionProcessor {
asset: AssetEntity;
}
export type IMetadataExtractionJob = IExifExtractionProcessor | IVideoLengthExtractionProcessor;
export interface IReverseGeocodingProcessor {
/**
* The Asset entity that was saved in the database
*/
exif: ExifEntity;
}
export type IMetadataExtractionJob =
| IExifExtractionProcessor
| IVideoLengthExtractionProcessor
| IReverseGeocodingProcessor;