Skip to content

Commit 320580e

Browse files
committed
fix(server): adjust panorama medata for new image dimensions
1 parent db15e5e commit 320580e

File tree

5 files changed

+32
-20
lines changed

5 files changed

+32
-20
lines changed

server/src/repositories/media.repository.ts

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -121,19 +121,30 @@ export class MediaRepository {
121121
}
122122
}
123123

124-
async copyTagGroup(tagGroup: string, source: string, target: string): Promise<boolean> {
124+
async copyGPanoTags(source: string, target: string, ratio: number = 1): Promise<boolean> {
125+
const tagsToWrite: Record<string, any> = {};
125126
try {
126-
await exiftool.write(
127-
target,
128-
{},
129-
{
130-
ignoreMinorErrors: true,
131-
writeArgs: ['-TagsFromFile', source, `-${tagGroup}:all>${tagGroup}:all`, '-overwrite_original'],
132-
},
133-
);
127+
const metadata = await exiftool.readRaw(source, ['-XMP-GPano:all', '-G1', '-s']);
128+
129+
for (const [tag, val] of Object.entries(metadata)) {
130+
if (tag.startsWith('XMP-GPano:')) {
131+
tagsToWrite[tag] =
132+
tag.endsWith('Pixels') && typeof val === 'number' ? Math.round(val * ratio) : (tagsToWrite[tag] = val);
133+
}
134+
}
135+
} catch (error: any) {
136+
this.logger.warn(`Could not read or parse GPano tag data from image: ${error.message}`);
137+
return false;
138+
}
139+
140+
try {
141+
await exiftool.write(target, tagsToWrite, {
142+
ignoreMinorErrors: true,
143+
writeArgs: ['-overwrite_original'],
144+
});
134145
return true;
135146
} catch (error: any) {
136-
this.logger.warn(`Could not copy tag data to image: ${error.message}`);
147+
this.logger.warn(`Could not write GPano tag data to image: ${error.message}`);
137148
return false;
138149
}
139150
}

server/src/services/media.service.spec.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -865,7 +865,6 @@ describe(MediaService.name, () => {
865865
mocks.systemMetadata.get.mockResolvedValue({ image: { fullsize: { enabled: false } } });
866866
mocks.media.extract.mockResolvedValue({ buffer: extractedBuffer, format: RawExtractedFormat.Jpeg });
867867
mocks.media.getImageDimensions.mockResolvedValue({ width: 3840, height: 2160 });
868-
mocks.media.copyTagGroup.mockResolvedValue(true);
869868

870869
mocks.assetJob.getForGenerateThumbnailJob.mockResolvedValue(assetStub.panoramaTif);
871870

@@ -892,11 +891,11 @@ describe(MediaService.name, () => {
892891
expect.any(String),
893892
);
894893

895-
expect(mocks.media.copyTagGroup).toHaveBeenCalledTimes(2);
896-
expect(mocks.media.copyTagGroup).toHaveBeenCalledWith(
897-
'XMP-GPano',
894+
expect(mocks.media.copyGPanoTags).toHaveBeenCalledTimes(2);
895+
expect(mocks.media.copyGPanoTags).toHaveBeenCalledWith(
898896
assetStub.panoramaTif.originalPath,
899897
expect.any(String),
898+
1440 / assetStub.panoramaTif.exifInfo.exifImageHeight!,
900899
);
901900
});
902901

server/src/services/media.service.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -316,12 +316,13 @@ export class MediaService extends BaseService {
316316

317317
const outputs = await Promise.all(promises);
318318

319-
if (asset.exifInfo.projectionType === 'EQUIRECTANGULAR') {
319+
const originalSize = asset.exifInfo.exifImageHeight;
320+
if (asset.exifInfo.projectionType === 'EQUIRECTANGULAR' && originalSize) {
321+
// preview size is min(preview size, original size) so do the same for pano pixel adjustment
322+
const pixelRatio = image.preview.size > originalSize ? 1 : image.preview.size / originalSize;
320323
const promises = [
321-
this.mediaRepository.copyTagGroup('XMP-GPano', asset.originalPath, previewPath),
322-
fullsizePath
323-
? this.mediaRepository.copyTagGroup('XMP-GPano', asset.originalPath, fullsizePath)
324-
: Promise.resolve(),
324+
this.mediaRepository.copyGPanoTags(asset.originalPath, previewPath, pixelRatio),
325+
fullsizePath ? this.mediaRepository.copyGPanoTags(asset.originalPath, fullsizePath) : Promise.resolve(),
325326
];
326327
await Promise.all(promises);
327328
}

server/test/fixtures/asset.stub.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -897,6 +897,7 @@ export const assetStub = {
897897
exifInfo: {
898898
fileSizeInByte: 5000,
899899
projectionType: 'EQUIRECTANGULAR',
900+
exifImageHeight: 2160,
900901
} as Exif,
901902
duplicateId: null,
902903
isOffline: false,

server/test/repositories/media.repository.mock.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export const newMediaRepositoryMock = (): Mocked<RepositoryInterface<MediaReposi
66
return {
77
generateThumbnail: vitest.fn().mockImplementation(() => Promise.resolve()),
88
writeExif: vitest.fn().mockImplementation(() => Promise.resolve()),
9-
copyTagGroup: vitest.fn().mockImplementation(() => Promise.resolve()),
9+
copyGPanoTags: vitest.fn().mockResolvedValue(true),
1010
generateThumbhash: vitest.fn().mockResolvedValue(Buffer.from('')),
1111
decodeImage: vitest.fn().mockResolvedValue({ data: Buffer.from(''), info: {} }),
1212
extract: vitest.fn().mockResolvedValue(null),

0 commit comments

Comments
 (0)