mirror of
https://github.com/immich-app/immich
synced 2026-08-15 13:03:57 +00:00
fix(server): preserve metadata when sidecar writes fail
This commit is contained in:
parent
db2033a4b0
commit
3e05fff27b
3 changed files with 39 additions and 0 deletions
19
server/src/repositories/metadata.repository.spec.ts
Normal file
19
server/src/repositories/metadata.repository.spec.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { ExifTool } from 'exiftool-vendored';
|
||||||
|
import { LoggingRepository } from 'src/repositories/logging.repository';
|
||||||
|
import { MetadataRepository } from 'src/repositories/metadata.repository';
|
||||||
|
import { automock } from 'test/utils';
|
||||||
|
|
||||||
|
describe(MetadataRepository.name, () => {
|
||||||
|
it('should propagate sidecar write errors', async () => {
|
||||||
|
const error = new Error('read-only file system');
|
||||||
|
const logger = automock(LoggingRepository, { strict: false });
|
||||||
|
const sut = new MetadataRepository(logger);
|
||||||
|
const write = vitest.fn().mockRejectedValue(error);
|
||||||
|
sut['exiftool'] = { write } as unknown as ExifTool;
|
||||||
|
|
||||||
|
await expect(sut.writeTags('/read-only/asset.jpg.xmp', { Description: 'description' })).rejects.toBe(error);
|
||||||
|
expect(logger.warn).toHaveBeenCalledWith(
|
||||||
|
'Error writing exif data (/read-only/asset.jpg.xmp): Error: read-only file system',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -132,6 +132,7 @@ export class MetadataRepository {
|
||||||
await this.exiftool.write(path, tagsToWrite);
|
await this.exiftool.write(path, tagsToWrite);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.warn(`Error writing exif data (${path}): ${error}`);
|
this.logger.warn(`Error writing exif data (${path}): ${error}`);
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2019,6 +2019,25 @@ describe(MetadataService.name, () => {
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should retain locked properties when writing tags fails', async () => {
|
||||||
|
const asset = AssetFactory.from()
|
||||||
|
.file({ type: AssetFileType.Sidecar })
|
||||||
|
.exif({ description: 'database description' })
|
||||||
|
.build();
|
||||||
|
const error = new Error('read-only file system');
|
||||||
|
|
||||||
|
mocks.assetJob.getLockedPropertiesForMetadataExtraction.mockResolvedValue(['description']);
|
||||||
|
mocks.assetJob.getForSidecarWriteJob.mockResolvedValue(getForSidecarWrite(asset));
|
||||||
|
mocks.metadata.writeTags.mockRejectedValue(error);
|
||||||
|
|
||||||
|
await expect(sut.handleSidecarWrite({ id: asset.id })).rejects.toBe(error);
|
||||||
|
expect(mocks.metadata.writeTags).toHaveBeenCalledWith(asset.files[0].path, {
|
||||||
|
Description: 'database description',
|
||||||
|
ImageDescription: 'database description',
|
||||||
|
});
|
||||||
|
expect(mocks.asset.unlockProperties).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
it('should write rating', async () => {
|
it('should write rating', async () => {
|
||||||
const asset = AssetFactory.from().file({ type: AssetFileType.Sidecar }).exif().build();
|
const asset = AssetFactory.from().file({ type: AssetFileType.Sidecar }).exif().build();
|
||||||
asset.exifInfo.rating = 4;
|
asset.exifInfo.rating = 4;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue