1
0
Fork 0
mirror of https://github.com/immich-app/immich.git synced 2024-12-29 15:11:58 +00:00

fix: remove asset.resized (#11983)

fix: remove resized
This commit is contained in:
Jason Rasmussen 2024-08-22 23:24:49 -04:00 committed by GitHub
parent f69ce6ad8a
commit 7fbf50a75e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 77 additions and 127 deletions

View file

@ -843,7 +843,6 @@ describe('/asset', () => {
expected: { expected: {
type: AssetTypeEnum.Image, type: AssetTypeEnum.Image,
originalFileName: '8bit-sRGB.avif', originalFileName: '8bit-sRGB.avif',
resized: true,
exifInfo: { exifInfo: {
description: '', description: '',
exifImageHeight: 1080, exifImageHeight: 1080,
@ -859,7 +858,6 @@ describe('/asset', () => {
expected: { expected: {
type: AssetTypeEnum.Image, type: AssetTypeEnum.Image,
originalFileName: 'el_torcal_rocks.jpg', originalFileName: 'el_torcal_rocks.jpg',
resized: true,
exifInfo: { exifInfo: {
dateTimeOriginal: '2012-08-05T11:39:59.000Z', dateTimeOriginal: '2012-08-05T11:39:59.000Z',
exifImageWidth: 512, exifImageWidth: 512,
@ -883,7 +881,6 @@ describe('/asset', () => {
expected: { expected: {
type: AssetTypeEnum.Image, type: AssetTypeEnum.Image,
originalFileName: '8bit-sRGB.jxl', originalFileName: '8bit-sRGB.jxl',
resized: true,
exifInfo: { exifInfo: {
description: '', description: '',
exifImageHeight: 1080, exifImageHeight: 1080,
@ -899,7 +896,6 @@ describe('/asset', () => {
expected: { expected: {
type: AssetTypeEnum.Image, type: AssetTypeEnum.Image,
originalFileName: 'IMG_2682.heic', originalFileName: 'IMG_2682.heic',
resized: true,
fileCreatedAt: '2019-03-21T16:04:22.348Z', fileCreatedAt: '2019-03-21T16:04:22.348Z',
exifInfo: { exifInfo: {
dateTimeOriginal: '2019-03-21T16:04:22.348Z', dateTimeOriginal: '2019-03-21T16:04:22.348Z',
@ -924,7 +920,6 @@ describe('/asset', () => {
expected: { expected: {
type: AssetTypeEnum.Image, type: AssetTypeEnum.Image,
originalFileName: 'density_plot.png', originalFileName: 'density_plot.png',
resized: true,
exifInfo: { exifInfo: {
exifImageWidth: 800, exifImageWidth: 800,
exifImageHeight: 800, exifImageHeight: 800,
@ -939,7 +934,6 @@ describe('/asset', () => {
expected: { expected: {
type: AssetTypeEnum.Image, type: AssetTypeEnum.Image,
originalFileName: 'glarus.nef', originalFileName: 'glarus.nef',
resized: true,
fileCreatedAt: '2010-07-20T17:27:12.000Z', fileCreatedAt: '2010-07-20T17:27:12.000Z',
exifInfo: { exifInfo: {
make: 'NIKON CORPORATION', make: 'NIKON CORPORATION',
@ -961,7 +955,6 @@ describe('/asset', () => {
expected: { expected: {
type: AssetTypeEnum.Image, type: AssetTypeEnum.Image,
originalFileName: 'philadelphia.nef', originalFileName: 'philadelphia.nef',
resized: true,
fileCreatedAt: '2016-09-22T22:10:29.060Z', fileCreatedAt: '2016-09-22T22:10:29.060Z',
exifInfo: { exifInfo: {
make: 'NIKON CORPORATION', make: 'NIKON CORPORATION',
@ -984,7 +977,6 @@ describe('/asset', () => {
expected: { expected: {
type: AssetTypeEnum.Image, type: AssetTypeEnum.Image,
originalFileName: '4_3.rw2', originalFileName: '4_3.rw2',
resized: true,
fileCreatedAt: '2018-05-10T08:42:37.842Z', fileCreatedAt: '2018-05-10T08:42:37.842Z',
exifInfo: { exifInfo: {
make: 'Panasonic', make: 'Panasonic',
@ -1008,7 +1000,6 @@ describe('/asset', () => {
expected: { expected: {
type: AssetTypeEnum.Image, type: AssetTypeEnum.Image,
originalFileName: '12bit-compressed-(3_2).arw', originalFileName: '12bit-compressed-(3_2).arw',
resized: true,
fileCreatedAt: '2016-09-27T10:51:44.000Z', fileCreatedAt: '2016-09-27T10:51:44.000Z',
exifInfo: { exifInfo: {
make: 'SONY', make: 'SONY',
@ -1033,7 +1024,6 @@ describe('/asset', () => {
expected: { expected: {
type: AssetTypeEnum.Image, type: AssetTypeEnum.Image,
originalFileName: '14bit-uncompressed-(3_2).arw', originalFileName: '14bit-uncompressed-(3_2).arw',
resized: true,
fileCreatedAt: '2016-01-08T14:08:01.000Z', fileCreatedAt: '2016-01-08T14:08:01.000Z',
exifInfo: { exifInfo: {
make: 'SONY', make: 'SONY',

Binary file not shown.

View file

@ -8335,9 +8335,6 @@
}, },
"type": "array" "type": "array"
}, },
"resized": {
"type": "boolean"
},
"smartInfo": { "smartInfo": {
"$ref": "#/components/schemas/SmartInfoResponseDto" "$ref": "#/components/schemas/SmartInfoResponseDto"
}, },
@ -8390,7 +8387,6 @@
"originalFileName", "originalFileName",
"originalPath", "originalPath",
"ownerId", "ownerId",
"resized",
"thumbhash", "thumbhash",
"type", "type",
"updatedAt" "updatedAt"

View file

@ -229,7 +229,6 @@ export type AssetResponseDto = {
owner?: UserResponseDto; owner?: UserResponseDto;
ownerId: string; ownerId: string;
people?: PersonWithFacesResponseDto[]; people?: PersonWithFacesResponseDto[];
resized: boolean;
smartInfo?: SmartInfoResponseDto; smartInfo?: SmartInfoResponseDto;
stack?: (AssetStackResponseDto) | null; stack?: (AssetStackResponseDto) | null;
tags?: TagResponseDto[]; tags?: TagResponseDto[];

View file

@ -14,7 +14,6 @@ import { AssetFaceEntity } from 'src/entities/asset-face.entity';
import { AssetEntity } from 'src/entities/asset.entity'; import { AssetEntity } from 'src/entities/asset.entity';
import { SmartInfoEntity } from 'src/entities/smart-info.entity'; import { SmartInfoEntity } from 'src/entities/smart-info.entity';
import { AssetType } from 'src/enum'; import { AssetType } from 'src/enum';
import { getAssetFiles } from 'src/utils/asset.util';
import { mimeTypes } from 'src/utils/mime-types'; import { mimeTypes } from 'src/utils/mime-types';
export class SanitizedAssetResponseDto { export class SanitizedAssetResponseDto {
@ -23,7 +22,6 @@ export class SanitizedAssetResponseDto {
type!: AssetType; type!: AssetType;
thumbhash!: string | null; thumbhash!: string | null;
originalMimeType?: string; originalMimeType?: string;
resized!: boolean;
localDateTime!: Date; localDateTime!: Date;
duration!: string; duration!: string;
livePhotoVideoId?: string | null; livePhotoVideoId?: string | null;
@ -112,7 +110,6 @@ export function mapAsset(entity: AssetEntity, options: AssetMapOptions = {}): As
originalMimeType: mimeTypes.lookup(entity.originalFileName), originalMimeType: mimeTypes.lookup(entity.originalFileName),
thumbhash: entity.thumbhash?.toString('base64') ?? null, thumbhash: entity.thumbhash?.toString('base64') ?? null,
localDateTime: entity.localDateTime, localDateTime: entity.localDateTime,
resized: !!getAssetFiles(entity.files).previewFile,
duration: entity.duration ?? '0:00:00.00000', duration: entity.duration ?? '0:00:00.00000',
livePhotoVideoId: entity.livePhotoVideoId, livePhotoVideoId: entity.livePhotoVideoId,
hasMetadata: false, hasMetadata: false,
@ -131,7 +128,6 @@ export function mapAsset(entity: AssetEntity, options: AssetMapOptions = {}): As
originalPath: entity.originalPath, originalPath: entity.originalPath,
originalFileName: entity.originalFileName, originalFileName: entity.originalFileName,
originalMimeType: mimeTypes.lookup(entity.originalFileName), originalMimeType: mimeTypes.lookup(entity.originalFileName),
resized: !!getAssetFiles(entity.files).previewFile,
thumbhash: entity.thumbhash?.toString('base64') ?? null, thumbhash: entity.thumbhash?.toString('base64') ?? null,
fileCreatedAt: entity.fileCreatedAt, fileCreatedAt: entity.fileCreatedAt,
fileModifiedAt: entity.fileModifiedAt, fileModifiedAt: entity.fileModifiedAt,

View file

@ -598,12 +598,6 @@ SELECT
"asset"."sidecarPath" AS "asset_sidecarPath", "asset"."sidecarPath" AS "asset_sidecarPath",
"asset"."stackId" AS "asset_stackId", "asset"."stackId" AS "asset_stackId",
"asset"."duplicateId" AS "asset_duplicateId", "asset"."duplicateId" AS "asset_duplicateId",
"files"."id" AS "files_id",
"files"."assetId" AS "files_assetId",
"files"."createdAt" AS "files_createdAt",
"files"."updatedAt" AS "files_updatedAt",
"files"."type" AS "files_type",
"files"."path" AS "files_path",
"exifInfo"."assetId" AS "exifInfo_assetId", "exifInfo"."assetId" AS "exifInfo_assetId",
"exifInfo"."description" AS "exifInfo_description", "exifInfo"."description" AS "exifInfo_description",
"exifInfo"."exifImageWidth" AS "exifInfo_exifImageWidth", "exifInfo"."exifImageWidth" AS "exifInfo_exifImageWidth",
@ -665,7 +659,6 @@ SELECT
"stackedAssets"."duplicateId" AS "stackedAssets_duplicateId" "stackedAssets"."duplicateId" AS "stackedAssets_duplicateId"
FROM FROM
"assets" "asset" "assets" "asset"
LEFT JOIN "asset_files" "files" ON "files"."assetId" = "asset"."id"
LEFT JOIN "exif" "exifInfo" ON "exifInfo"."assetId" = "asset"."id" LEFT JOIN "exif" "exifInfo" ON "exifInfo"."assetId" = "asset"."id"
LEFT JOIN "asset_stack" "stack" ON "stack"."id" = "asset"."stackId" LEFT JOIN "asset_stack" "stack" ON "stack"."id" = "asset"."stackId"
LEFT JOIN "assets" "stackedAssets" ON "stackedAssets"."stackId" = "stack"."id" LEFT JOIN "assets" "stackedAssets" ON "stackedAssets"."stackId" = "stack"."id"
@ -692,7 +685,6 @@ SELECT
)::timestamptz AS "timeBucket" )::timestamptz AS "timeBucket"
FROM FROM
"assets" "asset" "assets" "asset"
LEFT JOIN "asset_files" "files" ON "files"."assetId" = "asset"."id"
LEFT JOIN "exif" "exifInfo" ON "exifInfo"."assetId" = "asset"."id" LEFT JOIN "exif" "exifInfo" ON "exifInfo"."assetId" = "asset"."id"
LEFT JOIN "asset_stack" "stack" ON "stack"."id" = "asset"."stackId" LEFT JOIN "asset_stack" "stack" ON "stack"."id" = "asset"."stackId"
LEFT JOIN "assets" "stackedAssets" ON "stackedAssets"."stackId" = "stack"."id" LEFT JOIN "assets" "stackedAssets" ON "stackedAssets"."stackId" = "stack"."id"
@ -744,12 +736,6 @@ SELECT
"asset"."sidecarPath" AS "asset_sidecarPath", "asset"."sidecarPath" AS "asset_sidecarPath",
"asset"."stackId" AS "asset_stackId", "asset"."stackId" AS "asset_stackId",
"asset"."duplicateId" AS "asset_duplicateId", "asset"."duplicateId" AS "asset_duplicateId",
"files"."id" AS "files_id",
"files"."assetId" AS "files_assetId",
"files"."createdAt" AS "files_createdAt",
"files"."updatedAt" AS "files_updatedAt",
"files"."type" AS "files_type",
"files"."path" AS "files_path",
"exifInfo"."assetId" AS "exifInfo_assetId", "exifInfo"."assetId" AS "exifInfo_assetId",
"exifInfo"."description" AS "exifInfo_description", "exifInfo"."description" AS "exifInfo_description",
"exifInfo"."exifImageWidth" AS "exifInfo_exifImageWidth", "exifInfo"."exifImageWidth" AS "exifInfo_exifImageWidth",
@ -811,7 +797,6 @@ SELECT
"stackedAssets"."duplicateId" AS "stackedAssets_duplicateId" "stackedAssets"."duplicateId" AS "stackedAssets_duplicateId"
FROM FROM
"assets" "asset" "assets" "asset"
LEFT JOIN "asset_files" "files" ON "files"."assetId" = "asset"."id"
LEFT JOIN "exif" "exifInfo" ON "exifInfo"."assetId" = "asset"."id" LEFT JOIN "exif" "exifInfo" ON "exifInfo"."assetId" = "asset"."id"
LEFT JOIN "asset_stack" "stack" ON "stack"."id" = "asset"."stackId" LEFT JOIN "asset_stack" "stack" ON "stack"."id" = "asset"."stackId"
LEFT JOIN "assets" "stackedAssets" ON "stackedAssets"."stackId" = "stack"."id" LEFT JOIN "assets" "stackedAssets" ON "stackedAssets"."stackId" = "stack"."id"
@ -865,12 +850,6 @@ SELECT
"asset"."sidecarPath" AS "asset_sidecarPath", "asset"."sidecarPath" AS "asset_sidecarPath",
"asset"."stackId" AS "asset_stackId", "asset"."stackId" AS "asset_stackId",
"asset"."duplicateId" AS "asset_duplicateId", "asset"."duplicateId" AS "asset_duplicateId",
"files"."id" AS "files_id",
"files"."assetId" AS "files_assetId",
"files"."createdAt" AS "files_createdAt",
"files"."updatedAt" AS "files_updatedAt",
"files"."type" AS "files_type",
"files"."path" AS "files_path",
"exifInfo"."assetId" AS "exifInfo_assetId", "exifInfo"."assetId" AS "exifInfo_assetId",
"exifInfo"."description" AS "exifInfo_description", "exifInfo"."description" AS "exifInfo_description",
"exifInfo"."exifImageWidth" AS "exifInfo_exifImageWidth", "exifInfo"."exifImageWidth" AS "exifInfo_exifImageWidth",
@ -932,7 +911,6 @@ SELECT
"stackedAssets"."duplicateId" AS "stackedAssets_duplicateId" "stackedAssets"."duplicateId" AS "stackedAssets_duplicateId"
FROM FROM
"assets" "asset" "assets" "asset"
LEFT JOIN "asset_files" "files" ON "files"."assetId" = "asset"."id"
LEFT JOIN "exif" "exifInfo" ON "exifInfo"."assetId" = "asset"."id" LEFT JOIN "exif" "exifInfo" ON "exifInfo"."assetId" = "asset"."id"
LEFT JOIN "asset_stack" "stack" ON "stack"."id" = "asset"."stackId" LEFT JOIN "asset_stack" "stack" ON "stack"."id" = "asset"."stackId"
LEFT JOIN "assets" "stackedAssets" ON "stackedAssets"."stackId" = "stack"."id" LEFT JOIN "assets" "stackedAssets" ON "stackedAssets"."stackId" = "stack"."id"
@ -964,7 +942,6 @@ SELECT DISTINCT
c.city AS "value" c.city AS "value"
FROM FROM
"assets" "asset" "assets" "asset"
LEFT JOIN "asset_files" "files" ON "files"."assetId" = "asset"."id"
INNER JOIN "exif" "e" ON "asset"."id" = e."assetId" INNER JOIN "exif" "e" ON "asset"."id" = e."assetId"
INNER JOIN "cities" "c" ON c.city = "e"."city" INNER JOIN "cities" "c" ON c.city = "e"."city"
WHERE WHERE
@ -995,7 +972,6 @@ SELECT DISTINCT
unnest("si"."tags") AS "value" unnest("si"."tags") AS "value"
FROM FROM
"assets" "asset" "assets" "asset"
LEFT JOIN "asset_files" "files" ON "files"."assetId" = "asset"."id"
INNER JOIN "smart_info" "si" ON "asset"."id" = si."assetId" INNER JOIN "smart_info" "si" ON "asset"."id" = si."assetId"
INNER JOIN "random_tags" "t" ON "si"."tags" @> ARRAY[t.tag] INNER JOIN "random_tags" "t" ON "si"."tags" @> ARRAY[t.tag]
WHERE WHERE
@ -1038,12 +1014,6 @@ SELECT
"asset"."sidecarPath" AS "asset_sidecarPath", "asset"."sidecarPath" AS "asset_sidecarPath",
"asset"."stackId" AS "asset_stackId", "asset"."stackId" AS "asset_stackId",
"asset"."duplicateId" AS "asset_duplicateId", "asset"."duplicateId" AS "asset_duplicateId",
"files"."id" AS "files_id",
"files"."assetId" AS "files_assetId",
"files"."createdAt" AS "files_createdAt",
"files"."updatedAt" AS "files_updatedAt",
"files"."type" AS "files_type",
"files"."path" AS "files_path",
"exifInfo"."assetId" AS "exifInfo_assetId", "exifInfo"."assetId" AS "exifInfo_assetId",
"exifInfo"."description" AS "exifInfo_description", "exifInfo"."description" AS "exifInfo_description",
"exifInfo"."exifImageWidth" AS "exifInfo_exifImageWidth", "exifInfo"."exifImageWidth" AS "exifInfo_exifImageWidth",
@ -1078,7 +1048,6 @@ SELECT
"stack"."primaryAssetId" AS "stack_primaryAssetId" "stack"."primaryAssetId" AS "stack_primaryAssetId"
FROM FROM
"assets" "asset" "assets" "asset"
LEFT JOIN "asset_files" "files" ON "files"."assetId" = "asset"."id"
LEFT JOIN "exif" "exifInfo" ON "exifInfo"."assetId" = "asset"."id" LEFT JOIN "exif" "exifInfo" ON "exifInfo"."assetId" = "asset"."id"
LEFT JOIN "asset_stack" "stack" ON "stack"."id" = "asset"."stackId" LEFT JOIN "asset_stack" "stack" ON "stack"."id" = "asset"."stackId"
WHERE WHERE
@ -1120,12 +1089,6 @@ SELECT
"asset"."sidecarPath" AS "asset_sidecarPath", "asset"."sidecarPath" AS "asset_sidecarPath",
"asset"."stackId" AS "asset_stackId", "asset"."stackId" AS "asset_stackId",
"asset"."duplicateId" AS "asset_duplicateId", "asset"."duplicateId" AS "asset_duplicateId",
"files"."id" AS "files_id",
"files"."assetId" AS "files_assetId",
"files"."createdAt" AS "files_createdAt",
"files"."updatedAt" AS "files_updatedAt",
"files"."type" AS "files_type",
"files"."path" AS "files_path",
"exifInfo"."assetId" AS "exifInfo_assetId", "exifInfo"."assetId" AS "exifInfo_assetId",
"exifInfo"."description" AS "exifInfo_description", "exifInfo"."description" AS "exifInfo_description",
"exifInfo"."exifImageWidth" AS "exifInfo_exifImageWidth", "exifInfo"."exifImageWidth" AS "exifInfo_exifImageWidth",
@ -1160,7 +1123,6 @@ SELECT
"stack"."primaryAssetId" AS "stack_primaryAssetId" "stack"."primaryAssetId" AS "stack_primaryAssetId"
FROM FROM
"assets" "asset" "assets" "asset"
LEFT JOIN "asset_files" "files" ON "files"."assetId" = "asset"."id"
LEFT JOIN "exif" "exifInfo" ON "exifInfo"."assetId" = "asset"."id" LEFT JOIN "exif" "exifInfo" ON "exifInfo"."assetId" = "asset"."id"
LEFT JOIN "asset_stack" "stack" ON "stack"."id" = "asset"."stackId" LEFT JOIN "asset_stack" "stack" ON "stack"."id" = "asset"."stackId"
WHERE WHERE
@ -1197,12 +1159,6 @@ SELECT
"asset"."sidecarPath" AS "asset_sidecarPath", "asset"."sidecarPath" AS "asset_sidecarPath",
"asset"."stackId" AS "asset_stackId", "asset"."stackId" AS "asset_stackId",
"asset"."duplicateId" AS "asset_duplicateId", "asset"."duplicateId" AS "asset_duplicateId",
"files"."id" AS "files_id",
"files"."assetId" AS "files_assetId",
"files"."createdAt" AS "files_createdAt",
"files"."updatedAt" AS "files_updatedAt",
"files"."type" AS "files_type",
"files"."path" AS "files_path",
"exifInfo"."assetId" AS "exifInfo_assetId", "exifInfo"."assetId" AS "exifInfo_assetId",
"exifInfo"."description" AS "exifInfo_description", "exifInfo"."description" AS "exifInfo_description",
"exifInfo"."exifImageWidth" AS "exifInfo_exifImageWidth", "exifInfo"."exifImageWidth" AS "exifInfo_exifImageWidth",
@ -1264,7 +1220,6 @@ SELECT
"stackedAssets"."duplicateId" AS "stackedAssets_duplicateId" "stackedAssets"."duplicateId" AS "stackedAssets_duplicateId"
FROM FROM
"assets" "asset" "assets" "asset"
LEFT JOIN "asset_files" "files" ON "files"."assetId" = "asset"."id"
LEFT JOIN "exif" "exifInfo" ON "exifInfo"."assetId" = "asset"."id" LEFT JOIN "exif" "exifInfo" ON "exifInfo"."assetId" = "asset"."id"
LEFT JOIN "asset_stack" "stack" ON "stack"."id" = "asset"."stackId" LEFT JOIN "asset_stack" "stack" ON "stack"."id" = "asset"."stackId"
LEFT JOIN "assets" "stackedAssets" ON "stackedAssets"."stackId" = "stack"."id" LEFT JOIN "assets" "stackedAssets" ON "stackedAssets"."stackId" = "stack"."id"

View file

@ -710,10 +710,7 @@ export class AssetRepository implements IAssetRepository {
} }
private getBuilder(options: AssetBuilderOptions) { private getBuilder(options: AssetBuilderOptions) {
const builder = this.repository const builder = this.repository.createQueryBuilder('asset').where('asset.isVisible = true');
.createQueryBuilder('asset')
.where('asset.isVisible = true')
.leftJoinAndSelect('asset.files', 'files');
if (options.assetType !== undefined) { if (options.assetType !== undefined) {
builder.andWhere('asset.type = :assetType', { assetType: options.assetType }); builder.andWhere('asset.type = :assetType', { assetType: options.assetType });

View file

@ -54,7 +54,6 @@ const assetResponse: AssetResponseDto = {
originalMimeType: 'image/jpeg', originalMimeType: 'image/jpeg',
originalPath: 'fake_path/jpeg', originalPath: 'fake_path/jpeg',
originalFileName: 'asset_1.jpeg', originalFileName: 'asset_1.jpeg',
resized: false,
thumbhash: null, thumbhash: null,
fileModifiedAt: today, fileModifiedAt: today,
isOffline: false, isOffline: false,
@ -82,7 +81,6 @@ const assetResponseWithoutMetadata = {
id: 'id_1', id: 'id_1',
type: AssetType.VIDEO, type: AssetType.VIDEO,
originalMimeType: 'image/jpeg', originalMimeType: 'image/jpeg',
resized: false,
thumbhash: null, thumbhash: null,
localDateTime: today, localDateTime: today,
duration: '0:00:00.00000', duration: '0:00:00.00000',

View file

@ -4,9 +4,9 @@
import MotionPhotoAction from '$lib/components/asset-viewer/actions/motion-photo-action.svelte'; import MotionPhotoAction from '$lib/components/asset-viewer/actions/motion-photo-action.svelte';
import NextAssetAction from '$lib/components/asset-viewer/actions/next-asset-action.svelte'; import NextAssetAction from '$lib/components/asset-viewer/actions/next-asset-action.svelte';
import PreviousAssetAction from '$lib/components/asset-viewer/actions/previous-asset-action.svelte'; import PreviousAssetAction from '$lib/components/asset-viewer/actions/previous-asset-action.svelte';
import Icon from '$lib/components/elements/icon.svelte';
import { AssetAction, ProjectionType } from '$lib/constants'; import { AssetAction, ProjectionType } from '$lib/constants';
import { updateNumberOfComments } from '$lib/stores/activity.store'; import { updateNumberOfComments } from '$lib/stores/activity.store';
import { closeEditorCofirm } from '$lib/stores/asset-editor.store';
import { assetViewingStore } from '$lib/stores/asset-viewing.store'; import { assetViewingStore } from '$lib/stores/asset-viewing.store';
import type { AssetStore } from '$lib/stores/assets.store'; import type { AssetStore } from '$lib/stores/assets.store';
import { isShowDetail } from '$lib/stores/preferences.store'; import { isShowDetail } from '$lib/stores/preferences.store';
@ -25,14 +25,13 @@
getActivities, getActivities,
getActivityStatistics, getActivityStatistics,
getAllAlbums, getAllAlbums,
getStack,
runAssetJobs, runAssetJobs,
type ActivityResponseDto, type ActivityResponseDto,
type AlbumResponseDto, type AlbumResponseDto,
type AssetResponseDto, type AssetResponseDto,
getStack,
type StackResponseDto, type StackResponseDto,
} from '@immich/sdk'; } from '@immich/sdk';
import { mdiImageBrokenVariant } from '@mdi/js';
import { createEventDispatcher, onDestroy, onMount } from 'svelte'; import { createEventDispatcher, onDestroy, onMount } from 'svelte';
import { t } from 'svelte-i18n'; import { t } from 'svelte-i18n';
import { fly } from 'svelte/transition'; import { fly } from 'svelte/transition';
@ -42,13 +41,13 @@
import ActivityViewer from './activity-viewer.svelte'; import ActivityViewer from './activity-viewer.svelte';
import AssetViewerNavBar from './asset-viewer-nav-bar.svelte'; import AssetViewerNavBar from './asset-viewer-nav-bar.svelte';
import DetailPanel from './detail-panel.svelte'; import DetailPanel from './detail-panel.svelte';
import CropArea from './editor/crop-tool/crop-area.svelte';
import EditorPanel from './editor/editor-panel.svelte';
import PanoramaViewer from './panorama-viewer.svelte'; import PanoramaViewer from './panorama-viewer.svelte';
import PhotoViewer from './photo-viewer.svelte'; import PhotoViewer from './photo-viewer.svelte';
import SlideshowBar from './slideshow-bar.svelte'; import SlideshowBar from './slideshow-bar.svelte';
import VideoViewer from './video-wrapper-viewer.svelte'; import VideoViewer from './video-wrapper-viewer.svelte';
import EditorPanel from './editor/editor-panel.svelte';
import CropArea from './editor/crop-tool/crop-area.svelte';
import { closeEditorCofirm } from '$lib/stores/asset-editor.store';
export let assetStore: AssetStore | null = null; export let assetStore: AssetStore | null = null;
export let asset: AssetResponseDto; export let asset: AssetResponseDto;
export let preloadAssets: AssetResponseDto[] = []; export let preloadAssets: AssetResponseDto[] = [];
@ -481,15 +480,7 @@
{/key} {/key}
{:else} {:else}
{#key asset.id} {#key asset.id}
{#if !asset.resized} {#if asset.type === AssetTypeEnum.Image}
<div class="flex h-full w-full justify-center">
<div
class="px-auto flex aspect-square h-full items-center justify-center bg-gray-100 dark:bg-immich-dark-gray"
>
<Icon path={mdiImageBrokenVariant} size="25%" />
</div>
</div>
{:else if asset.type === AssetTypeEnum.Image}
{#if shouldPlayMotionPhoto && asset.livePhotoVideoId} {#if shouldPlayMotionPhoto && asset.livePhotoVideoId}
<VideoViewer <VideoViewer
assetId={asset.livePhotoVideoId} assetId={asset.livePhotoVideoId}

View file

@ -1,5 +1,7 @@
<script lang="ts"> <script lang="ts">
import { shortcuts } from '$lib/actions/shortcut'; import { shortcuts } from '$lib/actions/shortcut';
import { zoomImageAction, zoomed } from '$lib/actions/zoom-image';
import BrokenAsset from '$lib/components/assets/broken-asset.svelte';
import { photoViewer } from '$lib/stores/assets.store'; import { photoViewer } from '$lib/stores/assets.store';
import { boundingBoxesArray } from '$lib/stores/people.store'; import { boundingBoxesArray } from '$lib/stores/people.store';
import { alwaysLoadOriginalFile } from '$lib/stores/preferences.store'; import { alwaysLoadOriginalFile } from '$lib/stores/preferences.store';
@ -9,15 +11,13 @@
import { isWebCompatibleImage } from '$lib/utils/asset-utils'; import { isWebCompatibleImage } from '$lib/utils/asset-utils';
import { getBoundingBox } from '$lib/utils/people-utils'; import { getBoundingBox } from '$lib/utils/people-utils';
import { getAltText } from '$lib/utils/thumbnail-util'; import { getAltText } from '$lib/utils/thumbnail-util';
import { AssetTypeEnum, type AssetResponseDto, AssetMediaSize, type SharedLinkResponseDto } from '@immich/sdk'; import { AssetMediaSize, AssetTypeEnum, type AssetResponseDto, type SharedLinkResponseDto } from '@immich/sdk';
import { zoomImageAction, zoomed } from '$lib/actions/zoom-image';
import { canCopyImagesToClipboard, copyImageToClipboard } from 'copy-image-clipboard'; import { canCopyImagesToClipboard, copyImageToClipboard } from 'copy-image-clipboard';
import { onDestroy, onMount } from 'svelte'; import { onDestroy, onMount } from 'svelte';
import { t } from 'svelte-i18n';
import { fade } from 'svelte/transition'; import { fade } from 'svelte/transition';
import LoadingSpinner from '../shared-components/loading-spinner.svelte'; import LoadingSpinner from '../shared-components/loading-spinner.svelte';
import { NotificationType, notificationController } from '../shared-components/notification/notification'; import { NotificationType, notificationController } from '../shared-components/notification/notification';
import { t } from 'svelte-i18n';
export let asset: AssetResponseDto; export let asset: AssetResponseDto;
export let preloadAssets: AssetResponseDto[] | undefined = undefined; export let preloadAssets: AssetResponseDto[] | undefined = undefined;
@ -137,7 +137,7 @@
]} ]}
/> />
{#if imageError} {#if imageError}
<div class="h-full flex items-center justify-center">{$t('error_loading_image')}</div> <BrokenAsset square />
{/if} {/if}
<!-- svelte-ignore a11y-missing-attribute --> <!-- svelte-ignore a11y-missing-attribute -->
<img bind:this={loader} style="display:none" src={imageLoaderUrl} aria-hidden="true" /> <img bind:this={loader} style="display:none" src={imageLoaderUrl} aria-hidden="true" />

View file

@ -0,0 +1,25 @@
<script lang="ts">
import Icon from '$lib/components/elements/icon.svelte';
import { mdiImageBrokenVariant } from '@mdi/js';
import { t } from 'svelte-i18n';
export let square = false;
export let noMessage = false;
</script>
<section class="flex h-full w-full justify-center">
<div
class="px-auto flex flex-col {square
? 'aspect-square'
: 'w-full'} h-full items-center justify-center bg-gray-100 dark:text-gray-100 dark:bg-immich-dark-gray"
>
<slot name="message">
{#if !noMessage}
<div class="text-lg absolute top-2/3">{$t('error_loading_image')}</div>
{/if}
</slot>
<div class="flex h-full w-full items-center justify-center p-4 bg-gray-100 dark:text-slate-200 dark:bg-gray-700">
<Icon path={mdiImageBrokenVariant} size="33%" />
</div>
</div>
</section>

View file

@ -1,12 +1,12 @@
<script lang="ts"> <script lang="ts">
import { onMount } from 'svelte';
import { fade } from 'svelte/transition';
import { thumbhash } from '$lib/actions/thumbhash'; import { thumbhash } from '$lib/actions/thumbhash';
import BrokenAsset from '$lib/components/assets/broken-asset.svelte';
import Icon from '$lib/components/elements/icon.svelte'; import Icon from '$lib/components/elements/icon.svelte';
import { TUNABLES } from '$lib/utils/tunables'; import { TUNABLES } from '$lib/utils/tunables';
import { mdiEyeOffOutline, mdiImageBrokenVariant } from '@mdi/js'; import { mdiEyeOffOutline } from '@mdi/js';
import { onMount } from 'svelte';
import { t } from 'svelte-i18n';
import { fade } from 'svelte/transition';
export let url: string; export let url: string;
export let altText: string | undefined; export let altText: string | undefined;
@ -48,9 +48,9 @@
</script> </script>
{#if errored} {#if errored}
<div class="absolute flex h-full w-full items-center justify-center p-4 z-10"> <BrokenAsset>
<Icon path={mdiImageBrokenVariant} size="48" /> <div slot="message" class="absolute top-2/3">{$t('error_loading_image')}</div>
</div> </BrokenAsset>
{:else} {:else}
<img <img
bind:this={img} bind:this={img}

View file

@ -13,7 +13,6 @@
mdiCameraBurst, mdiCameraBurst,
mdiCheckCircle, mdiCheckCircle,
mdiHeart, mdiHeart,
mdiImageBrokenVariant,
mdiMotionPauseOutline, mdiMotionPauseOutline,
mdiMotionPlayOutline, mdiMotionPlayOutline,
mdiRotate360, mdiRotate360,
@ -297,20 +296,14 @@
</div> </div>
{/if} {/if}
{#if asset.resized} <ImageThumbnail
<ImageThumbnail url={getAssetThumbnailUrl({ id: asset.id, size: AssetMediaSize.Thumbnail, checksum: asset.checksum })}
url={getAssetThumbnailUrl({ id: asset.id, size: AssetMediaSize.Thumbnail, checksum: asset.checksum })} altText={$getAltText(asset)}
altText={$getAltText(asset)} widthStyle="{width}px"
widthStyle="{width}px" heightStyle="{height}px"
heightStyle="{height}px" curve={selected}
curve={selected} onComplete={() => (loaded = true)}
onComplete={() => (loaded = true)} />
/>
{:else}
<div class="absolute flex h-full w-full items-center justify-center p-4 z-10">
<Icon path={mdiImageBrokenVariant} size="48" />
</div>
{/if}
{#if asset.type === AssetTypeEnum.Video} {#if asset.type === AssetTypeEnum.Video}
<div class="absolute top-0 h-full w-full"> <div class="absolute top-0 h-full w-full">

View file

@ -47,13 +47,15 @@ describe('ShareCover component', () => {
expect(img.className).toBe('z-0 rounded-xl object-cover aspect-square text'); expect(img.className).toBe('z-0 rounded-xl object-cover aspect-square text');
}); });
it('renders fallback image when asset is not resized', () => { it.skip('renders fallback image when asset is not resized', () => {
const link = sharedLinkFactory.build({ assets: [assetFactory.build({ resized: false })] }); const link = sharedLinkFactory.build({ assets: [assetFactory.build()] });
render(ShareCover, { render(ShareCover, {
link: link, link: link,
preload: false, preload: false,
}); });
// TODO emit image error event and check if fallback image is rendered
const img = screen.getByTestId<HTMLImageElement>('album-image'); const img = screen.getByTestId<HTMLImageElement>('album-image');
expect(img.alt).toBe('unnamed_share'); expect(img.alt).toBe('unnamed_share');
}); });

View file

@ -1,16 +1,25 @@
<script lang="ts"> <script lang="ts">
import BrokenAsset from '$lib/components/assets/broken-asset.svelte';
export let alt; export let alt;
export let preload = false; export let preload = false;
export let src: string; export let src: string;
let className = ''; let className = '';
export { className as class }; export { className as class };
let isBroken = false;
</script> </script>
<img {#if isBroken}
{alt} <BrokenAsset noMessage />
class="z-0 rounded-xl object-cover aspect-square {className}" {:else}
data-testid="album-image" <img
draggable="false" {alt}
loading={preload ? 'eager' : 'lazy'} on:error={() => (isBroken = true)}
{src} class="z-0 rounded-xl object-cover aspect-square {className}"
/> data-testid="album-image"
draggable="false"
loading={preload ? 'eager' : 'lazy'}
{src}
/>
{/if}

View file

@ -12,10 +12,10 @@
export { className as class }; export { className as class };
</script> </script>
<div class="relative shrink-0"> <div class="relative shrink-0 size-24">
{#if link?.album} {#if link?.album}
<AlbumCover album={link.album} class={className} {preload} /> <AlbumCover album={link.album} class={className} {preload} />
{:else if link.assets[0]?.resized} {:else if link.assets[0]}
<AssetCover <AssetCover
alt={$t('individual_share')} alt={$t('individual_share')}
class={className} class={className}

View file

@ -41,7 +41,7 @@
href={isExpired ? undefined : `${AppRoute.SHARE}/${link.key}`} href={isExpired ? undefined : `${AppRoute.SHARE}/${link.key}`}
class="flex gap-4 w-full py-4" class="flex gap-4 w-full py-4"
> >
<ShareCover class="size-24 transition-all duration-300 hover:shadow-lg" {link} /> <ShareCover class="transition-all duration-300 hover:shadow-lg" {link} />
<div class="flex flex-col justify-between"> <div class="flex flex-col justify-between">
<div class="info-top"> <div class="info-top">

View file

@ -12,7 +12,6 @@ export const assetFactory = Sync.makeFactory<AssetResponseDto>({
originalPath: Sync.each(() => faker.system.filePath()), originalPath: Sync.each(() => faker.system.filePath()),
originalFileName: Sync.each(() => faker.system.fileName()), originalFileName: Sync.each(() => faker.system.fileName()),
originalMimeType: Sync.each(() => faker.system.mimeType()), originalMimeType: Sync.each(() => faker.system.mimeType()),
resized: true,
thumbhash: Sync.each(() => faker.string.alphanumeric(28)), thumbhash: Sync.each(() => faker.string.alphanumeric(28)),
fileCreatedAt: Sync.each(() => faker.date.past().toISOString()), fileCreatedAt: Sync.each(() => faker.date.past().toISOString()),
fileModifiedAt: Sync.each(() => faker.date.past().toISOString()), fileModifiedAt: Sync.each(() => faker.date.past().toISOString()),