diff --git a/mobile/openapi/doc/GetAssetByTimeBucketDto.md b/mobile/openapi/doc/GetAssetByTimeBucketDto.md index b0f7212293..da314c71fc 100644 Binary files a/mobile/openapi/doc/GetAssetByTimeBucketDto.md and b/mobile/openapi/doc/GetAssetByTimeBucketDto.md differ diff --git a/mobile/openapi/lib/model/get_asset_by_time_bucket_dto.dart b/mobile/openapi/lib/model/get_asset_by_time_bucket_dto.dart index 10210c2e6b..8301132ac5 100644 Binary files a/mobile/openapi/lib/model/get_asset_by_time_bucket_dto.dart and b/mobile/openapi/lib/model/get_asset_by_time_bucket_dto.dart differ diff --git a/mobile/openapi/test/get_asset_by_time_bucket_dto_test.dart b/mobile/openapi/test/get_asset_by_time_bucket_dto_test.dart index 591f461497..e6021df73c 100644 Binary files a/mobile/openapi/test/get_asset_by_time_bucket_dto_test.dart and b/mobile/openapi/test/get_asset_by_time_bucket_dto_test.dart differ diff --git a/server/apps/immich/src/api-v1/asset/asset-repository.ts b/server/apps/immich/src/api-v1/asset/asset-repository.ts index cf033c783a..982e5c9f53 100644 --- a/server/apps/immich/src/api-v1/asset/asset-repository.ts +++ b/server/apps/immich/src/api-v1/asset/asset-repository.ts @@ -104,19 +104,23 @@ export class AssetRepository implements IAssetRepository { return this.getAssetCount(items); } - async getAssetByTimeBucket(userId: string, getAssetByTimeBucketDto: GetAssetByTimeBucketDto): Promise { + async getAssetByTimeBucket(userId: string, dto: GetAssetByTimeBucketDto): Promise { // Get asset entity from a list of time buckets - return await this.assetRepository + let builder = this.assetRepository .createQueryBuilder('asset') .where('asset.ownerId = :userId', { userId: userId }) .andWhere(`date_trunc('month', "fileCreatedAt") IN (:...buckets)`, { - buckets: [...getAssetByTimeBucketDto.timeBucket], + buckets: [...dto.timeBucket], }) - .andWhere('asset.resizePath is not NULL') .andWhere('asset.isVisible = true') .andWhere('asset.isArchived = false') - .orderBy('asset.fileCreatedAt', 'DESC') - .getMany(); + .orderBy('asset.fileCreatedAt', 'DESC'); + + if (!dto.withoutThumbs) { + builder = builder.andWhere('asset.resizePath is not NULL'); + } + + return builder.getMany(); } async getAssetCountByTimeBucket(userId: string, timeBucket: TimeGroupEnum) { diff --git a/server/apps/immich/src/api-v1/asset/dto/get-asset-by-time-bucket.dto.ts b/server/apps/immich/src/api-v1/asset/dto/get-asset-by-time-bucket.dto.ts index 6203c3e04e..ad846751c6 100644 --- a/server/apps/immich/src/api-v1/asset/dto/get-asset-by-time-bucket.dto.ts +++ b/server/apps/immich/src/api-v1/asset/dto/get-asset-by-time-bucket.dto.ts @@ -1,5 +1,7 @@ import { ApiProperty } from '@nestjs/swagger'; -import { IsNotEmpty, IsOptional, IsUUID } from 'class-validator'; +import { Transform } from 'class-transformer'; +import { IsBoolean, IsNotEmpty, IsOptional, IsUUID } from 'class-validator'; +import { toBoolean } from '../../../utils/transform.util'; export class GetAssetByTimeBucketDto { @IsNotEmpty() @@ -15,4 +17,12 @@ export class GetAssetByTimeBucketDto { @IsUUID('4') @ApiProperty({ format: 'uuid' }) userId?: string; + + /** + * Include assets without thumbnails + */ + @IsOptional() + @IsBoolean() + @Transform(toBoolean) + withoutThumbs?: boolean; } diff --git a/server/immich-openapi-specs.json b/server/immich-openapi-specs.json index 64a586d23c..225a2e9878 100644 --- a/server/immich-openapi-specs.json +++ b/server/immich-openapi-specs.json @@ -5988,6 +5988,10 @@ "userId": { "type": "string", "format": "uuid" + }, + "withoutThumbs": { + "type": "boolean", + "description": "Include assets without thumbnails" } }, "required": [ diff --git a/web/src/api/open-api/api.ts b/web/src/api/open-api/api.ts index 17e681ebaf..cc3e37ee77 100644 --- a/web/src/api/open-api/api.ts +++ b/web/src/api/open-api/api.ts @@ -1330,6 +1330,12 @@ export interface GetAssetByTimeBucketDto { * @memberof GetAssetByTimeBucketDto */ 'userId'?: string; + /** + * Include assets without thumbnails + * @type {boolean} + * @memberof GetAssetByTimeBucketDto + */ + 'withoutThumbs'?: boolean; } /** * diff --git a/web/src/lib/components/asset-viewer/asset-viewer.svelte b/web/src/lib/components/asset-viewer/asset-viewer.svelte index 4c827fca25..4f61239f63 100644 --- a/web/src/lib/components/asset-viewer/asset-viewer.svelte +++ b/web/src/lib/components/asset-viewer/asset-viewer.svelte @@ -11,6 +11,7 @@ import { createEventDispatcher, onDestroy, onMount } from 'svelte'; import ChevronLeft from 'svelte-material-icons/ChevronLeft.svelte'; import ChevronRight from 'svelte-material-icons/ChevronRight.svelte'; + import ImageBrokenVariant from 'svelte-material-icons/ImageBrokenVariant.svelte'; import { fly } from 'svelte/transition'; import AlbumSelectionModal from '../shared-components/album-selection-modal.svelte'; import { @@ -350,7 +351,15 @@
{#key asset.id} - {#if asset.type === AssetTypeEnum.Image} + {#if !asset.resizePath} +
+
+ +
+
+ {:else if asset.type === AssetTypeEnum.Image} {#if shouldPlayMotionPhoto && asset.livePhotoVideoId}
@@ -121,12 +122,19 @@
{/if} - + + {#if asset.resizePath} + + {:else} +
+ +
+ {/if} {#if asset.type === AssetTypeEnum.Video}
diff --git a/web/src/lib/stores/assets.store.ts b/web/src/lib/stores/assets.store.ts index 61023c64c2..cf8865fc07 100644 --- a/web/src/lib/stores/assets.store.ts +++ b/web/src/lib/stores/assets.store.ts @@ -67,7 +67,8 @@ function createAssetStore() { const { data: assets } = await api.assetApi.getAssetByTimeBucket( { timeBucket: [bucket], - userId: _assetGridState.userId + userId: _assetGridState.userId, + withoutThumbs: true }, { signal: currentBucketData?.cancelToken.signal } );