mirror of
https://github.com/samsonjs/immich.git
synced 2026-03-25 09:15:56 +00:00
fix: time zone upserts (#25889)
This commit is contained in:
parent
27a2808470
commit
9c098109e0
7 changed files with 69 additions and 7 deletions
|
|
@ -473,6 +473,7 @@ describe('/asset', () => {
|
|||
id: user1Assets[0].id,
|
||||
exifInfo: expect.objectContaining({
|
||||
dateTimeOriginal: '2023-11-20T01:11:00+00:00',
|
||||
timeZone: 'UTC-7',
|
||||
}),
|
||||
});
|
||||
expect(status).toEqual(200);
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ import {
|
|||
onBeforeUnlink,
|
||||
} from 'src/utils/asset.util';
|
||||
import { updateLockedColumns } from 'src/utils/database';
|
||||
import { extractTimeZone } from 'src/utils/date';
|
||||
import { transformOcrBoundingBox } from 'src/utils/transform';
|
||||
|
||||
@Injectable()
|
||||
|
|
@ -168,12 +169,13 @@ export class AssetService extends BaseService {
|
|||
},
|
||||
_.isUndefined,
|
||||
);
|
||||
const extractedTimeZone = dateTimeOriginal ? DateTime.fromISO(dateTimeOriginal, { setZone: true }).zone : undefined;
|
||||
|
||||
if (Object.keys(exifDto).length > 0) {
|
||||
await this.assetRepository.updateAllExif(ids, exifDto);
|
||||
}
|
||||
|
||||
const extractedTimeZone = extractTimeZone(dateTimeOriginal);
|
||||
|
||||
if (
|
||||
(dateTimeRelative !== undefined && dateTimeRelative !== 0) ||
|
||||
timeZone !== undefined ||
|
||||
|
|
@ -513,12 +515,11 @@ export class AssetService extends BaseService {
|
|||
rating?: number;
|
||||
}) {
|
||||
const { id, description, dateTimeOriginal, latitude, longitude, rating } = dto;
|
||||
const extractedTimeZone = dateTimeOriginal ? DateTime.fromISO(dateTimeOriginal, { setZone: true }).zone : undefined;
|
||||
const writes = _.omitBy(
|
||||
{
|
||||
description,
|
||||
dateTimeOriginal,
|
||||
timeZone: extractedTimeZone?.type === 'fixed' ? extractedTimeZone.name : undefined,
|
||||
timeZone: extractTimeZone(dateTimeOriginal)?.name,
|
||||
latitude,
|
||||
longitude,
|
||||
rating,
|
||||
|
|
|
|||
|
|
@ -1766,13 +1766,14 @@ describe(MetadataService.name, () => {
|
|||
const asset = factory.jobAssets.sidecarWrite();
|
||||
const description = 'this is a description';
|
||||
const gps = 12;
|
||||
const date = '2023-11-22T04:56:12.196Z';
|
||||
const date = '2023-11-21T22:56:12.196-06:00';
|
||||
|
||||
mocks.assetJob.getLockedPropertiesForMetadataExtraction.mockResolvedValue([
|
||||
'description',
|
||||
'latitude',
|
||||
'longitude',
|
||||
'dateTimeOriginal',
|
||||
'timeZone',
|
||||
]);
|
||||
mocks.assetJob.getForSidecarWriteJob.mockResolvedValue(asset);
|
||||
await expect(
|
||||
|
|
@ -1792,6 +1793,7 @@ describe(MetadataService.name, () => {
|
|||
'latitude',
|
||||
'longitude',
|
||||
'dateTimeOriginal',
|
||||
'timeZone',
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ import { BaseService } from 'src/services/base.service';
|
|||
import { JobItem, JobOf } from 'src/types';
|
||||
import { getAssetFiles } from 'src/utils/asset.util';
|
||||
import { isAssetChecksumConstraint } from 'src/utils/database';
|
||||
import { mergeTimeZone } from 'src/utils/date';
|
||||
import { mimeTypes } from 'src/utils/mime-types';
|
||||
import { isFaceImportEnabled } from 'src/utils/misc';
|
||||
import { upsertTags } from 'src/utils/tag';
|
||||
|
|
@ -431,14 +432,16 @@ export class MetadataService extends BaseService {
|
|||
const { sidecarFile } = getAssetFiles(asset.files);
|
||||
const sidecarPath = sidecarFile?.path || `${asset.originalPath}.xmp`;
|
||||
|
||||
const { description, dateTimeOriginal, latitude, longitude, rating, tags } = _.pick(
|
||||
const { description, dateTimeOriginal, latitude, longitude, rating, tags, timeZone } = _.pick(
|
||||
{
|
||||
description: asset.exifInfo.description,
|
||||
dateTimeOriginal: asset.exifInfo.dateTimeOriginal,
|
||||
// the kysely type is wrong here; fixed in 0.28.3
|
||||
dateTimeOriginal: asset.exifInfo.dateTimeOriginal as string | null,
|
||||
latitude: asset.exifInfo.latitude,
|
||||
longitude: asset.exifInfo.longitude,
|
||||
rating: asset.exifInfo.rating,
|
||||
tags: asset.exifInfo.tags,
|
||||
timeZone: asset.exifInfo.timeZone,
|
||||
},
|
||||
lockedProperties,
|
||||
);
|
||||
|
|
@ -447,7 +450,7 @@ export class MetadataService extends BaseService {
|
|||
<Tags>{
|
||||
Description: description,
|
||||
ImageDescription: description,
|
||||
DateTimeOriginal: dateTimeOriginal ? String(dateTimeOriginal) : undefined,
|
||||
DateTimeOriginal: mergeTimeZone(dateTimeOriginal, timeZone)?.toISO(),
|
||||
GPSLatitude: latitude,
|
||||
GPSLongitude: longitude,
|
||||
Rating: rating,
|
||||
|
|
|
|||
|
|
@ -1,3 +1,16 @@
|
|||
import { DateTime } from 'luxon';
|
||||
|
||||
export const asDateString = (x: Date | string | null): string | null => {
|
||||
return x instanceof Date ? x.toISOString().split('T')[0] : x;
|
||||
};
|
||||
|
||||
export const extractTimeZone = (dateTimeOriginal?: string | null) => {
|
||||
const extractedTimeZone = dateTimeOriginal ? DateTime.fromISO(dateTimeOriginal, { setZone: true }).zone : undefined;
|
||||
return extractedTimeZone?.type === 'fixed' ? extractedTimeZone : undefined;
|
||||
};
|
||||
|
||||
export const mergeTimeZone = (dateTimeOriginal?: string | null, timeZone?: string | null) => {
|
||||
return dateTimeOriginal
|
||||
? DateTime.fromISO(dateTimeOriginal, { zone: 'UTC' }).setZone(timeZone ?? undefined)
|
||||
: undefined;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -456,6 +456,47 @@ describe(AssetService.name, () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('should relatively update an assets with timezone', async () => {
|
||||
const { sut, ctx } = setup();
|
||||
ctx.getMock(JobRepository).queueAll.mockResolvedValue();
|
||||
const { user } = await ctx.newUser();
|
||||
const auth = factory.auth({ user });
|
||||
const { asset } = await ctx.newAsset({ ownerId: user.id });
|
||||
await ctx.newExif({ assetId: asset.id, dateTimeOriginal: '2023-11-19T18:11:00', timeZone: 'UTC+5' });
|
||||
|
||||
await sut.updateAll(auth, { ids: [asset.id], dateTimeRelative: -1441 });
|
||||
|
||||
await expect(ctx.get(AssetRepository).getById(asset.id, { exifInfo: true })).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
exifInfo: expect.objectContaining({
|
||||
dateTimeOriginal: '2023-11-18T18:10:00+00:00',
|
||||
timeZone: 'UTC+5',
|
||||
lockedProperties: ['timeZone', 'dateTimeOriginal'],
|
||||
}),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should relatively update an assets and set a timezone', async () => {
|
||||
const { sut, ctx } = setup();
|
||||
ctx.getMock(JobRepository).queueAll.mockResolvedValue();
|
||||
const { user } = await ctx.newUser();
|
||||
const auth = factory.auth({ user });
|
||||
const { asset } = await ctx.newAsset({ ownerId: user.id });
|
||||
await ctx.newExif({ assetId: asset.id, dateTimeOriginal: '2023-11-19T18:11:00' });
|
||||
|
||||
await sut.updateAll(auth, { ids: [asset.id], dateTimeRelative: -11, timeZone: 'UTC+5' });
|
||||
|
||||
await expect(ctx.get(AssetRepository).getById(asset.id, { exifInfo: true })).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
exifInfo: expect.objectContaining({
|
||||
dateTimeOriginal: '2023-11-19T18:00:00+00:00',
|
||||
timeZone: 'UTC+5',
|
||||
}),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should update dateTimeOriginal', async () => {
|
||||
const { sut, ctx } = setup();
|
||||
ctx.getMock(JobRepository).queueAll.mockResolvedValue();
|
||||
|
|
|
|||
|
|
@ -361,6 +361,7 @@ const assetSidecarWriteFactory = () => {
|
|||
latitude: 12,
|
||||
longitude: 12,
|
||||
dateTimeOriginal: '2023-11-22T04:56:12.196Z',
|
||||
timeZone: 'UTC-6',
|
||||
} as unknown as Exif,
|
||||
};
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue