Browse Source

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

Alex Tran 3 years ago
parent
commit
44fc358b5d

+ 6 - 1
server/src/api-v1/asset/asset.controller.ts

@@ -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);
+  }
 }

+ 10 - 0
server/src/api-v1/asset/asset.service.ts

@@ -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'],
+    });
+  }
 }

+ 2 - 3
server/src/api-v1/asset/entities/asset.entity.ts

@@ -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 {

+ 17 - 6
server/src/api-v1/asset/entities/exif.entity.ts

@@ -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;
 }

+ 2 - 1
server/src/modules/background-task/background-task.module.ts

@@ -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],

+ 26 - 3
server/src/modules/background-task/background-task.processor.ts

@@ -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) {

+ 3 - 1
server/src/modules/background-task/background-task.service.ts

@@ -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() },
     );

+ 1 - 1
server/src/modules/image-optimize/image-optimize.service.ts

@@ -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',
       {