From b2be0a976307cd478555a879b974d8064c59af7d Mon Sep 17 00:00:00 2001 From: grasdk <115414609+grasdk@users.noreply.github.com> Date: Sun, 11 Feb 2024 15:55:26 +0100 Subject: [PATCH] consolidate exif parsing libraries - rework of timestamps (#4) * exifr is used for most tags now * New timestamp handling, removed exif-parser, date supported for png * Removed offset from testhelper. It's optional * explanations * Feature/timestamps (#3) * preparing for further timestamp test * Added more test and fixed offset calculation bug * Revered old dimension test, added new timestamp tests, some bug fixes * Renamed png-test because faces overrule keywords --- package-lock.json | 1 - package.json | 1 - .../model/database/enitites/MediaEntity.ts | 4 + .../model/fileaccess/MetadataLoader.ts | 403 ++++++++++-------- src/common/entities/MediaDTO.ts | 1 + src/common/entities/PhotoDTO.ts | 1 + src/common/entities/VideoDTO.ts | 1 + test/backend/assets/Chars.json | 3 +- .../edge_case_exif_data/before_epoch.json | 3 +- .../edge_case_exif_data/date_error.json | 2 +- ...tes.json => png_with_faces_and_dates.json} | 3 +- ...dates.png => png_with_faces_and_dates.png} | Bin test/backend/assets/test_png.json | 3 +- test/backend/assets/timestamps/big_ben.jpg | Bin 0 -> 18532 bytes test/backend/assets/timestamps/big_ben.json | 25 ++ .../big_ben_no_tsoffset_but_gps_utc.jpg | Bin 0 -> 18663 bytes .../big_ben_no_tsoffset_but_gps_utc.json | 25 ++ .../assets/timestamps/big_ben_only_time.jpg | Bin 0 -> 17850 bytes .../assets/timestamps/big_ben_only_time.json | 20 + .../assets/timestamps/sydney_opera_house.jpg | Bin 0 -> 22755 bytes .../assets/timestamps/sydney_opera_house.json | 25 ++ ...ey_opera_house_no_tsoffset_but_gps_utc.jpg | Bin 0 -> 22653 bytes ...y_opera_house_no_tsoffset_but_gps_utc.json | 25 ++ test/backend/assets/two_ratings.json | 3 +- test/backend/assets/xmp/xmp_subject.json | 3 +- .../model/threading/MetaDataLoader.spec.ts | 32 +- 26 files changed, 408 insertions(+), 176 deletions(-) rename test/backend/assets/{png_with_keyword_and_dates.json => png_with_faces_and_dates.json} (85%) rename test/backend/assets/{png_with_keyword_and_dates.png => png_with_faces_and_dates.png} (100%) create mode 100644 test/backend/assets/timestamps/big_ben.jpg create mode 100644 test/backend/assets/timestamps/big_ben.json create mode 100644 test/backend/assets/timestamps/big_ben_no_tsoffset_but_gps_utc.jpg create mode 100644 test/backend/assets/timestamps/big_ben_no_tsoffset_but_gps_utc.json create mode 100644 test/backend/assets/timestamps/big_ben_only_time.jpg create mode 100644 test/backend/assets/timestamps/big_ben_only_time.json create mode 100644 test/backend/assets/timestamps/sydney_opera_house.jpg create mode 100644 test/backend/assets/timestamps/sydney_opera_house.json create mode 100644 test/backend/assets/timestamps/sydney_opera_house_no_tsoffset_but_gps_utc.jpg create mode 100644 test/backend/assets/timestamps/sydney_opera_house_no_tsoffset_but_gps_utc.json diff --git a/package-lock.json b/package-lock.json index db09d172..51b2286a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,7 +26,6 @@ "nodemailer": "6.9.4", "reflect-metadata": "0.1.13", "sharp": "0.31.3", - "ts-exif-parser": "0.2.2", "ts-node-iptc": "1.0.11", "typeconfig": "2.1.2", "typeorm": "0.3.12", diff --git a/package.json b/package.json index f3dac2e6..7219c2c0 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,6 @@ "nodemailer": "6.9.4", "reflect-metadata": "0.1.13", "sharp": "0.31.3", - "ts-exif-parser": "0.2.2", "ts-node-iptc": "1.0.11", "typeconfig": "2.1.2", "typeorm": "0.3.12", diff --git a/src/backend/model/database/enitites/MediaEntity.ts b/src/backend/model/database/enitites/MediaEntity.ts index 52bbf600..57c13132 100644 --- a/src/backend/model/database/enitites/MediaEntity.ts +++ b/src/backend/model/database/enitites/MediaEntity.ts @@ -105,6 +105,10 @@ export class MediaMetadataEntity implements MediaMetadata { }) @Index() creationDate: number; + + @Column('text') + creationDateOffset?: string; + @Column('int', {unsigned: true}) fileSize: number; diff --git a/src/backend/model/fileaccess/MetadataLoader.ts b/src/backend/model/fileaccess/MetadataLoader.ts index 8ded9e1d..3e10e1fa 100644 --- a/src/backend/model/fileaccess/MetadataLoader.ts +++ b/src/backend/model/fileaccess/MetadataLoader.ts @@ -12,7 +12,6 @@ import { FfprobeData } from 'fluent-ffmpeg'; import { FileHandle } from 'fs/promises'; import * as util from 'node:util'; import * as path from 'path'; -import { ExifParserFactory, OrientationTypes } from 'ts-exif-parser'; import { IptcParser } from 'ts-node-iptc'; import { Utils } from '../../../common/Utils'; import { FFmpegFactory } from '../FFmpegFactory'; @@ -135,7 +134,7 @@ export class MetadataLoader { fullPathWithoutExt + '.xmp', fullPathWithoutExt + '.XMP', ]; - + for (const sidecarPath of sidecarPaths) { if (fs.existsSync(sidecarPath)) { const sidecarData = await exifr.sidecar(sidecarPath); @@ -148,7 +147,8 @@ export class MetadataLoader { if (metadata.keywords.indexOf(kw) === -1) { metadata.keywords.push(kw); } - } } + } + } if ((sidecarData as SideCar).xmp.Rating !== undefined) { metadata.rating = (sidecarData as SideCar).xmp.Rating; } @@ -168,7 +168,7 @@ export class MetadataLoader { } private static readonly EMPTY_METADATA: PhotoMetadata = { - size: {width: 1, height: 1}, + size: { width: 0, height: 0 }, creationDate: 0, fileSize: 0, }; @@ -177,10 +177,55 @@ export class MetadataLoader { public static async loadPhotoMetadata(fullPath: string): Promise { let fileHandle: FileHandle; const metadata: PhotoMetadata = { - size: {width: 1, height: 1}, + size: { width: 0, height: 0 }, creationDate: 0, fileSize: 0, }; + const exifrOptions = { + tiff: true, + xmp: true, + icc: false, + jfif: false, //not needed and not supported for png + ihdr: true, + iptc: false, //exifr reads UTF8-encoded data wrongly, using IptcParser instead + exif: true, + gps: true, + reviveValues: false, //don't convert timestamps + translateValues: false, //don't translate orientation from numbers to strings etc. + mergeOutput: false //don't merge output, because things like Microsoft Rating (percent) and xmp.rating will be merged + }; + + //function to convert timestamp into milliseconds taking offset into account + const timestampToMS = (timestamp: string, offset: string) => { + "replace first two : with - in timestamp string and add offset if exists (else +00:00 - UTC), parse this into MS" + return Date.parse(timestamp.replace(':', '-').replace(':', '-') + (offset ? offset : '+00:00')); + } + + const getTimeOffsetByGPSStamp = (timestamp: string, gpsTimeStamp: string, gps: any) => { + let UTCTimestamp = gpsTimeStamp; + if (!UTCTimestamp && + gps && + gps.GPSDateStamp && + gps.GPSTimeStamp) { + //GPS timestamp is always UTC (+00:00) + UTCTimestamp = gps.GPSDateStamp.replaceAll(':', '-') + gps.GPSTimeStamp.join(':') + '+00:00'; + } + if (UTCTimestamp) { + //offset in minutes is the difference between gps timestamp and given timestamp + let offsetMinutes = (Date.parse(UTCTimestamp) - Date.parse(timestamp.replace(':', '-').replace(':', '-'))) / 1000 / 60; + if (-720 <= offsetMinutes && offsetMinutes <= 840) { + //valid offset is within -12 and +14 hrs (https://en.wikipedia.org/wiki/List_of_UTC_offsets) + return (offsetMinutes < 0 ? "-" : "+") + //leading +/- + ("0" + Math.trunc(Math.abs(offsetMinutes) / 60)).slice(-2) + ":" + //zeropadded hours and ':' + ("0" + Math.abs(offsetMinutes) % 60).slice(-2); //zeropadded minutes + } else { + return undefined; + } + } else { + return undefined; + } + } + try { const data = Buffer.allocUnsafe(Config.Media.photoMetadataSize); fileHandle = await fs.promises.open(fullPath, 'r'); @@ -193,7 +238,6 @@ export class MetadataLoader { } finally { await fileHandle.close(); } - try { try { const stat = fs.statSync(fullPath); @@ -202,118 +246,17 @@ export class MetadataLoader { } catch (err) { // ignoring errors } - try { - const exif = ExifParserFactory.create(data).parse(); - if ( - exif.tags.ISO || - exif.tags.Model || - exif.tags.Make || - exif.tags.FNumber || - exif.tags.ExposureTime || - exif.tags.FocalLength || - exif.tags.LensModel - ) { - if (exif.tags.Model && exif.tags.Model !== '') { - metadata.cameraData = metadata.cameraData || {}; - metadata.cameraData.model = '' + exif.tags.Model; - } - if (exif.tags.Make && exif.tags.Make !== '') { - metadata.cameraData = metadata.cameraData || {}; - metadata.cameraData.make = '' + exif.tags.Make; - } - if (exif.tags.LensModel && exif.tags.LensModel !== '') { - metadata.cameraData = metadata.cameraData || {}; - metadata.cameraData.lens = '' + exif.tags.LensModel; - } - if (Utils.isUInt32(exif.tags.ISO)) { - metadata.cameraData = metadata.cameraData || {}; - metadata.cameraData.ISO = parseInt('' + exif.tags.ISO, 10); - } - if (Utils.isFloat32(exif.tags.FocalLength)) { - metadata.cameraData = metadata.cameraData || {}; - metadata.cameraData.focalLength = parseFloat( - '' + exif.tags.FocalLength - ); - } - if (Utils.isFloat32(exif.tags.ExposureTime)) { - metadata.cameraData = metadata.cameraData || {}; - metadata.cameraData.exposure = parseFloat( - parseFloat('' + exif.tags.ExposureTime).toFixed(6) - ); - } - if (Utils.isFloat32(exif.tags.FNumber)) { - metadata.cameraData = metadata.cameraData || {}; - metadata.cameraData.fStop = parseFloat( - parseFloat('' + exif.tags.FNumber).toFixed(2) - ); - } - } - if ( - !isNaN(exif.tags.GPSLatitude) || - exif.tags.GPSLongitude || - exif.tags.GPSAltitude - ) { - metadata.positionData = metadata.positionData || {}; - metadata.positionData.GPSData = {}; - - if (Utils.isFloat32(exif.tags.GPSLongitude)) { - metadata.positionData.GPSData.longitude = parseFloat( - exif.tags.GPSLongitude.toFixed(6) - ); - } - if (Utils.isFloat32(exif.tags.GPSLatitude)) { - metadata.positionData.GPSData.latitude = parseFloat( - exif.tags.GPSLatitude.toFixed(6) - ); - } - } - if ( - exif.tags.CreateDate || - exif.tags.DateTimeOriginal || - exif.tags.ModifyDate - ) { - metadata.creationDate = - (exif.tags.DateTimeOriginal || - exif.tags.CreateDate || - exif.tags.ModifyDate) * 1000; - } - if (exif.imageSize) { - metadata.size = { - width: exif.imageSize.width, - height: exif.imageSize.height, - }; - } else if ( - exif.tags.RelatedImageWidth && - exif.tags.RelatedImageHeight - ) { - metadata.size = { - width: exif.tags.RelatedImageWidth, - height: exif.tags.RelatedImageHeight, - }; - } else if ( - exif.tags.ImageWidth && - exif.tags.ImageHeight - ) { - metadata.size = { - width: exif.tags.ImageWidth, - height: exif.tags.ImageHeight, - }; - } else { - const info = imageSize(fullPath); - metadata.size = {width: info.width, height: info.height}; - } - } catch (err) { - Logger.debug(LOG_TAG, 'Error parsing exif', fullPath, err); - try { - const info = imageSize(fullPath); - metadata.size = {width: info.width, height: info.height}; - } catch (e) { - metadata.size = {width: 1, height: 1}; - } + //read the actual image size, don't rely on tags for this + const info = imageSize(fullPath); + metadata.size = { width: info.width, height: info.height }; + } catch (e) { + //in case of failure, set dimensions to 0 so they may be read via tags + metadata.size = { width: 0, height: 0 }; } - try { + + try { //Parse iptc data using the IptcParser, which works correctly for both UTF-8 and ASCII const iptcData = IptcParser.parse(data); if (iptcData.country_or_primary_location_name) { metadata.positionData = metadata.positionData || {}; @@ -351,61 +294,187 @@ export class MetadataLoader { // Logger.debug(LOG_TAG, 'Error parsing iptc data', fullPath, err); } - if (!metadata.creationDate) { - // creationDate can be negative, when it was created before epoch (1970) - metadata.creationDate = 0; - } - try { - const exifrOptions = { - tiff: true, - xmp: true, - icc: false, - jfif: false, //not needed and not supported for png - ihdr: true, - iptc: false, //exifr reads UTF8-encoded data wrongly - exif: true, - gps: true, - translateValues: false, //don't translate orientation from numbers to strings etc. - mergeOutput: false //don't merge output, because things like Microsoft Rating (percent) and xmp.rating will be merged - }; - - const exif = await exifr.parse(data, exifrOptions); - if (exif.xmp && exif.xmp.Rating) { - metadata.rating = exif.xmp.Rating; - if (metadata.rating < 0) { - metadata.rating = 0; - } - } + let orientation = 1; //Orientation 1 is normal + const exif = await exifr.parse(data, exifrOptions); + //exif is structured in sections, we read the data by section + + //dc-section (subject is the only tag we want from dc) if (exif.dc && - exif.dc.subject && - exif.dc.subject.length > 0) { + exif.dc.subject && + exif.dc.subject.length > 0) { const subj = Array.isArray(exif.dc.subject) ? exif.dc.subject : [exif.dc.subject]; if (metadata.keywords === undefined) { - metadata.keywords = []; + metadata.keywords = []; } for (const kw of subj) { - if (metadata.keywords.indexOf(kw) === -1) { - metadata.keywords.push(kw); - } + if (metadata.keywords.indexOf(kw) === -1) { + metadata.keywords.push(kw); + } } - } - let orientation = OrientationTypes.TOP_LEFT; - if (exif.ifd0 && - exif.ifd0.Orientation) { - orientation = parseInt( - exif.ifd0.Orientation as any, - 10 - ) as number; } - if (OrientationTypes.BOTTOM_LEFT < orientation) { + + //ifd0 section + if (exif.ifd0) { + if (exif.ifd0.ImageWidth && metadata.size.width <= 0) { + metadata.size.width = exif.ifd0.ImageWidth; + } + if (exif.ifd0.ImageHeight && metadata.size.height <= 0) { + metadata.size.height = exif.ifd0.ImageHeight; + } + if (exif.ifd0.Orientation) { + orientation = parseInt( + exif.ifd0.Orientation as any, + 10 + ) as number; + } + if (exif.ifd0.Make && exif.ifd0.Make !== '') { + metadata.cameraData = metadata.cameraData || {}; + metadata.cameraData.make = '' + exif.ifd0.Make; + } + if (exif.ifd0.Model && exif.ifd0.Model !== '') { + metadata.cameraData = metadata.cameraData || {}; + metadata.cameraData.model = '' + exif.ifd0.Model; + } + //if (exif.ifd0.ModifyDate) {} //Deferred to the exif-section where the other timestamps are + } + + //exif section + if (exif.exif) { + if (exif.exif.DateTimeOriginal) { + //DateTimeOriginal is when the camera shutter closed + if (exif.exif.OffsetTimeOriginal) { //OffsetTimeOriginal is the corresponding offset + metadata.creationDate = timestampToMS(exif.exif.DateTimeOriginal, exif.exif.OffsetTimeOriginal); + metadata.creationDateOffset = exif.exif.OffsetTimeOriginal; + } else { + let alt_offset = exif.exif.OffsetTimeDigitized || exif.exif.OffsetTime || getTimeOffsetByGPSStamp(exif.exif.DateTimeOriginal, exif.exif.GPSTimeStamp, exif.gps); + metadata.creationDate = timestampToMS(exif.exif.DateTimeOriginal, alt_offset); + metadata.creationDateOffset = alt_offset; + } + } else if (exif.exif.CreateDate) { //using else if here, because DateTimeOriginal has preceedence + //Create is when the camera wrote the file (typically within the same ms as shutter close) + if (exif.exif.OffsetTimeDigitized) { //OffsetTimeDigitized is the corresponding offset + metadata.creationDate = timestampToMS(exif.exif.CreateDate, exif.exif.OffsetTimeDigitized); + metadata.creationDateOffset = exif.exif.OffsetTimeDigitized; + } else { + let alt_offset = exif.exif.OffsetTimeOriginal || exif.exif.OffsetTime || getTimeOffsetByGPSStamp(exif.exif.DateTimeOriginal, exif.exif.GPSTimeStamp, exif.gps); + metadata.creationDate = timestampToMS(exif.exif.DateTimeOriginal, alt_offset); + metadata.creationDateOffset = alt_offset; + } + } else if (exif.ifd0?.ModifyDate) { //using else if here, because DateTimeOriginal and CreatDate have preceedence + if (exif.exif.OffsetTime) { + //exif.Offsettime is the offset corresponding to ifd0.ModifyDate + metadata.creationDate = timestampToMS(exif.ifd0.ModifyDate, exif.exif?.OffsetTime); + metadata.creationDateOffset = exif.exif?.OffsetTime + } else { + let alt_offset = exif.exif.DateTimeOriginal || exif.exif.OffsetTimeDigitized || getTimeOffsetByGPSStamp(exif.ifd0.ModifyDate, exif.exif.GPSTimeStamp, exif.gps); + metadata.creationDate = timestampToMS(exif.ifd0.ModifyDate, alt_offset); + metadata.creationDateOffset = alt_offset; + } + } + if (exif.exif.LensModel && exif.exif.LensModel !== '') { + metadata.cameraData = metadata.cameraData || {}; + metadata.cameraData.lens = '' + exif.exif.LensModel; + } + if (Utils.isUInt32(exif.exif.ISO)) { + metadata.cameraData = metadata.cameraData || {}; + metadata.cameraData.ISO = parseInt('' + exif.exif.ISO, 10); + } + if (Utils.isFloat32(exif.exif.FocalLength)) { + metadata.cameraData = metadata.cameraData || {}; + metadata.cameraData.focalLength = parseFloat( + '' + exif.exif.FocalLength + ); + } + if (Utils.isFloat32(exif.exif.ExposureTime)) { + metadata.cameraData = metadata.cameraData || {}; + metadata.cameraData.exposure = parseFloat( + parseFloat('' + exif.exif.ExposureTime).toFixed(6) + ); + } + if (Utils.isFloat32(exif.exif.FNumber)) { + metadata.cameraData = metadata.cameraData || {}; + metadata.cameraData.fStop = parseFloat( + parseFloat('' + exif.exif.FNumber).toFixed(2) + ); + } + if (exif.exif.ExifImageWidth && metadata.size.width <= 0) { + metadata.size.width = exif.exif.ExifImageWidth; + } + if (exif.exif.ExifImageHeight && metadata.size.height <= 0) { + metadata.size.height = exif.exif.ExifImageHeight; + } + } + + //gps section + if (exif.gps) { + metadata.positionData = metadata.positionData || {}; + metadata.positionData.GPSData = metadata.positionData.GPSData || {}; + + if (Utils.isFloat32(exif.gps.longitude)) { + metadata.positionData.GPSData.longitude = parseFloat( + exif.gps.longitude.toFixed(6) + ); + } + if (Utils.isFloat32(exif.gps.latitude)) { + metadata.positionData.GPSData.latitude = parseFloat( + exif.gps.latitude.toFixed(6) + ); + } + + if (metadata.positionData) { + if (!metadata.positionData.GPSData || + Object.keys(metadata.positionData.GPSData).length === 0) { + metadata.positionData.GPSData = undefined; + metadata.positionData = undefined; + } + } + } + //photoshop section (sometimes has City, Country and State) + if (exif.photoshop) { + function unescape(tag: string) { + return tag.replace(/&#([0-9]{1,3});/gi, function (match, numStr) { + return String.fromCharCode(parseInt(numStr, 10)); + }); + } + + if (!metadata.positionData?.country && exif.photoshop.Country) { + metadata.positionData = metadata.positionData || {}; + metadata.positionData.country = unescape(exif.photoshop.Country); + } + if (!metadata.positionData?.state && exif.photoshop.State) { + metadata.positionData = metadata.positionData || {}; + metadata.positionData.state = unescape(exif.photoshop.State); + } + if (!metadata.positionData?.city && exif.photoshop.City) { + metadata.positionData = metadata.positionData || {}; + metadata.positionData.city = unescape(exif.photoshop.City); + } + } + + /////////////////////////////////////// + metadata.size.height = Math.max(metadata.size.height, 1); //ensure height dimension is positive + metadata.size.width = Math.max(metadata.size.width, 1); //ensure width dimension is positive + + //Before moving on to the XMP section (particularly the regions (mwg-rs)) + //we need to switch width and height for images that are rotated sideways + if (4 < orientation) { //Orientation is sideways (rotated 90% or 270%) // noinspection JSSuspiciousNameCombination const height = metadata.size.width; // noinspection JSSuspiciousNameCombination metadata.size.width = metadata.size.height; metadata.size.height = height; } + /////////////////////////////////////// + //xmp section + if (exif.xmp && exif.xmp.Rating) { + metadata.rating = exif.xmp.Rating; + if (metadata.rating < 0) { + metadata.rating = 0; + } + } + //xmp."mwg-rs" section if (Config.Faces.enabled && exif["mwg-rs"] && exif["mwg-rs"].Regions) { @@ -422,24 +491,24 @@ export class MetadataLoader { x: string, y: string ) => { - if (OrientationTypes.BOTTOM_LEFT < orientation) { + if (4 < orientation) { //roation is sidewards (90 or 270 degrees) [x, y] = [y, x]; [w, h] = [h, w]; } let swapX = 0; let swapY = 0; switch (orientation) { - case OrientationTypes.TOP_RIGHT: - case OrientationTypes.RIGHT_TOP: + case 2: //TOP RIGHT (Mirror horizontal): + case 6: //RIGHT TOP (Rotate 90 CW) swapX = 1; break; - case OrientationTypes.BOTTOM_RIGHT: - case OrientationTypes.RIGHT_BOTTOM: + case 3: // BOTTOM RIGHT (Rotate 180) + case 7: // RIGHT BOTTOM (Mirror horizontal and rotate 90 CW) swapX = 1; swapY = 1; break; - case OrientationTypes.BOTTOM_LEFT: - case OrientationTypes.LEFT_BOTTOM: + case 4: //BOTTOM_LEFT (Mirror vertical) + case 8: //LEFT_BOTTOM (Rotate 270 CW) swapY = 1; break; } @@ -451,7 +520,6 @@ export class MetadataLoader { top: Math.round(Math.abs(parseFloat(y) - swapY) * metadata.size.height), }; }; - /* Adobe Lightroom based face region structure */ if ( regionRoot && @@ -497,7 +565,7 @@ export class MetadataLoader { box.top = Math.round(Math.max(0, box.top - box.height / 2)); - faces.push({name, box}); + faces.push({ name, box }); } } if (faces.length > 0) { @@ -517,6 +585,11 @@ export class MetadataLoader { // ignoring errors } + if (!metadata.creationDate) { + // creationDate can be negative, when it was created before epoch (1970) + metadata.creationDate = 0; + } + try { // search for sidecar and merge metadata const fullPathWithoutExt = path.parse(fullPath).name; @@ -564,7 +637,5 @@ export class MetadataLoader { return MetadataLoader.EMPTY_METADATA; } return metadata; - - } } diff --git a/src/common/entities/MediaDTO.ts b/src/common/entities/MediaDTO.ts index 9c282abe..7bcdcd99 100644 --- a/src/common/entities/MediaDTO.ts +++ b/src/common/entities/MediaDTO.ts @@ -17,6 +17,7 @@ export interface MediaMetadata { size: MediaDimension; creationDate: number; fileSize: number; + creationDateOffset?: string; keywords?: string[]; rating?: RatingTypes; title?: string; diff --git a/src/common/entities/PhotoDTO.ts b/src/common/entities/PhotoDTO.ts index a4af1c24..6184bbd2 100644 --- a/src/common/entities/PhotoDTO.ts +++ b/src/common/entities/PhotoDTO.ts @@ -33,6 +33,7 @@ export interface PhotoMetadata extends MediaMetadata { positionData?: PositionMetaData; size: MediaDimension; creationDate: number; + creationDateOffset?: string; fileSize: number; faces?: FaceRegion[]; } diff --git a/src/common/entities/VideoDTO.ts b/src/common/entities/VideoDTO.ts index d9cefefc..ef0e39fd 100644 --- a/src/common/entities/VideoDTO.ts +++ b/src/common/entities/VideoDTO.ts @@ -11,6 +11,7 @@ export interface VideoDTO extends MediaDTO { export interface VideoMetadata extends MediaMetadata { size: MediaDimension; creationDate: number; + creationDateOffset?: string; bitRate: number; duration: number; // in milliseconds fileSize: number; diff --git a/test/backend/assets/Chars.json b/test/backend/assets/Chars.json index 281eb7c9..83bec784 100644 --- a/test/backend/assets/Chars.json +++ b/test/backend/assets/Chars.json @@ -3,7 +3,8 @@ "width": 1920, "height": 1080 }, - "creationDate": 1706659327000, + "creationDate": 1706655727000, + "creationDateOffset": "+01:00", "fileSize": 111432, "positionData": { "GPSData": { diff --git a/test/backend/assets/edge_case_exif_data/before_epoch.json b/test/backend/assets/edge_case_exif_data/before_epoch.json index 51ddb9a6..982679dd 100644 --- a/test/backend/assets/edge_case_exif_data/before_epoch.json +++ b/test/backend/assets/edge_case_exif_data/before_epoch.json @@ -9,7 +9,8 @@ "model": "Canon EOS 600D" }, "caption": "Bambi Caption", - "creationDate": -11630935227000, + "creationDate": -11630942427000, + "creationDateOffset": "+02:00", "faces": [ { "box": { diff --git a/test/backend/assets/edge_case_exif_data/date_error.json b/test/backend/assets/edge_case_exif_data/date_error.json index 7600f4da..7d973302 100644 --- a/test/backend/assets/edge_case_exif_data/date_error.json +++ b/test/backend/assets/edge_case_exif_data/date_error.json @@ -7,7 +7,7 @@ "make": "NIKON", "model": "E880" }, - "creationDate": -2211753600000, + "creationDate": 0, "fileSize": 72850, "size": { "height": 768, diff --git a/test/backend/assets/png_with_keyword_and_dates.json b/test/backend/assets/png_with_faces_and_dates.json similarity index 85% rename from test/backend/assets/png_with_keyword_and_dates.json rename to test/backend/assets/png_with_faces_and_dates.json index 2f4ad94e..a7272249 100644 --- a/test/backend/assets/png_with_keyword_and_dates.json +++ b/test/backend/assets/png_with_faces_and_dates.json @@ -4,7 +4,8 @@ "width": 26, "height": 26 }, - "creationDate": 1707167247786, + "creationDate": 1599990007000, + "creationDateOffset": "+05:00", "fileSize": 5758, "keywords": [ ], diff --git a/test/backend/assets/png_with_keyword_and_dates.png b/test/backend/assets/png_with_faces_and_dates.png similarity index 100% rename from test/backend/assets/png_with_keyword_and_dates.png rename to test/backend/assets/png_with_faces_and_dates.png diff --git a/test/backend/assets/test_png.json b/test/backend/assets/test_png.json index 88800147..7b528aac 100644 --- a/test/backend/assets/test_png.json +++ b/test/backend/assets/test_png.json @@ -24,5 +24,6 @@ "size": { "height": 26, "width": 26 - } + }, + "creationDate": 1707171121504 } diff --git a/test/backend/assets/timestamps/big_ben.jpg b/test/backend/assets/timestamps/big_ben.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0649be533dc6f48db824dfd3c56bfce117247e64 GIT binary patch literal 18532 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 literal 0 HcmV?d00001 diff --git a/test/backend/assets/timestamps/big_ben.json b/test/backend/assets/timestamps/big_ben.json new file mode 100644 index 00000000..c4080036 --- /dev/null +++ b/test/backend/assets/timestamps/big_ben.json @@ -0,0 +1,25 @@ +{ + "size": { + "width": 200, + "height": 300 + }, + "creationDate": 1686141955000, + "creationDateOffset": "+01:00", + "fileSize": 18532, + "cameraData": { + "model": "Canon EOS R5", + "make": "Canon" + }, + "positionData": { + "GPSData": { + "longitude": -0.124575, + "latitude": 51.500694 + }, + "country": "Storbritannien", + "state": "England", + "city": "St James's" + }, + "keywords": [ + "Big Ben" + ] +} \ No newline at end of file diff --git a/test/backend/assets/timestamps/big_ben_no_tsoffset_but_gps_utc.jpg b/test/backend/assets/timestamps/big_ben_no_tsoffset_but_gps_utc.jpg new file mode 100644 index 0000000000000000000000000000000000000000..48897cf140d88e31f01c5fcbd89cbf46ce8c4916 GIT binary patch literal 18663 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 W2-!cur~wv|^}Tf)tK#6p$^Qq^NGX8; literal 0 HcmV?d00001 diff --git a/test/backend/assets/timestamps/big_ben_only_time.json b/test/backend/assets/timestamps/big_ben_only_time.json new file mode 100644 index 00000000..5113607d --- /dev/null +++ b/test/backend/assets/timestamps/big_ben_only_time.json @@ -0,0 +1,20 @@ +{ + "size": { + "width": 200, + "height": 300 + }, + "creationDate": 1686145555000, + "fileSize": 17850, + "cameraData": { + "model": "Canon EOS R5", + "make": "Canon" + }, + "positionData": { + "country": "Storbritannien", + "state": "England", + "city": "St James's" + }, + "keywords": [ + "Big Ben" + ] +} \ No newline at end of file diff --git a/test/backend/assets/timestamps/sydney_opera_house.jpg b/test/backend/assets/timestamps/sydney_opera_house.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b595487ed5c187ce6f0430dd7e5505bbb262fc50 GIT binary patch literal 22755 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 literal 0 HcmV?d00001 diff --git a/test/backend/assets/timestamps/sydney_opera_house.json b/test/backend/assets/timestamps/sydney_opera_house.json new file mode 100644 index 00000000..dda6cff5 --- /dev/null +++ b/test/backend/assets/timestamps/sydney_opera_house.json @@ -0,0 +1,25 @@ +{ + "size": { + "width": 300, + "height": 200 + }, + "creationDate": 1600512957000, + "creationDateOffset": "+10:00", + "fileSize": 22755, + "cameraData": { + "model": "ILCE-7RM3", + "make": "Sony" + }, + "positionData": { + "GPSData": { + "longitude": 151.210381, + "latitude": -33.855698 + }, + "country": "Australien", + "state": "New South Wales", + "city": "Dawes Point" + }, + "keywords": [ + "Sydney Opera House" + ] +} \ No newline at end of file diff --git a/test/backend/assets/timestamps/sydney_opera_house_no_tsoffset_but_gps_utc.jpg b/test/backend/assets/timestamps/sydney_opera_house_no_tsoffset_but_gps_utc.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f1c9a652d337a90e9c251477c8cd2121fa807884 GIT binary patch literal 22653 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�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 literal 0 HcmV?d00001 diff --git a/test/backend/assets/timestamps/sydney_opera_house_no_tsoffset_but_gps_utc.json b/test/backend/assets/timestamps/sydney_opera_house_no_tsoffset_but_gps_utc.json new file mode 100644 index 00000000..e5f78368 --- /dev/null +++ b/test/backend/assets/timestamps/sydney_opera_house_no_tsoffset_but_gps_utc.json @@ -0,0 +1,25 @@ +{ + "size": { + "width": 300, + "height": 200 + }, + "creationDate": 1600512957000, + "creationDateOffset": "+10:00", + "fileSize": 22653, + "cameraData": { + "model": "ILCE-7RM3", + "make": "Sony" + }, + "positionData": { + "GPSData": { + "longitude": 151.210381, + "latitude": -33.855698 + }, + "country": "Australien", + "state": "New South Wales", + "city": "Dawes Point" + }, + "keywords": [ + "Sydney Opera House" + ] +} \ No newline at end of file diff --git a/test/backend/assets/two_ratings.json b/test/backend/assets/two_ratings.json index 924232b0..67f1b08e 100644 --- a/test/backend/assets/two_ratings.json +++ b/test/backend/assets/two_ratings.json @@ -7,7 +7,8 @@ "make": "samsung", "model": "SM-G975F" }, - "creationDate": 1619181527000, + "creationDate": 1619174327000, + "creationDateOffset": "+02:00", "fileSize": 4877, "rating":3, "size": { diff --git a/test/backend/assets/xmp/xmp_subject.json b/test/backend/assets/xmp/xmp_subject.json index a9a46f4a..a8a641ef 100644 --- a/test/backend/assets/xmp/xmp_subject.json +++ b/test/backend/assets/xmp/xmp_subject.json @@ -7,7 +7,8 @@ "make": "samsung", "model": "SM-G975F" }, - "creationDate": 1614703656000, + "creationDate": 1614700056000, + "creationDateOffset": "+01:00", "fileSize": 4709, "keywords": [ "Max", diff --git a/test/backend/unit/model/threading/MetaDataLoader.spec.ts b/test/backend/unit/model/threading/MetaDataLoader.spec.ts index 0e074069..5a0e7e49 100644 --- a/test/backend/unit/model/threading/MetaDataLoader.spec.ts +++ b/test/backend/unit/model/threading/MetaDataLoader.spec.ts @@ -24,11 +24,16 @@ describe('MetadataLoader', () => { it('should load png', async () => { const data = await MetadataLoader.loadPhotoMetadata(path.join(__dirname, '/../../../assets/test_png.png')); - delete data.creationDate; // creation time for png not supported const expected = require(path.join(__dirname, '/../../../assets/test_png.json')); expect(Utils.clone(data)).to.be.deep.equal(expected); }); + it('should load png with faces and dates', async () => { + const data = await MetadataLoader.loadPhotoMetadata(path.join(__dirname, '/../../../assets/png_with_faces_and_dates.png')); + const expected = require(path.join(__dirname, '/../../../assets/png_with_faces_and_dates.json')); + expect(Utils.clone(data)).to.be.deep.equal(expected); + }); + it('should load jpg', async () => { const data = await MetadataLoader.loadPhotoMetadata(path.join(__dirname, '/../../../assets/test image öüóőúéáű-.,.jpg')); const expected = require(path.join(__dirname, '/../../../assets/test image öüóőúéáű-.,.json')); @@ -69,6 +74,31 @@ describe('MetadataLoader', () => { expect(Utils.clone(data)).to.be.deep.equal(expected); }); + it('should load jpg with timestamps, timezone AEST (UTC+10) and gps (UTC)', async () => { + const data = await MetadataLoader.loadPhotoMetadata(path.join(__dirname, '/../../../assets/timestamps/sydney_opera_house.jpg')); + const expected = require(path.join(__dirname, '/../../../assets/timestamps/sydney_opera_house.json')); + expect(Utils.clone(data)).to.be.deep.equal(expected); + }); + it('should load jpg with timestamps and gps (UTC) and calculate offset +10', async () => { + const data = await MetadataLoader.loadPhotoMetadata(path.join(__dirname, '/../../../assets/timestamps/sydney_opera_house_no_tsoffset_but_gps_utc.jpg')); + const expected = require(path.join(__dirname, '/../../../assets/timestamps/sydney_opera_house_no_tsoffset_but_gps_utc.json')); + expect(Utils.clone(data)).to.be.deep.equal(expected); + }); + it('should load jpg with timestamps, timezone BST (UTC+1) and gps (UTC)', async () => { + const data = await MetadataLoader.loadPhotoMetadata(path.join(__dirname, '/../../../assets/timestamps/big_ben.jpg')); + const expected = require(path.join(__dirname, '/../../../assets/timestamps/big_ben.json')); + expect(Utils.clone(data)).to.be.deep.equal(expected); + }); + it('should load jpg with timestamps and gps (UTC) and calculate offset +1', async () => { + const data = await MetadataLoader.loadPhotoMetadata(path.join(__dirname, '/../../../assets/timestamps/big_ben_no_tsoffset_but_gps_utc.jpg')); + const expected = require(path.join(__dirname, '/../../../assets/timestamps/big_ben_no_tsoffset_but_gps_utc.json')); + expect(Utils.clone(data)).to.be.deep.equal(expected); + }); + it('should load jpg with timestamps but no offset and no GPS to calculate it from', async () => { + const data = await MetadataLoader.loadPhotoMetadata(path.join(__dirname, '/../../../assets/timestamps/big_ben_only_time.jpg')); + const expected = require(path.join(__dirname, '/../../../assets/timestamps/big_ben_only_time.json')); + expect(Utils.clone(data)).to.be.deep.equal(expected); + }); describe('should load jpg with proper height and orientation', () => { it('jpg 1', async () => { const data = await MetadataLoader.loadPhotoMetadata(path.join(__dirname, '/../../../assets/orientation/broken_orientation_exif.jpg'));