diff --git a/server/package-lock.json b/server/package-lock.json index 6cc3572ec4f9d..1ca61d6ed7232 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -34,7 +34,7 @@ "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "cookie-parser": "^1.4.6", - "exiftool-vendored": "~26.2.0", + "exiftool-vendored": "~27.0.0", "fast-glob": "^3.3.2", "fluent-ffmpeg": "^2.1.2", "geo-tz": "^8.0.0", @@ -9127,9 +9127,10 @@ } }, "node_modules/exiftool-vendored": { - "version": "26.2.0", - "resolved": "https://registry.npmjs.org/exiftool-vendored/-/exiftool-vendored-26.2.0.tgz", - "integrity": "sha512-7P6jQ944or7ic2SJzW+uaWK4TLDXlaCppHrBayl4MpIrVcEeQjiQTez4/oOH0wULIRu4j4H6Xruz4SLrDaafUg==", + "version": "27.0.0", + "resolved": "https://registry.npmjs.org/exiftool-vendored/-/exiftool-vendored-27.0.0.tgz", + "integrity": "sha512-/jHX8Jjadj0YJzpqnuBo1Yy2ln2hnRbBIc+3jcVOLQ6qhHEKsLRlfJ145Ghn7k/EcnfpDzVX3V8AUCTC8juTow==", + "license": "MIT", "dependencies": { "@photostructure/tz-lookup": "^10.0.0", "@types/luxon": "^3.4.2", @@ -22600,9 +22601,9 @@ } }, "exiftool-vendored": { - "version": "26.2.0", - "resolved": "https://registry.npmjs.org/exiftool-vendored/-/exiftool-vendored-26.2.0.tgz", - "integrity": "sha512-7P6jQ944or7ic2SJzW+uaWK4TLDXlaCppHrBayl4MpIrVcEeQjiQTez4/oOH0wULIRu4j4H6Xruz4SLrDaafUg==", + "version": "27.0.0", + "resolved": "https://registry.npmjs.org/exiftool-vendored/-/exiftool-vendored-27.0.0.tgz", + "integrity": "sha512-/jHX8Jjadj0YJzpqnuBo1Yy2ln2hnRbBIc+3jcVOLQ6qhHEKsLRlfJ145Ghn7k/EcnfpDzVX3V8AUCTC8juTow==", "requires": { "@photostructure/tz-lookup": "^10.0.0", "@types/luxon": "^3.4.2", diff --git a/server/package.json b/server/package.json index 6625555bb0daa..1633302ea3e7b 100644 --- a/server/package.json +++ b/server/package.json @@ -60,7 +60,7 @@ "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "cookie-parser": "^1.4.6", - "exiftool-vendored": "~26.2.0", + "exiftool-vendored": "~27.0.0", "fast-glob": "^3.3.2", "fluent-ffmpeg": "^2.1.2", "geo-tz": "^8.0.0", diff --git a/server/src/repositories/metadata.repository.ts b/server/src/repositories/metadata.repository.ts index 0b32233f6a8f8..a7c6bd13bb8fd 100644 --- a/server/src/repositories/metadata.repository.ts +++ b/server/src/repositories/metadata.repository.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { InjectDataSource, InjectRepository } from '@nestjs/typeorm'; -import { DefaultReadTaskOptions, Tags, exiftool } from 'exiftool-vendored'; +import { DefaultReadTaskOptions, ExifTool, Tags } from 'exiftool-vendored'; import geotz from 'geo-tz'; import { DummyValue, GenerateSql } from 'src/decorators'; import { ExifEntity } from 'src/entities/exif.entity'; @@ -20,40 +20,39 @@ export class MetadataRepository implements IMetadataRepository { @Inject(ILoggerRepository) private logger: ILoggerRepository, ) { this.logger.setContext(MetadataRepository.name); + this.exiftool = new ExifTool({ + defaultVideosToUTC: true, + backfillTimezones: true, + inferTimezoneFromDatestamps: true, + useMWG: true, + numericTags: [...DefaultReadTaskOptions.numericTags, 'FocalLength'], + /* eslint unicorn/no-array-callback-reference: off, unicorn/no-array-method-this-argument: off */ + geoTz: (lat, lon) => geotz.find(lat, lon)[0], + // Enable exiftool LFS to parse metadata for files larger than 2GB. + readArgs: ['-api', 'largefilesupport=1'], + writeArgs: ['-api', 'largefilesupport=1', '-overwrite_original'], + }); } + private exiftool: ExifTool; async teardown() { - await exiftool.end(); + await this.exiftool.end(); } readTags(path: string): Promise { - return exiftool - .read(path, undefined, { - ...DefaultReadTaskOptions, - - // Enable exiftool LFS to parse metadata for files larger than 2GB. - optionalArgs: ['-api', 'largefilesupport=1'], - defaultVideosToUTC: true, - backfillTimezones: true, - inferTimezoneFromDatestamps: true, - useMWG: true, - numericTags: [...DefaultReadTaskOptions.numericTags, 'FocalLength'], - /* eslint unicorn/no-array-callback-reference: off, unicorn/no-array-method-this-argument: off */ - geoTz: (lat, lon) => geotz.find(lat, lon)[0], - }) - .catch((error) => { - this.logger.warn(`Error reading exif data (${path}): ${error}`, error?.stack); - return null; - }) as Promise; + return this.exiftool.read(path).catch((error) => { + this.logger.warn(`Error reading exif data (${path}): ${error}`, error?.stack); + return null; + }) as Promise; } extractBinaryTag(path: string, tagName: string): Promise { - return exiftool.extractBinaryTagToBuffer(tagName, path); + return this.exiftool.extractBinaryTagToBuffer(tagName, path); } async writeTags(path: string, tags: Partial): Promise { try { - await exiftool.write(path, tags, ['-overwrite_original']); + await this.exiftool.write(path, tags); } catch (error) { this.logger.warn(`Error writing exif data (${path}): ${error}`); }