Create database and model for EXIF

This commit is contained in:
Alex Tran 2022-02-09 22:59:32 -06:00
parent 03b67a98d3
commit ea9766a60b
9 changed files with 175 additions and 7 deletions

View file

@ -30,6 +30,7 @@ import { promisify } from 'util';
import { stat } from 'fs';
import { pipeline } from 'stream';
import { GetNewAssetQueryDto } from './dto/get-new-asset-query.dto';
import { BackgroundTaskService } from '../../modules/background-task/background-task.service';
const fileInfo = promisify(stat);
@ -37,8 +38,9 @@ const fileInfo = promisify(stat);
@Controller('asset')
export class AssetController {
constructor(
private readonly assetService: AssetService,
private readonly assetOptimizeService: AssetOptimizeService,
private assetService: AssetService,
private assetOptimizeService: AssetOptimizeService,
private backgroundTaskService: BackgroundTaskService,
) {}
@Post('upload')
@ -53,6 +55,7 @@ export class AssetController {
if (savedAsset && savedAsset.type == AssetType.IMAGE) {
await this.assetOptimizeService.resizeImage(savedAsset);
await this.backgroundTaskService.extractExif(savedAsset);
}
if (savedAsset && savedAsset.type == AssetType.VIDEO) {

View file

@ -6,6 +6,8 @@ import { AssetEntity } from './entities/asset.entity';
import { ImageOptimizeModule } from '../../modules/image-optimize/image-optimize.module';
import { AssetOptimizeService } from '../../modules/image-optimize/image-optimize.service';
import { BullModule } from '@nestjs/bull';
import { BackgroundTaskModule } from '../../modules/background-task/background-task.module';
import { BackgroundTaskService } from '../../modules/background-task/background-task.service';
@Module({
imports: [
@ -17,11 +19,20 @@ import { BullModule } from '@nestjs/bull';
removeOnFail: false,
},
}),
BullModule.registerQueue({
name: 'background-task',
defaultJobOptions: {
attempts: 3,
removeOnComplete: true,
removeOnFail: false,
},
}),
TypeOrmModule.forFeature([AssetEntity]),
ImageOptimizeModule,
BackgroundTaskModule,
],
controllers: [AssetController],
providers: [AssetService, AssetOptimizeService],
providers: [AssetService, AssetOptimizeService, BackgroundTaskService],
exports: [],
})
export class AssetModule {}

View file

@ -0,0 +1,48 @@
import { IsNotEmpty, IsOptional } from 'class-validator';
export class CreateExifDto {
@IsNotEmpty()
assetId: string;
@IsOptional()
make: string;
@IsOptional()
model: string;
@IsOptional()
imageName: string;
@IsOptional()
exifImageWidth: number;
@IsOptional()
exifImageHeight: number;
@IsOptional()
fileSizeInByte: number;
@IsOptional()
orientation: string;
@IsOptional()
dateTimeOriginal: Date;
@IsOptional()
modifiedDate: Date;
@IsOptional()
lensModel: string;
@IsOptional()
fNumber: number;
@IsOptional()
focalLenght: number;
@IsOptional()
iso: number;
@IsOptional()
exposureTime: number;
}

View file

@ -0,0 +1,4 @@
import { PartialType } from '@nestjs/mapped-types';
import { CreateExifDto } from './create-exif.dto';
export class UpdateExifDto extends PartialType(CreateExifDto) {}

View file

@ -1,4 +1,5 @@
import { Column, Entity, PrimaryColumn, PrimaryGeneratedColumn, Unique } from 'typeorm';
import { Column, Entity, JoinColumn, OneToOne, PrimaryColumn, PrimaryGeneratedColumn, Unique } from 'typeorm';
import { ExifEntity } from './exif.entity';
@Entity('assets')
@Unique(['deviceAssetId', 'userId', 'deviceId'])
@ -38,6 +39,10 @@ export class AssetEntity {
@Column({ nullable: true })
duration: string;
@OneToOne(() => ExifEntity, { cascade: true })
@JoinColumn({ name: 'assetId', referencedColumnName: 'id' })
exifData?: ExifEntity;
}
export enum AssetType {

View file

@ -0,0 +1,56 @@
import { Index } from 'typeorm';
import { Column } from 'typeorm/decorator/columns/Column';
import { PrimaryGeneratedColumn } from 'typeorm/decorator/columns/PrimaryGeneratedColumn';
import { Entity } from 'typeorm/decorator/entity/Entity';
@Entity('exif')
export class ExifEntity {
@PrimaryGeneratedColumn()
id: string;
@Index({ unique: true })
@Column({ type: 'uuid' })
assetId: string;
@Column({ nullable: true })
make: string;
@Column({ nullable: true })
model: string;
@Column({ nullable: true })
imageName: string;
@Column({ nullable: true })
exifImageWidth: number;
@Column({ nullable: true })
exifImageHeight: number;
@Column({ nullable: true })
fileSizeInByte: number;
@Column({ nullable: true })
orientation: string;
@Column({ type: 'timestamptz', nullable: true })
dateTimeOriginal: Date;
@Column({ type: 'timestamptz', nullable: true })
modifiedDate: Date;
@Column({ nullable: true })
lensModel: string;
@Column({ nullable: true })
fNumber: number;
@Column({ nullable: true })
focalLenght: number;
@Column({ nullable: true })
iso: number;
@Column({ nullable: true })
exposureTime: number;
}

View file

@ -2,6 +2,7 @@ import { BullModule } from '@nestjs/bull';
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AssetEntity } from '../../api-v1/asset/entities/asset.entity';
import { BackgroundTaskProcessor } from './background-task.processor';
import { BackgroundTaskService } from './background-task.service';
@Module({
@ -16,6 +17,7 @@ import { BackgroundTaskService } from './background-task.service';
}),
TypeOrmModule.forFeature([AssetEntity]),
],
providers: [BackgroundTaskService],
providers: [BackgroundTaskService, BackgroundTaskProcessor],
exports: [BackgroundTaskService],
})
export class BackgroundTaskModule {}

View file

@ -4,13 +4,33 @@ import { Job, Queue } from 'bull';
import { Repository } from 'typeorm';
import { AssetEntity } from '../../api-v1/asset/entities/asset.entity';
import { ConfigService } from '@nestjs/config';
import exifr from 'exifr';
import { readFile } from 'fs/promises';
import { Logger } from '@nestjs/common';
@Processor('background-task')
export class ImageOptimizeProcessor {
export class BackgroundTaskProcessor {
constructor(
@InjectRepository(AssetEntity)
private assetRepository: Repository<AssetEntity>,
private configService: ConfigService,
) {}
@Process('extract-exif')
async extractExif(job: Job) {
const { savedAsset }: { savedAsset: AssetEntity } = job.data;
const fileBuffer = await readFile(savedAsset.originalPath);
const exifData = await exifr.parse(fileBuffer);
console.log('=====================');
console.log(exifData);
try {
} catch (e) {
Logger.error(`Error extracting EXIF ${e.toString()}`, 'extractExif');
}
}
}

View file

@ -1,4 +1,23 @@
import { InjectQueue } from '@nestjs/bull/dist/decorators';
import { Injectable } from '@nestjs/common';
import { Queue } from 'bull';
import { randomUUID } from 'node:crypto';
import { AssetEntity } from '../../api-v1/asset/entities/asset.entity';
@Injectable()
export class BackgroundTaskService {}
export class BackgroundTaskService {
constructor(
@InjectQueue('background-task')
private backgroundTaskQueue: Queue,
) {}
async extractExif(savedAsset: AssetEntity) {
const job = await this.backgroundTaskQueue.add(
'extract-exif',
{
savedAsset,
},
{ jobId: randomUUID() },
);
}
}