mirror of
				https://github.com/immich-app/immich.git
				synced 2025-11-03 19:29:32 -05:00 
			
		
		
		
	fix(server): exif duration with scale (#4541)
This commit is contained in:
		
							parent
							
								
									5a7ef02387
								
							
						
					
					
						commit
						29182cfc9a
					
				@ -409,6 +409,54 @@ describe(MetadataService.name, () => {
 | 
			
		||||
        localDateTime: new Date('1970-01-01'),
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should handle duration', async () => {
 | 
			
		||||
      assetMock.getByIds.mockResolvedValue([assetStub.image]);
 | 
			
		||||
      metadataMock.getExifTags.mockResolvedValue({ Duration: 6.21 });
 | 
			
		||||
 | 
			
		||||
      await sut.handleMetadataExtraction({ id: assetStub.image.id });
 | 
			
		||||
 | 
			
		||||
      expect(assetMock.getByIds).toHaveBeenCalledWith([assetStub.image.id]);
 | 
			
		||||
      expect(assetMock.upsertExif).toHaveBeenCalled();
 | 
			
		||||
      expect(assetMock.save).toHaveBeenCalledWith(
 | 
			
		||||
        expect.objectContaining({
 | 
			
		||||
          id: assetStub.image.id,
 | 
			
		||||
          duration: '00:00:06.210',
 | 
			
		||||
        }),
 | 
			
		||||
      );
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should handle duration as an object without Scale', async () => {
 | 
			
		||||
      assetMock.getByIds.mockResolvedValue([assetStub.image]);
 | 
			
		||||
      metadataMock.getExifTags.mockResolvedValue({ Duration: { Value: 6.2 } });
 | 
			
		||||
 | 
			
		||||
      await sut.handleMetadataExtraction({ id: assetStub.image.id });
 | 
			
		||||
 | 
			
		||||
      expect(assetMock.getByIds).toHaveBeenCalledWith([assetStub.image.id]);
 | 
			
		||||
      expect(assetMock.upsertExif).toHaveBeenCalled();
 | 
			
		||||
      expect(assetMock.save).toHaveBeenCalledWith(
 | 
			
		||||
        expect.objectContaining({
 | 
			
		||||
          id: assetStub.image.id,
 | 
			
		||||
          duration: '00:00:06.200',
 | 
			
		||||
        }),
 | 
			
		||||
      );
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should handle duration with scale', async () => {
 | 
			
		||||
      assetMock.getByIds.mockResolvedValue([assetStub.image]);
 | 
			
		||||
      metadataMock.getExifTags.mockResolvedValue({ Duration: { Scale: 1.11111111111111e-5, Value: 558720 } });
 | 
			
		||||
 | 
			
		||||
      await sut.handleMetadataExtraction({ id: assetStub.image.id });
 | 
			
		||||
 | 
			
		||||
      expect(assetMock.getByIds).toHaveBeenCalledWith([assetStub.image.id]);
 | 
			
		||||
      expect(assetMock.upsertExif).toHaveBeenCalled();
 | 
			
		||||
      expect(assetMock.save).toHaveBeenCalledWith(
 | 
			
		||||
        expect.objectContaining({
 | 
			
		||||
          id: assetStub.image.id,
 | 
			
		||||
          duration: '00:00:06.207',
 | 
			
		||||
        }),
 | 
			
		||||
      );
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  describe('handleQueueSidecar', () => {
 | 
			
		||||
 | 
			
		||||
@ -7,6 +7,7 @@ import { Duration } from 'luxon';
 | 
			
		||||
import { usePagination } from '../domain.util';
 | 
			
		||||
import { IBaseJob, IEntityJob, JOBS_ASSET_PAGINATION_SIZE, JobName, QueueName } from '../job';
 | 
			
		||||
import {
 | 
			
		||||
  ExifDuration,
 | 
			
		||||
  IAlbumRepository,
 | 
			
		||||
  IAssetRepository,
 | 
			
		||||
  ICryptoRepository,
 | 
			
		||||
@ -398,7 +399,11 @@ export class MetadataService {
 | 
			
		||||
    return bitsPerSample;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private getDuration(seconds?: number): string {
 | 
			
		||||
    return Duration.fromObject({ seconds }).toFormat('hh:mm:ss.SSS');
 | 
			
		||||
  private getDuration(seconds?: number | ExifDuration): string {
 | 
			
		||||
    let _seconds = seconds as number;
 | 
			
		||||
    if (typeof seconds === 'object') {
 | 
			
		||||
      _seconds = seconds.Value * (seconds?.Scale || 1);
 | 
			
		||||
    }
 | 
			
		||||
    return Duration.fromObject({ seconds: _seconds }).toFormat('hh:mm:ss.SSS');
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -14,7 +14,12 @@ export interface ReverseGeocodeResult {
 | 
			
		||||
  city: string | null;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface ImmichTags extends Omit<Tags, 'FocalLength'> {
 | 
			
		||||
export interface ExifDuration {
 | 
			
		||||
  Value: number;
 | 
			
		||||
  Scale?: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface ImmichTags extends Omit<Tags, 'FocalLength' | 'Duration'> {
 | 
			
		||||
  ContentIdentifier?: string;
 | 
			
		||||
  MotionPhoto?: number;
 | 
			
		||||
  MotionPhotoVersion?: number;
 | 
			
		||||
@ -22,6 +27,7 @@ export interface ImmichTags extends Omit<Tags, 'FocalLength'> {
 | 
			
		||||
  MediaGroupUUID?: string;
 | 
			
		||||
  ImagePixelDepth?: string;
 | 
			
		||||
  FocalLength?: number;
 | 
			
		||||
  Duration?: number | ExifDuration;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IMetadataRepository {
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user