feat(server): read additional lens exif tags (#17125)

* fix(server): read additional lens exif tags

* Update order of read tags

* Fix e2e test

* Fix e2e test

* Fix e2e test

* Fix e2e test

* Update test

* Filter unknown lens exif data

* Formatting fixes
This commit is contained in:
Snowknight26 2025-04-10 12:02:41 -05:00 committed by GitHub
parent e3995fb5f4
commit 7fcab4b251
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 57 additions and 11 deletions

View File

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

View File

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

View File

@ -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<ImmichTags['RegionInfo']> };
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),