1
0
Fork 0
mirror of https://github.com/immich-app/immich.git synced 2024-12-28 22:51:59 +00:00

Add timezone to exif entity (#1894)

* Add timezone to exif entity

* Refactor logging

---------

Co-authored-by: Andrea Alemani <andrea.alemani94@gmail.com>
This commit is contained in:
AndreAle94 2023-04-02 21:11:24 +02:00 committed by GitHub
parent 8b001b87d2
commit 94b2ea9b5f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 75 additions and 12 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -17,6 +17,7 @@ import { ConfigService } from '@nestjs/config';
import { InjectRepository } from '@nestjs/typeorm';
import { Job } from 'bull';
import { ExifDateTime, exiftool, Tags } from 'exiftool-vendored';
import tz_lookup from '@photostructure/tz-lookup';
import ffmpeg, { FfprobeData } from 'fluent-ffmpeg';
import { getName } from 'i18n-iso-countries';
import geocoder, { InitOptions } from 'local-reverse-geocoder';
@ -190,6 +191,17 @@ export class MetadataExtractionProcessor {
return exifDate.toDate();
};
const exifTimeZone = (exifDate: string | ExifDateTime | undefined) => {
if (!exifDate) return null;
if (typeof exifDate === 'string') {
return null;
}
return exifDate.zone ?? null;
};
const timeZone = exifTimeZone(exifData?.DateTimeOriginal ?? exifData?.CreateDate ?? asset.fileCreatedAt);
const fileCreatedAt = exifToDate(exifData?.DateTimeOriginal ?? exifData?.CreateDate ?? asset.fileCreatedAt);
const fileModifiedAt = exifToDate(exifData?.ModifyDate ?? asset.fileModifiedAt);
const fileStats = fs.statSync(asset.originalPath);
@ -207,6 +219,7 @@ export class MetadataExtractionProcessor {
newExif.orientation = exifData?.Orientation?.toString() || null;
newExif.dateTimeOriginal = fileCreatedAt;
newExif.modifyDate = fileModifiedAt;
newExif.timeZone = timeZone;
newExif.lensModel = exifData?.LensModel || null;
newExif.fNumber = exifData?.FNumber || null;
newExif.focalLength = exifData?.FocalLength ? parseFloat(exifData.FocalLength) : null;
@ -308,6 +321,7 @@ export class MetadataExtractionProcessor {
newExif.fileSizeInByte = data.format.size || null;
newExif.dateTimeOriginal = fileCreatedAt ? new Date(fileCreatedAt) : null;
newExif.modifyDate = null;
newExif.timeZone = null;
newExif.latitude = null;
newExif.longitude = null;
newExif.city = null;
@ -345,6 +359,14 @@ export class MetadataExtractionProcessor {
}
}
if (newExif.longitude && newExif.latitude) {
try {
newExif.timeZone = tz_lookup(newExif.latitude, newExif.longitude);
} catch (error: any) {
this.logger.warn(`Error while calculating timezone from gps coordinates: ${error}`, error?.stack);
}
}
// Reverse GeoCoding
if (this.isGeocodeInitialized && newExif.longitude && newExif.latitude) {
const { country, state, city } = await this.reverseGeocodeExif(newExif.latitude, newExif.longitude);

View file

@ -3537,6 +3537,11 @@
"nullable": true,
"default": null
},
"timeZone": {
"type": "string",
"nullable": true,
"default": null
},
"lensModel": {
"type": "string",
"nullable": true,

View file

@ -13,6 +13,7 @@ export class ExifResponseDto {
orientation?: string | null = null;
dateTimeOriginal?: Date | null = null;
modifyDate?: Date | null = null;
timeZone?: string | null = null;
lensModel?: string | null = null;
fNumber?: number | null = null;
focalLength?: number | null = null;
@ -36,6 +37,7 @@ export function mapExif(entity: ExifEntity): ExifResponseDto {
orientation: entity.orientation,
dateTimeOriginal: entity.dateTimeOriginal,
modifyDate: entity.modifyDate,
timeZone: entity.timeZone,
lensModel: entity.lensModel,
fNumber: entity.fNumber,
focalLength: entity.focalLength,

View file

@ -323,6 +323,7 @@ const assetInfo: ExifResponseDto = {
orientation: 'orientation',
dateTimeOriginal: today,
modifyDate: today,
timeZone: 'America/Los_Angeles',
lensModel: 'fancy',
fNumber: 100,
focalLength: 100,
@ -607,6 +608,7 @@ export const sharedLinkStub = {
orientation: 'orientation',
dateTimeOriginal: today,
modifyDate: today,
timeZone: 'America/Los_Angeles',
latitude: 100,
longitude: 100,
city: 'city',

View file

@ -34,6 +34,9 @@ export class ExifEntity {
@Column({ type: 'timestamptz', nullable: true })
modifyDate!: Date | null;
@Column({ type: 'varchar', nullable: true })
timeZone!: string | null;
@Column({ type: 'float', nullable: true })
latitude!: number | null;

View file

@ -0,0 +1,14 @@
import { MigrationInterface, QueryRunner } from "typeorm";
export class AddExifTimeZone1677497925328 implements MigrationInterface {
name = 'AddExifTimeZone1677497925328'
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "exif" ADD "timeZone" character varying`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "timeZone"`);
}
}

View file

@ -1126,6 +1126,12 @@ export interface ExifResponseDto {
* @memberof ExifResponseDto
*/
'modifyDate'?: string | null;
/**
*
* @type {string}
* @memberof ExifResponseDto
*/
'timeZone'?: string | null;
/**
*
* @type {string}

View file

@ -8,6 +8,7 @@
import { AssetResponseDto, AlbumResponseDto } from '@api';
import { asByteUnitString } from '../../utils/byte-units';
import { locale } from '$lib/stores/preferences.store';
import { DateTime } from 'luxon';
import type { LatLngTuple } from 'leaflet';
export let asset: AssetResponseDto;
@ -55,7 +56,9 @@
{/if}
{#if asset.exifInfo?.dateTimeOriginal}
{@const assetDateTimeOriginal = new Date(asset.exifInfo.dateTimeOriginal)}
{@const assetDateTimeOriginal = DateTime.fromISO(asset.exifInfo.dateTimeOriginal, {
zone: asset.exifInfo.timeZone ?? undefined
})}
<div class="flex gap-4 py-4">
<div>
<Calendar size="24" />
@ -63,20 +66,26 @@
<div>
<p>
{assetDateTimeOriginal.toLocaleDateString($locale, {
month: 'short',
day: 'numeric',
year: 'numeric'
})}
{assetDateTimeOriginal.toLocaleString(
{
month: 'short',
day: 'numeric',
year: 'numeric'
},
{ locale: $locale }
)}
</p>
<div class="flex gap-2 text-sm">
<p>
{assetDateTimeOriginal.toLocaleString($locale, {
weekday: 'short',
hour: 'numeric',
minute: '2-digit',
timeZoneName: 'longOffset'
})}
{assetDateTimeOriginal.toLocaleString(
{
weekday: 'short',
hour: 'numeric',
minute: '2-digit',
timeZoneName: 'longOffset'
},
{ locale: $locale }
)}
</p>
</div>
</div>