1
0
mirror of https://github.com/immich-app/immich.git synced 2024-11-28 09:33:27 +02:00

fix(server): DateTimeOriginal overwrite issue with sidecar file (#11306)

* fix(server): DateTimeOriginal overwrite issue with sidecar file

* update unit test
This commit is contained in:
Michel Heusschen 2024-07-23 13:59:46 +02:00 committed by GitHub
parent 6394b4a9a3
commit 8725656fd2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 50 additions and 5 deletions

View File

@ -472,6 +472,44 @@ describe('/asset', () => {
expect(status).toEqual(200); expect(status).toEqual(200);
}); });
it('should update date time original when sidecar file contains DateTimeOriginal', async () => {
const sidecarData = `<?xpacket begin='?' id='W5M0MpCehiHzreSzNTczkc9d'?>
<x:xmpmeta xmlns:x='adobe:ns:meta/' x:xmptk='Image::ExifTool 12.40'>
<rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'>
<rdf:Description rdf:about=''
xmlns:exif='http://ns.adobe.com/exif/1.0/'>
<exif:ExifVersion>0220</exif:ExifVersion> <exif:DateTimeOriginal>2024-07-11T10:32:52Z</exif:DateTimeOriginal>
<exif:GPSVersionID>2.3.0.0</exif:GPSVersionID>
</rdf:Description>
</rdf:RDF>
</x:xmpmeta>
<?xpacket end='w'?>`;
const { id } = await utils.createAsset(user1.accessToken, {
sidecarData: {
bytes: Buffer.from(sidecarData),
filename: 'example.xmp',
},
});
await utils.waitForQueueFinish(admin.accessToken, 'metadataExtraction');
const assetInfo = await utils.getAssetInfo(user1.accessToken, id);
expect(assetInfo.exifInfo?.dateTimeOriginal).toBe('2024-07-11T10:32:52.000Z');
const { status, body } = await request(app)
.put(`/assets/${id}`)
.set('Authorization', `Bearer ${user1.accessToken}`)
.send({ dateTimeOriginal: '2023-11-19T18:11:00.000-07:00' });
expect(body).toMatchObject({
id,
exifInfo: expect.objectContaining({
dateTimeOriginal: '2023-11-20T01:11:00.000Z',
}),
});
expect(status).toEqual(200);
});
it('should reject invalid gps coordinates', async () => { it('should reject invalid gps coordinates', async () => {
for (const test of [ for (const test of [
{ latitude: 12 }, { latitude: 12 },

View File

@ -50,7 +50,7 @@ type CommandResponse = { stdout: string; stderr: string; exitCode: number | null
type EventType = 'assetUpload' | 'assetUpdate' | 'assetDelete' | 'userDelete' | 'assetHidden'; type EventType = 'assetUpload' | 'assetUpdate' | 'assetDelete' | 'userDelete' | 'assetHidden';
type WaitOptions = { event: EventType; id?: string; total?: number; timeout?: number }; type WaitOptions = { event: EventType; id?: string; total?: number; timeout?: number };
type AdminSetupOptions = { onboarding?: boolean }; type AdminSetupOptions = { onboarding?: boolean };
type AssetData = { bytes?: Buffer; filename: string }; type FileData = { bytes?: Buffer; filename: string };
const dbUrl = 'postgres://postgres:postgres@127.0.0.1:5433/immich'; const dbUrl = 'postgres://postgres:postgres@127.0.0.1:5433/immich';
export const baseUrl = 'http://127.0.0.1:2283'; export const baseUrl = 'http://127.0.0.1:2283';
@ -291,7 +291,10 @@ export const utils = {
createAsset: async ( createAsset: async (
accessToken: string, accessToken: string,
dto?: Partial<Omit<AssetMediaCreateDto, 'assetData'>> & { assetData?: AssetData }, dto?: Partial<Omit<AssetMediaCreateDto, 'assetData' | 'sidecarData'>> & {
assetData?: FileData;
sidecarData?: FileData;
},
) => { ) => {
const _dto = { const _dto = {
deviceAssetId: 'test-1', deviceAssetId: 'test-1',
@ -313,6 +316,10 @@ export const utils = {
.attach('assetData', assetData, filename) .attach('assetData', assetData, filename)
.set('Authorization', `Bearer ${accessToken}`); .set('Authorization', `Bearer ${accessToken}`);
if (dto?.sidecarData?.bytes) {
void builder.attach('sidecarData', dto.sidecarData.bytes, dto.sidecarData.filename);
}
for (const [key, value] of Object.entries(_dto)) { for (const [key, value] of Object.entries(_dto)) {
void builder.field(key, String(value)); void builder.field(key, String(value));
} }
@ -325,7 +332,7 @@ export const utils = {
replaceAsset: async ( replaceAsset: async (
accessToken: string, accessToken: string,
assetId: string, assetId: string,
dto?: Partial<Omit<AssetMediaCreateDto, 'assetData'>> & { assetData?: AssetData }, dto?: Partial<Omit<AssetMediaCreateDto, 'assetData'>> & { assetData?: FileData },
) => { ) => {
const _dto = { const _dto = {
deviceAssetId: 'test-1', deviceAssetId: 'test-1',

View File

@ -911,7 +911,7 @@ describe(MetadataService.name, () => {
expect(metadataMock.writeTags).toHaveBeenCalledWith(assetStub.sidecar.sidecarPath, { expect(metadataMock.writeTags).toHaveBeenCalledWith(assetStub.sidecar.sidecarPath, {
Description: description, Description: description,
ImageDescription: description, ImageDescription: description,
CreationDate: date, DateTimeOriginal: date,
GPSLatitude: gps, GPSLatitude: gps,
GPSLongitude: gps, GPSLongitude: gps,
}); });

View File

@ -316,7 +316,7 @@ export class MetadataService implements OnEvents {
{ {
Description: description, Description: description,
ImageDescription: description, ImageDescription: description,
CreationDate: dateTimeOriginal, DateTimeOriginal: dateTimeOriginal,
GPSLatitude: latitude, GPSLatitude: latitude,
GPSLongitude: longitude, GPSLongitude: longitude,
}, },