Skip to content

Commit 766cafe

Browse files
committed
fix: use GPSLongitudeRef and GPSLatitudeRef EXIF fields
1 parent 303307e commit 766cafe

File tree

4 files changed

+64
-34
lines changed

4 files changed

+64
-34
lines changed

e2e/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
"eslint-config-prettier": "^10.1.8",
3737
"eslint-plugin-prettier": "^5.1.3",
3838
"eslint-plugin-unicorn": "^60.0.0",
39-
"exiftool-vendored": "^28.3.1",
39+
"exiftool-vendored": "^30.4.0",
4040
"globals": "^16.0.0",
4141
"jose": "^5.6.3",
4242
"luxon": "^3.4.4",

pnpm-lock.yaml

Lines changed: 25 additions & 28 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

server/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@
7070
"cookie": "^1.0.2",
7171
"cookie-parser": "^1.4.7",
7272
"cron": "4.3.0",
73-
"exiftool-vendored": "^28.8.0",
73+
"exiftool-vendored": "^30.4.0",
7474
"express": "^5.1.0",
7575
"fast-glob": "^3.3.2",
7676
"fluent-ffmpeg": "^2.1.2",

server/src/repositories/metadata.repository.ts

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { Injectable } from '@nestjs/common';
22
import { BinaryField, DefaultReadTaskOptions, ExifTool, Tags } from 'exiftool-vendored';
33
import geotz from 'geo-tz';
4+
import fs from 'node:fs/promises';
5+
import path from 'node:path';
46
import { LoggingRepository } from 'src/repositories/logging.repository';
57

68
interface ExifDuration {
@@ -71,6 +73,8 @@ export interface ImmichTags extends Omit<Tags, TagsWithWrongTypes> {
7173

7274
AndroidMake?: string;
7375
AndroidModel?: string;
76+
77+
MIMEEncoding?: string;
7478
}
7579

7680
@Injectable()
@@ -84,6 +88,7 @@ export class MetadataRepository {
8488
numericTags: [...DefaultReadTaskOptions.numericTags, 'FocalLength', 'FileSize'],
8589
/* eslint unicorn/no-array-callback-reference: off, unicorn/no-array-method-this-argument: off */
8690
geoTz: (lat, lon) => geotz.find(lat, lon)[0],
91+
geolocation: true,
8792
// Enable exiftool LFS to parse metadata for files larger than 2GB.
8893
readArgs: ['-api', 'largefilesupport=1'],
8994
writeArgs: ['-api', 'largefilesupport=1', '-overwrite_original'],
@@ -101,11 +106,39 @@ export class MetadataRepository {
101106
await this.exiftool.end();
102107
}
103108

104-
readTags(path: string): Promise<ImmichTags> {
105-
return this.exiftool.read(path).catch((error) => {
106-
this.logger.warn(`Error reading exif data (${path}): ${error}`, error?.stack);
109+
async readTags(filePath: string): Promise<ImmichTags> {
110+
try {
111+
const tags = (await this.exiftool.read(filePath)) as ImmichTags;
112+
113+
// exiftool 13.25+ may skip XMP payload parsing for UTF-16LE XMP
114+
// (https://github.com/exiftool/exiftool/issues/348)
115+
const needsUtf8Normalization = tags.FileType === 'XMP' && tags.MIMEEncoding === 'utf-16le';
116+
117+
if (!needsUtf8Normalization) {
118+
return tags;
119+
}
120+
121+
try {
122+
// Create a temporary UTF-8 copy
123+
const xmpContent = await fs.readFile(filePath, 'utf-16le');
124+
const tmpDir = await fs.mkdtemp('immich-metadata-');
125+
const tmpFile = path.join(tmpDir, path.basename(filePath));
126+
await fs.writeFile(tmpFile, xmpContent, 'utf8');
127+
128+
// Try parsing the converted XMP file using exiftool
129+
try {
130+
return (await this.exiftool.read(tmpFile)) as ImmichTags;
131+
} finally {
132+
await fs.rm(tmpDir, { recursive: true, force: true }).catch(() => {});
133+
}
134+
} catch (fallbackError) {
135+
this.logger.warn(`UTF-8 normalization failed (${filePath}): ${fallbackError}`);
136+
return tags;
137+
}
138+
} catch (error) {
139+
this.logger.warn(`Error reading exif data (${filePath}): ${error}`, (error as Error).stack);
107140
return {};
108-
}) as Promise<ImmichTags>;
141+
}
109142
}
110143

111144
extractBinaryTag(path: string, tagName: string): Promise<Buffer> {

0 commit comments

Comments
 (0)