mirror of
https://github.com/immich-app/immich.git
synced 2025-10-31 18:47:09 -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