diff --git a/e2e/src/api/specs/asset.e2e-spec.ts b/e2e/src/api/specs/asset.e2e-spec.ts index 100c2e8cdb..01129b3299 100644 --- a/e2e/src/api/specs/asset.e2e-spec.ts +++ b/e2e/src/api/specs/asset.e2e-spec.ts @@ -1141,7 +1141,7 @@ describe('/asset', () => { fNumber: 8, focalLength: 97, iso: 100, - lensModel: 'E PZ 18-105mm F4 G OSS', + lensModel: 'Sony E PZ 18-105mm F4 G OSS', fileSizeInByte: 25_001_984, dateTimeOriginal: '2016-09-27T10:51:44+00:00', orientation: '1', @@ -1163,7 +1163,7 @@ describe('/asset', () => { fNumber: 22, focalLength: 25, iso: 100, - lensModel: 'E 25mm F2', + lensModel: 'Zeiss Batis 25mm F2', fileSizeInByte: 49_512_448, dateTimeOriginal: '2016-01-08T14:08:01+00:00', orientation: '1', @@ -1234,7 +1234,7 @@ describe('/asset', () => { focalLength: 18.3, iso: 100, latitude: 36.613_24, - lensModel: 'GR LENS 18.3mm F2.8', + lensModel: '18.3mm F2.8', longitude: -121.897_85, make: 'RICOH IMAGING COMPANY, LTD.', model: 'RICOH GR III', diff --git a/server/src/services/metadata.service.spec.ts b/server/src/services/metadata.service.spec.ts index 06337cdd43..1ba57eeff0 100644 --- a/server/src/services/metadata.service.spec.ts +++ b/server/src/services/metadata.service.spec.ts @@ -1229,18 +1229,51 @@ describe(MetadataService.name, () => { }); it.each([ - { Make: '1', Model: '2', Device: { Manufacturer: '3', ModelName: '4' }, AndroidMake: '4', AndroidModel: '5' }, - { Device: { Manufacturer: '1', ModelName: '2' }, AndroidMake: '3', AndroidModel: '4' }, - { AndroidMake: '1', AndroidModel: '2' }, - ])('should read camera make and model correct place %s', async (metaData) => { + { + exif: { + Make: '1', + Model: '2', + Device: { Manufacturer: '3', ModelName: '4' }, + AndroidMake: '4', + AndroidModel: '5', + }, + expected: { make: '1', model: '2' }, + }, + { + exif: { Device: { Manufacturer: '1', ModelName: '2' }, AndroidMake: '3', AndroidModel: '4' }, + expected: { make: '1', model: '2' }, + }, + { exif: { AndroidMake: '1', AndroidModel: '2' }, expected: { make: '1', model: '2' } }, + ])('should read camera make and model $exif -> $expected', async ({ exif, expected }) => { mocks.asset.getByIds.mockResolvedValue([assetStub.image]); - mockReadTags(metaData); + mockReadTags(exif); + + await sut.handleMetadataExtraction({ id: assetStub.image.id }); + expect(mocks.asset.upsertExif).toHaveBeenCalledWith(expect.objectContaining(expected)); + }); + + it.each([ + { exif: {}, expected: null }, + { exif: { LensID: '1', LensSpec: '2', LensType: '3', LensModel: '4' }, expected: '1' }, + { exif: { LensSpec: '2', LensType: '3', LensModel: '4' }, expected: '3' }, + { exif: { LensSpec: '2', LensModel: '4' }, expected: '2' }, + { exif: { LensModel: '4' }, expected: '4' }, + { exif: { LensID: '----' }, expected: null }, + { exif: { LensID: 'Unknown (0 ff ff)' }, expected: null }, + { + exif: { LensID: 'Unknown (E1 40 19 36 2C 35 DF 0E) Tamron 10-24mm f/3.5-4.5 Di II VC HLD (B023) ?' }, + expected: null, + }, + { exif: { LensID: ' Unknown 6-30mm' }, expected: null }, + { exif: { LensID: '' }, expected: null }, + ])('should read camera lens information $exif -> $expected', async ({ exif, expected }) => { + mocks.asset.getByIds.mockResolvedValue([assetStub.image]); + mockReadTags(exif); await sut.handleMetadataExtraction({ id: assetStub.image.id }); expect(mocks.asset.upsertExif).toHaveBeenCalledWith( expect.objectContaining({ - make: '1', - model: '2', + lensModel: expected, }), ); }); diff --git a/server/src/services/metadata.service.ts b/server/src/services/metadata.service.ts index 72f7270844..273165b5ae 100644 --- a/server/src/services/metadata.service.ts +++ b/server/src/services/metadata.service.ts @@ -76,6 +76,19 @@ const validateRange = (value: number | undefined, min: number, max: number): Non return val; }; +const getLensModel = (exifTags: ImmichTags): string | null => { + const lensModel = String( + exifTags.LensID ?? exifTags.LensType ?? exifTags.LensSpec ?? exifTags.LensModel ?? '', + ).trim(); + if (lensModel === '----') { + return null; + } + if (lensModel.startsWith('Unknown')) { + return null; + } + return lensModel || null; +}; + type ImmichTagsWithFaces = ImmichTags & { RegionInfo: NonNullable }; type Dates = { @@ -228,7 +241,7 @@ export class MetadataService extends BaseService { fps: validate(Number.parseFloat(exifTags.VideoFrameRate!)), iso: validate(exifTags.ISO) as number, exposureTime: exifTags.ExposureTime ?? null, - lensModel: exifTags.LensModel ?? null, + lensModel: getLensModel(exifTags), fNumber: validate(exifTags.FNumber), focalLength: validate(exifTags.FocalLength),