feat(server): check additional exif date tags (#19216)
* feat(server): check additional exif date tags - Add support for UTC date tags (GPSDateTime, DateTimeUTC, GPSDateStamp, SonyDateTime2) - This matches tags that exiftool-vendored uses for tzSource in extractTzOffsetFromUTCOffset() * Review comments * nit * review comments * lots of tests for exif datetime * missed * format * format again --------- Co-authored-by: Alex <alex.tran1502@gmail.com>
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { ContainerDirectoryItem, Maybe, Tags } from 'exiftool-vendored';
|
||||
import { firstDateTime } from 'exiftool-vendored/dist/FirstDateTime';
|
||||
import { ContainerDirectoryItem, ExifDateTime, Tags } from 'exiftool-vendored';
|
||||
import { Insertable } from 'kysely';
|
||||
import _ from 'lodash';
|
||||
import { Duration } from 'luxon';
|
||||
@@ -32,19 +31,47 @@ import { isFaceImportEnabled } from 'src/utils/misc';
|
||||
import { upsertTags } from 'src/utils/tag';
|
||||
|
||||
/** look for a date from these tags (in order) */
|
||||
const EXIF_DATE_TAGS: Array<keyof Tags> = [
|
||||
const EXIF_DATE_TAGS: Array<keyof ImmichTags> = [
|
||||
'SubSecDateTimeOriginal',
|
||||
'DateTimeOriginal',
|
||||
'SubSecCreateDate',
|
||||
'CreationDate',
|
||||
'CreateDate',
|
||||
'SubSecMediaCreateDate',
|
||||
'DateTimeOriginal',
|
||||
'CreateDate',
|
||||
'MediaCreateDate',
|
||||
'CreationDate',
|
||||
'DateTimeCreated',
|
||||
'GPSDateTime',
|
||||
'DateTimeUTC',
|
||||
'SonyDateTime2',
|
||||
// Undocumented, non-standard tag from insta360 in xmp.GPano namespace
|
||||
'SourceImageCreateTime' as keyof Tags,
|
||||
'SourceImageCreateTime' as keyof ImmichTags,
|
||||
];
|
||||
|
||||
export function firstDateTime(tags: ImmichTags) {
|
||||
for (const tag of EXIF_DATE_TAGS) {
|
||||
const tagValue = tags?.[tag];
|
||||
|
||||
if (tagValue instanceof ExifDateTime) {
|
||||
return {
|
||||
tag,
|
||||
dateTime: tagValue,
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof tagValue !== 'string') {
|
||||
continue;
|
||||
}
|
||||
|
||||
const exifDateTime = ExifDateTime.fromEXIF(tagValue);
|
||||
if (exifDateTime) {
|
||||
return {
|
||||
tag,
|
||||
dateTime: exifDateTime,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const validate = <T>(value: T): NonNullable<T> | null => {
|
||||
// handle lists of numbers
|
||||
if (Array.isArray(value)) {
|
||||
@@ -407,7 +434,8 @@ export class MetadataService extends BaseService {
|
||||
|
||||
// prefer dates from sidecar tags
|
||||
if (sidecarTags) {
|
||||
const sidecarDate = firstDateTime(sidecarTags as Tags, EXIF_DATE_TAGS);
|
||||
const result = firstDateTime(sidecarTags);
|
||||
const sidecarDate = result?.dateTime;
|
||||
if (sidecarDate) {
|
||||
for (const tag of EXIF_DATE_TAGS) {
|
||||
delete mediaTags[tag];
|
||||
@@ -748,8 +776,12 @@ export class MetadataService extends BaseService {
|
||||
}
|
||||
|
||||
private getDates(asset: { id: string; originalPath: string }, exifTags: ImmichTags, stats: Stats) {
|
||||
const dateTime = firstDateTime(exifTags as Maybe<Tags>, EXIF_DATE_TAGS);
|
||||
this.logger.verbose(`Date and time is ${dateTime} for asset ${asset.id}: ${asset.originalPath}`);
|
||||
const result = firstDateTime(exifTags);
|
||||
const tag = result?.tag;
|
||||
const dateTime = result?.dateTime;
|
||||
this.logger.verbose(
|
||||
`Date and time is ${dateTime} using exifTag ${tag} for asset ${asset.id}: ${asset.originalPath}`,
|
||||
);
|
||||
|
||||
// timezone
|
||||
let timeZone = exifTags.tz ?? null;
|
||||
|
||||
Reference in New Issue
Block a user