mirror of
				https://github.com/immich-app/immich.git
				synced 2025-10-30 18:22:37 -04:00 
			
		
		
		
	fix: use full-size image for non-web-compatible panoramas (#20359)
* fix(web): use full-size image for non-web-compatible panoramas * always generate full-size image for panoramas * add unit test * fix formatting --------- Co-authored-by: gergo= <gergo@pitty.hu>
This commit is contained in:
		
							parent
							
								
									42f46b11f4
								
							
						
					
					
						commit
						6973683ea7
					
				| @ -861,6 +861,37 @@ describe(MediaService.name, () => { | |||||||
|       ); |       ); | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|  |     it('should always generate full-size preview from non-web-friendly panoramas', async () => { | ||||||
|  |       mocks.systemMetadata.get.mockResolvedValue({ image: { fullsize: { enabled: false } } }); | ||||||
|  |       mocks.media.extract.mockResolvedValue({ buffer: extractedBuffer, format: RawExtractedFormat.Jpeg }); | ||||||
|  |       mocks.media.getImageDimensions.mockResolvedValue({ width: 3840, height: 2160 }); | ||||||
|  | 
 | ||||||
|  |       mocks.assetJob.getForGenerateThumbnailJob.mockResolvedValue(assetStub.panoramaTif); | ||||||
|  | 
 | ||||||
|  |       await sut.handleGenerateThumbnails({ id: assetStub.image.id }); | ||||||
|  | 
 | ||||||
|  |       expect(mocks.media.decodeImage).toHaveBeenCalledOnce(); | ||||||
|  |       expect(mocks.media.decodeImage).toHaveBeenCalledWith(assetStub.panoramaTif.originalPath, { | ||||||
|  |         colorspace: Colorspace.Srgb, | ||||||
|  |         orientation: undefined, | ||||||
|  |         processInvalidImages: false, | ||||||
|  |         size: undefined, | ||||||
|  |       }); | ||||||
|  | 
 | ||||||
|  |       expect(mocks.media.generateThumbnail).toHaveBeenCalledTimes(3); | ||||||
|  |       expect(mocks.media.generateThumbnail).toHaveBeenCalledWith( | ||||||
|  |         rawBuffer, | ||||||
|  |         { | ||||||
|  |           colorspace: Colorspace.Srgb, | ||||||
|  |           format: ImageFormat.Jpeg, | ||||||
|  |           quality: 80, | ||||||
|  |           processInvalidImages: false, | ||||||
|  |           raw: rawInfo, | ||||||
|  |         }, | ||||||
|  |         expect.any(String), | ||||||
|  |       ); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|     it('should respect encoding options when generating full-size preview', async () => { |     it('should respect encoding options when generating full-size preview', async () => { | ||||||
|       mocks.systemMetadata.get.mockResolvedValue({ |       mocks.systemMetadata.get.mockResolvedValue({ | ||||||
|         image: { fullsize: { enabled: true, format: ImageFormat.Webp, quality: 90 } }, |         image: { fullsize: { enabled: true, format: ImageFormat.Webp, quality: 90 } }, | ||||||
|  | |||||||
| @ -271,7 +271,9 @@ export class MediaService extends BaseService { | |||||||
|     // Handle embedded preview extraction for RAW files
 |     // Handle embedded preview extraction for RAW files
 | ||||||
|     const extractEmbedded = image.extractEmbedded && mimeTypes.isRaw(asset.originalFileName); |     const extractEmbedded = image.extractEmbedded && mimeTypes.isRaw(asset.originalFileName); | ||||||
|     const extracted = extractEmbedded ? await this.extractImage(asset.originalPath, image.preview.size) : null; |     const extracted = extractEmbedded ? await this.extractImage(asset.originalPath, image.preview.size) : null; | ||||||
|     const generateFullsize = image.fullsize.enabled && !mimeTypes.isWebSupportedImage(asset.originalPath); |     const generateFullsize = | ||||||
|  |       (image.fullsize.enabled || asset.exifInfo.projectionType == 'EQUIRECTANGULAR') && | ||||||
|  |       !mimeTypes.isWebSupportedImage(asset.originalPath); | ||||||
|     const convertFullsize = generateFullsize && (!extracted || !mimeTypes.isWebSupportedImage(` .${extracted.format}`)); |     const convertFullsize = generateFullsize && (!extracted || !mimeTypes.isWebSupportedImage(` .${extracted.format}`)); | ||||||
| 
 | 
 | ||||||
|     const { info, data, colorspace } = await this.decodeImage( |     const { info, data, colorspace } = await this.decodeImage( | ||||||
|  | |||||||
							
								
								
									
										39
									
								
								server/test/fixtures/asset.stub.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										39
									
								
								server/test/fixtures/asset.stub.ts
									
									
									
									
										vendored
									
									
								
							| @ -866,4 +866,43 @@ export const assetStub = { | |||||||
|     stackId: null, |     stackId: null, | ||||||
|     visibility: AssetVisibility.Timeline, |     visibility: AssetVisibility.Timeline, | ||||||
|   }), |   }), | ||||||
|  |   panoramaTif: Object.freeze({ | ||||||
|  |     id: 'asset-id', | ||||||
|  |     status: AssetStatus.Active, | ||||||
|  |     deviceAssetId: 'device-asset-id', | ||||||
|  |     fileModifiedAt: new Date('2023-02-23T05:06:29.716Z'), | ||||||
|  |     fileCreatedAt: new Date('2023-02-23T05:06:29.716Z'), | ||||||
|  |     owner: userStub.user1, | ||||||
|  |     ownerId: 'user-id', | ||||||
|  |     deviceId: 'device-id', | ||||||
|  |     originalPath: '/original/path.tif', | ||||||
|  |     checksum: Buffer.from('file hash', 'utf8'), | ||||||
|  |     type: AssetType.Image, | ||||||
|  |     files, | ||||||
|  |     thumbhash: Buffer.from('blablabla', 'base64'), | ||||||
|  |     encodedVideoPath: null, | ||||||
|  |     createdAt: new Date('2023-02-23T05:06:29.716Z'), | ||||||
|  |     updatedAt: new Date('2023-02-23T05:06:29.716Z'), | ||||||
|  |     localDateTime: new Date('2023-02-23T05:06:29.716Z'), | ||||||
|  |     isFavorite: true, | ||||||
|  |     duration: null, | ||||||
|  |     isExternal: false, | ||||||
|  |     livePhotoVideo: null, | ||||||
|  |     livePhotoVideoId: null, | ||||||
|  |     sharedLinks: [], | ||||||
|  |     originalFileName: 'asset-id.tif', | ||||||
|  |     faces: [], | ||||||
|  |     deletedAt: null, | ||||||
|  |     sidecarPath: null, | ||||||
|  |     exifInfo: { | ||||||
|  |       fileSizeInByte: 5000, | ||||||
|  |       projectionType: 'EQUIRECTANGULAR', | ||||||
|  |     } as Exif, | ||||||
|  |     duplicateId: null, | ||||||
|  |     isOffline: false, | ||||||
|  |     updateId: '42', | ||||||
|  |     libraryId: null, | ||||||
|  |     stackId: null, | ||||||
|  |     visibility: AssetVisibility.Timeline, | ||||||
|  |   }), | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| <script lang="ts"> | <script lang="ts"> | ||||||
|   import { authManager } from '$lib/managers/auth-manager.svelte'; |   import { authManager } from '$lib/managers/auth-manager.svelte'; | ||||||
|   import { getAssetOriginalUrl } from '$lib/utils'; |   import { getAssetOriginalUrl, getAssetThumbnailUrl } from '$lib/utils'; | ||||||
|   import { isWebCompatibleImage } from '$lib/utils/asset-utils'; |   import { isWebCompatibleImage } from '$lib/utils/asset-utils'; | ||||||
|   import { AssetMediaSize, viewAsset, type AssetResponseDto } from '@immich/sdk'; |   import { AssetMediaSize, viewAsset, type AssetResponseDto } from '@immich/sdk'; | ||||||
|   import { LoadingSpinner } from '@immich/ui'; |   import { LoadingSpinner } from '@immich/ui'; | ||||||
| @ -25,7 +25,9 @@ | |||||||
|   {:then [data, { default: PhotoSphereViewer }]} |   {:then [data, { default: PhotoSphereViewer }]} | ||||||
|     <PhotoSphereViewer |     <PhotoSphereViewer | ||||||
|       panorama={data} |       panorama={data} | ||||||
|       originalPanorama={isWebCompatibleImage(asset) ? getAssetOriginalUrl(asset.id) : undefined} |       originalPanorama={isWebCompatibleImage(asset) | ||||||
|  |         ? getAssetOriginalUrl(asset.id) | ||||||
|  |         : getAssetThumbnailUrl({ id: asset.id, size: AssetMediaSize.Fullsize, cacheKey: asset.thumbhash })} | ||||||
|     /> |     /> | ||||||
|   {:catch} |   {:catch} | ||||||
|     {$t('errors.failed_to_load_asset')} |     {$t('errors.failed_to_load_asset')} | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user