Extract and store EXIF on uploaded, create endpoint to get asset with exif data

This commit is contained in:
Alex Tran 2022-02-10 00:11:48 -06:00
parent ea9766a60b
commit 44fc358b5d
8 changed files with 67 additions and 16 deletions

View file

@ -55,7 +55,7 @@ export class AssetController {
if (savedAsset && savedAsset.type == AssetType.IMAGE) {
await this.assetOptimizeService.resizeImage(savedAsset);
await this.backgroundTaskService.extractExif(savedAsset);
await this.backgroundTaskService.extractExif(savedAsset, file.originalname, file.size);
}
if (savedAsset && savedAsset.type == AssetType.VIDEO) {
@ -158,4 +158,9 @@ export class AssetController {
async getUserAssetsByDeviceId(@GetAuthUser() authUser: AuthUserDto, @Param('deviceId') deviceId: string) {
return await this.assetService.getUserAssetsByDeviceId(authUser, deviceId);
}
@Get('/assetById/:assetId')
async getAssetById(@GetAuthUser() authUser: AuthUserDto, @Param('assetId') assetId) {
return this.assetService.getAssetById(authUser, assetId);
}
}

View file

@ -112,4 +112,14 @@ export class AssetService {
},
});
}
public async getAssetById(authUser: AuthUserDto, assetId: string) {
return await this.assetRepository.findOne({
where: {
userId: authUser.id,
id: assetId,
},
relations: ['exifInfo'],
});
}
}

View file

@ -40,9 +40,8 @@ export class AssetEntity {
@Column({ nullable: true })
duration: string;
@OneToOne(() => ExifEntity, { cascade: true })
@JoinColumn({ name: 'assetId', referencedColumnName: 'id' })
exifData?: ExifEntity;
@OneToOne(() => ExifEntity, (exifEntity) => exifEntity.asset)
exifInfo: ExifEntity;
}
export enum AssetType {

View file

@ -1,7 +1,8 @@
import { Index } from 'typeorm';
import { Index, JoinColumn, OneToOne } from 'typeorm';
import { Column } from 'typeorm/decorator/columns/Column';
import { PrimaryGeneratedColumn } from 'typeorm/decorator/columns/PrimaryGeneratedColumn';
import { Entity } from 'typeorm/decorator/entity/Entity';
import { AssetEntity } from './asset.entity';
@Entity('exif')
export class ExifEntity {
@ -37,20 +38,30 @@ export class ExifEntity {
dateTimeOriginal: Date;
@Column({ type: 'timestamptz', nullable: true })
modifiedDate: Date;
modifyDate: Date;
@Column({ nullable: true })
lensModel: string;
@Column({ nullable: true })
@Column({ type: 'float8', nullable: true })
fNumber: number;
@Column({ nullable: true })
focalLenght: number;
@Column({ type: 'float8', nullable: true })
focalLength: number;
@Column({ nullable: true })
iso: number;
@Column({ nullable: true })
@Column({ type: 'float', nullable: true })
exposureTime: number;
@Column({ type: 'float', nullable: true })
latitude: number;
@Column({ type: 'float', nullable: true })
longitude: number;
@OneToOne(() => AssetEntity, { onDelete: 'CASCADE', nullable: true })
@JoinColumn({ name: 'assetId', referencedColumnName: 'id' })
asset: ExifEntity;
}

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 { ExifEntity } from '../../api-v1/asset/entities/exif.entity';
import { BackgroundTaskProcessor } from './background-task.processor';
import { BackgroundTaskService } from './background-task.service';
@ -15,7 +16,7 @@ import { BackgroundTaskService } from './background-task.service';
removeOnFail: false,
},
}),
TypeOrmModule.forFeature([AssetEntity]),
TypeOrmModule.forFeature([AssetEntity, ExifEntity]),
],
providers: [BackgroundTaskService, BackgroundTaskProcessor],
exports: [BackgroundTaskService],

View file

@ -7,6 +7,7 @@ import { ConfigService } from '@nestjs/config';
import exifr from 'exifr';
import { readFile } from 'fs/promises';
import { Logger } from '@nestjs/common';
import { ExifEntity } from '../../api-v1/asset/entities/exif.entity';
@Processor('background-task')
export class BackgroundTaskProcessor {
@ -14,19 +15,41 @@ export class BackgroundTaskProcessor {
@InjectRepository(AssetEntity)
private assetRepository: Repository<AssetEntity>,
@InjectRepository(ExifEntity)
private exifRepository: Repository<ExifEntity>,
private configService: ConfigService,
) {}
@Process('extract-exif')
async extractExif(job: Job) {
const { savedAsset }: { savedAsset: AssetEntity } = job.data;
const { savedAsset, fileName, fileSize }: { savedAsset: AssetEntity; fileName: string; fileSize: number } =
job.data;
const fileBuffer = await readFile(savedAsset.originalPath);
const exifData = await exifr.parse(fileBuffer);
console.log('=====================');
console.log(exifData);
const newExif = new ExifEntity();
newExif.assetId = savedAsset.id;
newExif.make = exifData['Make'] || null;
newExif.model = exifData['Model'] || null;
newExif.imageName = fileName || null;
newExif.exifImageHeight = exifData['ExifImageHeight'] || null;
newExif.exifImageWidth = exifData['ExifImageWidth'] || null;
newExif.fileSizeInByte = fileSize || null;
newExif.orientation = exifData['Orientation'] || null;
newExif.dateTimeOriginal = exifData['DateTimeOriginal'] || null;
newExif.modifyDate = exifData['ModifyDate'] || null;
newExif.lensModel = exifData['LensModel'] || null;
newExif.fNumber = exifData['FNumber'] || null;
newExif.focalLength = exifData['FocalLength'] || null;
newExif.iso = exifData['ISO'] || null;
newExif.exposureTime = exifData['ExposureTime'] || null;
newExif.latitude = exifData['latitude'] || null;
newExif.longitude = exifData['longitude'] || null;
await this.exifRepository.save(newExif);
try {
} catch (e) {

View file

@ -11,11 +11,13 @@ export class BackgroundTaskService {
private backgroundTaskQueue: Queue,
) {}
async extractExif(savedAsset: AssetEntity) {
async extractExif(savedAsset: AssetEntity, fileName: string, fileSize: number) {
const job = await this.backgroundTaskQueue.add(
'extract-exif',
{
savedAsset,
fileName,
fileSize,
},
{ jobId: randomUUID() },
);

View file

@ -22,7 +22,7 @@ export class AssetOptimizeService {
};
}
public async getVideoThumbnail(savedAsset: AssetEntity, filename: String) {
public async getVideoThumbnail(savedAsset: AssetEntity, filename: string) {
const job = await this.optimizeQueue.add(
'get-video-thumbnail',
{