From fb8d9d8c40abaad7eaae93b655b5331761b510df Mon Sep 17 00:00:00 2001 From: Zack Pollard Date: Tue, 16 Jul 2024 20:55:51 +0100 Subject: [PATCH] fix: downgrade exiftool-vendored to fix motion photo extraction (#11145) * Revert "chore(server): update exiftool and migrate off deprecated method signatures (#10367)" This reverts commit 1b67ea2d * fix: downgrade exiftool-vendored to 26.0.0 * chore: change motionphoto filenames to be kebab-case * test: add pixel 6 pro motionphoto e2e test case * test: add pixel 8a motion photo * chore: update test-assets submodule pointer --- e2e/src/api/specs/asset.e2e-spec.ts | 14 ++++-- e2e/src/api/specs/search.e2e-spec.ts | 8 ++-- e2e/test-assets | 2 +- server/package-lock.json | 47 +++++++++---------- server/package.json | 2 +- .../src/repositories/metadata.repository.ts | 43 ++++++++--------- 6 files changed, 62 insertions(+), 54 deletions(-) diff --git a/e2e/src/api/specs/asset.e2e-spec.ts b/e2e/src/api/specs/asset.e2e-spec.ts index e1b0fbc6a3..d0cf0c492c 100644 --- a/e2e/src/api/specs/asset.e2e-spec.ts +++ b/e2e/src/api/specs/asset.e2e-spec.ts @@ -1170,17 +1170,25 @@ describe('/asset', () => { // into the test here. it.each([ { - filepath: 'formats/motionphoto/Samsung One UI 5.jpg', + filepath: 'formats/motionphoto/samsung-one-ui-5.jpg', checksum: 'fr14niqCq6N20HB8rJYEvpsUVtI=', }, { - filepath: 'formats/motionphoto/Samsung One UI 6.jpg', + filepath: 'formats/motionphoto/samsung-one-ui-6.jpg', checksum: 'lT9Uviw/FFJYCjfIxAGPTjzAmmw=', }, { - filepath: 'formats/motionphoto/Samsung One UI 6.heic', + filepath: 'formats/motionphoto/samsung-one-ui-6.heic', checksum: '/ejgzywvgvzvVhUYVfvkLzFBAF0=', }, + { + filepath: 'formats/motionphoto/pixel-6-pro.jpg', + checksum: 'bFhLGbdK058PSk4FTfrSnoKWykc=', + }, + { + filepath: 'formats/motionphoto/pixel-8a.jpg', + checksum: '7YdY+WF0h+CXHbiXpi0HiCMTTjs=', + }, ])(`should extract motionphoto video from $filepath`, async ({ filepath, checksum }) => { const response = await utils.createAsset(admin.accessToken, { assetData: { diff --git a/e2e/src/api/specs/search.e2e-spec.ts b/e2e/src/api/specs/search.e2e-spec.ts index b49dac642e..b1116d4d6e 100644 --- a/e2e/src/api/specs/search.e2e-spec.ts +++ b/e2e/src/api/specs/search.e2e-spec.ts @@ -49,9 +49,9 @@ describe('/search', () => { { filename: '/albums/nature/silver_fir.jpg' }, { filename: '/formats/heic/IMG_2682.heic' }, { filename: '/formats/jpg/el_torcal_rocks.jpg' }, - { filename: '/formats/motionphoto/Samsung One UI 6.jpg' }, - { filename: '/formats/motionphoto/Samsung One UI 6.heic' }, - { filename: '/formats/motionphoto/Samsung One UI 5.jpg' }, + { filename: '/formats/motionphoto/samsung-one-ui-6.jpg' }, + { filename: '/formats/motionphoto/samsung-one-ui-6.heic' }, + { filename: '/formats/motionphoto/samsung-one-ui-5.jpg' }, { filename: '/metadata/gps-position/thompson-springs.jpg', dto: { isArchived: true } }, @@ -315,7 +315,7 @@ describe('/search', () => { { should: 'should search by originalFilename with spaces', deferred: () => ({ - dto: { originalFileName: 'Samsung One', type: 'IMAGE' }, + dto: { originalFileName: 'samsung-one', type: 'IMAGE' }, assets: [assetOneJpg5, assetOneJpg6, assetOneHeic6], }), }, diff --git a/e2e/test-assets b/e2e/test-assets index 625ec3a5e9..898069e47f 160000 --- a/e2e/test-assets +++ b/e2e/test-assets @@ -1 +1 @@ -Subproject commit 625ec3a5e9aa9b087ad986e0c2e6a24edb4ea81e +Subproject commit 898069e47f8e3283bf3bbd40b58b56d8fd57dc65 diff --git a/server/package-lock.json b/server/package-lock.json index 9b8377bab0..dd5b642a56 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": "~27.0.0", + "exiftool-vendored": "26.0.0", "fast-glob": "^3.3.2", "fluent-ffmpeg": "^2.1.2", "geo-tz": "^8.0.0", @@ -9549,10 +9549,9 @@ } }, "node_modules/exiftool-vendored": { - "version": "27.0.0", - "resolved": "https://registry.npmjs.org/exiftool-vendored/-/exiftool-vendored-27.0.0.tgz", - "integrity": "sha512-/jHX8Jjadj0YJzpqnuBo1Yy2ln2hnRbBIc+3jcVOLQ6qhHEKsLRlfJ145Ghn7k/EcnfpDzVX3V8AUCTC8juTow==", - "license": "MIT", + "version": "26.0.0", + "resolved": "https://registry.npmjs.org/exiftool-vendored/-/exiftool-vendored-26.0.0.tgz", + "integrity": "sha512-2TRxx21ovD95VvdSzHb/sTYYcwhiizQIhhVAbrgua9KoL902QRieREGvaUtfBZNjsptdjonuyku2kUBJCPqsgw==", "dependencies": { "@photostructure/tz-lookup": "^10.0.0", "@types/luxon": "^3.4.2", @@ -9561,23 +9560,23 @@ "luxon": "^3.4.4" }, "optionalDependencies": { - "exiftool-vendored.exe": "12.85.0", - "exiftool-vendored.pl": "12.85.0" + "exiftool-vendored.exe": "12.84.0", + "exiftool-vendored.pl": "12.84.0" } }, "node_modules/exiftool-vendored.exe": { - "version": "12.85.0", - "resolved": "https://registry.npmjs.org/exiftool-vendored.exe/-/exiftool-vendored.exe-12.85.0.tgz", - "integrity": "sha512-rWsKVp9oXsS79S3bfCNXKeEo4av0xcd7slk/TfPpCa5pojg8ZVXSVfPZMAAlhOuK63YXrKN/e3jRNReeGP+2Gw==", + "version": "12.84.0", + "resolved": "https://registry.npmjs.org/exiftool-vendored.exe/-/exiftool-vendored.exe-12.84.0.tgz", + "integrity": "sha512-9ocqJb0Pr9k0TownEMd75payF/XOQLF/swr/l0Ep49D+m609uIZsW09CtowhXmk1KrIFobS3+SkdXK04CSyUwQ==", "optional": true, "os": [ "win32" ] }, "node_modules/exiftool-vendored.pl": { - "version": "12.85.0", - "resolved": "https://registry.npmjs.org/exiftool-vendored.pl/-/exiftool-vendored.pl-12.85.0.tgz", - "integrity": "sha512-AelZQCCfl0a0g7PYx90TqbNGlSu2zDbRfCTjGw6bBBYnJF0NUfUWVhTpa8XGe2lHx1KYikH8AkJaey3esAxMAg==", + "version": "12.84.0", + "resolved": "https://registry.npmjs.org/exiftool-vendored.pl/-/exiftool-vendored.pl-12.84.0.tgz", + "integrity": "sha512-TxvMRaVYtd24Vupn48zy24LOYItIIWEu4dgt/VlqLwxQItTpvJTV9YH04iZRvaNh9ZdPRgVKWMuuUDBBHv+lAg==", "optional": true, "os": [ "!win32" @@ -23434,29 +23433,29 @@ } }, "exiftool-vendored": { - "version": "27.0.0", - "resolved": "https://registry.npmjs.org/exiftool-vendored/-/exiftool-vendored-27.0.0.tgz", - "integrity": "sha512-/jHX8Jjadj0YJzpqnuBo1Yy2ln2hnRbBIc+3jcVOLQ6qhHEKsLRlfJ145Ghn7k/EcnfpDzVX3V8AUCTC8juTow==", + "version": "26.0.0", + "resolved": "https://registry.npmjs.org/exiftool-vendored/-/exiftool-vendored-26.0.0.tgz", + "integrity": "sha512-2TRxx21ovD95VvdSzHb/sTYYcwhiizQIhhVAbrgua9KoL902QRieREGvaUtfBZNjsptdjonuyku2kUBJCPqsgw==", "requires": { "@photostructure/tz-lookup": "^10.0.0", "@types/luxon": "^3.4.2", "batch-cluster": "^13.0.0", - "exiftool-vendored.exe": "12.85.0", - "exiftool-vendored.pl": "12.85.0", + "exiftool-vendored.exe": "12.84.0", + "exiftool-vendored.pl": "12.84.0", "he": "^1.2.0", "luxon": "^3.4.4" } }, "exiftool-vendored.exe": { - "version": "12.85.0", - "resolved": "https://registry.npmjs.org/exiftool-vendored.exe/-/exiftool-vendored.exe-12.85.0.tgz", - "integrity": "sha512-rWsKVp9oXsS79S3bfCNXKeEo4av0xcd7slk/TfPpCa5pojg8ZVXSVfPZMAAlhOuK63YXrKN/e3jRNReeGP+2Gw==", + "version": "12.84.0", + "resolved": "https://registry.npmjs.org/exiftool-vendored.exe/-/exiftool-vendored.exe-12.84.0.tgz", + "integrity": "sha512-9ocqJb0Pr9k0TownEMd75payF/XOQLF/swr/l0Ep49D+m609uIZsW09CtowhXmk1KrIFobS3+SkdXK04CSyUwQ==", "optional": true }, "exiftool-vendored.pl": { - "version": "12.85.0", - "resolved": "https://registry.npmjs.org/exiftool-vendored.pl/-/exiftool-vendored.pl-12.85.0.tgz", - "integrity": "sha512-AelZQCCfl0a0g7PYx90TqbNGlSu2zDbRfCTjGw6bBBYnJF0NUfUWVhTpa8XGe2lHx1KYikH8AkJaey3esAxMAg==", + "version": "12.84.0", + "resolved": "https://registry.npmjs.org/exiftool-vendored.pl/-/exiftool-vendored.pl-12.84.0.tgz", + "integrity": "sha512-TxvMRaVYtd24Vupn48zy24LOYItIIWEu4dgt/VlqLwxQItTpvJTV9YH04iZRvaNh9ZdPRgVKWMuuUDBBHv+lAg==", "optional": true }, "express": { diff --git a/server/package.json b/server/package.json index 86a4f7d089..2a41a84bea 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": "~27.0.0", + "exiftool-vendored": "26.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 dfb9f676c2..56b948208a 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 { InjectRepository } from '@nestjs/typeorm'; -import { DefaultReadTaskOptions, ExifTool, Tags } from 'exiftool-vendored'; +import { DefaultReadTaskOptions, Tags, exiftool } from 'exiftool-vendored'; import geotz from 'geo-tz'; import { DummyValue, GenerateSql } from 'src/decorators'; import { ExifEntity } from 'src/entities/exif.entity'; @@ -17,39 +17,40 @@ 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 this.exiftool.end(); + await exiftool.end(); } readTags(path: string): Promise { - return this.exiftool.read(path).catch((error) => { - this.logger.warn(`Error reading exif data (${path}): ${error}`, error?.stack); - return null; - }) as 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; } extractBinaryTag(path: string, tagName: string): Promise { - return this.exiftool.extractBinaryTagToBuffer(tagName, path); + return exiftool.extractBinaryTagToBuffer(tagName, path); } async writeTags(path: string, tags: Partial): Promise { try { - await this.exiftool.write(path, tags); + await exiftool.write(path, tags, ['-overwrite_original']); } catch (error) { this.logger.warn(`Error writing exif data (${path}): ${error}`); }