From 227ab4d8a2022887c8a0655602b5f9ed08fb133d Mon Sep 17 00:00:00 2001 From: Jonathan Jogenfors Date: Mon, 27 Jan 2025 23:53:15 +0100 Subject: [PATCH] wip --- server/src/dtos/asset-response.dto.ts | 8 ++++++ server/src/entities/asset.entity.ts | 5 +++- server/src/interfaces/search.interface.ts | 3 ++ server/src/queries/access.repository.sql | 12 ++++++++ server/src/queries/activity.repository.sql | 6 ++++ server/src/queries/album.repository.sql | 6 ++++ server/src/queries/asset.repository.sql | 8 ++++-- server/src/queries/memory.repository.sql | 6 ++++ server/src/queries/person.repository.sql | 6 ++++ server/src/queries/search.repository.sql | 12 ++++++++ server/src/queries/shared.link.repository.sql | 9 ++++++ server/src/queries/stack.repository.sql | 9 ++++++ server/src/repositories/access.repository.ts | 28 ++++++++++++++++--- .../src/repositories/activity.repository.ts | 12 +++++++- server/src/repositories/album.repository.ts | 13 ++++++++- server/src/repositories/asset.repository.ts | 8 ++++-- server/src/repositories/memory.repository.ts | 5 +++- server/src/repositories/person.repository.ts | 11 +++++++- server/src/repositories/search.repository.ts | 12 ++++++++ .../repositories/shared-link.repository.ts | 9 ++++++ server/src/repositories/stack.repository.ts | 8 +++++- .../src/services/asset-media.service.spec.ts | 10 +++++++ server/src/utils/date-time.ts | 4 +-- 23 files changed, 194 insertions(+), 16 deletions(-) diff --git a/server/src/dtos/asset-response.dto.ts b/server/src/dtos/asset-response.dto.ts index 0658567912..5ac10c57a3 100644 --- a/server/src/dtos/asset-response.dto.ts +++ b/server/src/dtos/asset-response.dto.ts @@ -113,6 +113,14 @@ const hexOrBufferToBase64 = (encoded: string | Buffer) => { export function mapAsset(entity: AssetEntity, options: AssetMapOptions = {}): AssetResponseDto { const { stripMetadata = false, withStack = false } = options; + if (!entity.localDateTime) { + throw new Error('Asset localDateTime is missing'); + } else if (!entity.fileCreatedAt) { + throw new Error('Asset fileCreatedAt is missing'); + } else if (!entity.fileModifiedAt) { + throw new Error('Asset fileModifiedAt is missing'); + } + if (stripMetadata) { const sanitizedAssetResponse: SanitizedAssetResponseDto = { id: entity.id, diff --git a/server/src/entities/asset.entity.ts b/server/src/entities/asset.entity.ts index 420fedfc53..4ed74acec7 100644 --- a/server/src/entities/asset.entity.ts +++ b/server/src/entities/asset.entity.ts @@ -399,5 +399,8 @@ export function searchAssetBuilder(kysely: Kysely, options: AssetSearchBuild ) .$if(!!options.withExif, withExifInner) .$if(!!(options.withFaces || options.withPeople || options.personIds), (qb) => qb.select(withFacesAndPeople)) - .$if(!options.withDeleted, (qb) => qb.where('assets.deletedAt', 'is', null)); + .$if(!options.withDeleted, (qb) => qb.where('assets.deletedAt', 'is', null)) + .$if(!options.withNullFileModifiedAt, (qb) => qb.where('assets.fileModifiedAt', 'is not', null)) + .$if(!options.withNullFileCreatedAt, (qb) => qb.where('assets.fileCreatedAt', 'is not', null)) + .$if(!options.withNullLocalDateTime, (qb) => qb.where('assets.localDateTime', 'is not', null)); } diff --git a/server/src/interfaces/search.interface.ts b/server/src/interfaces/search.interface.ts index bb76ff7b1f..d6a109e89a 100644 --- a/server/src/interfaces/search.interface.ts +++ b/server/src/interfaces/search.interface.ts @@ -63,6 +63,9 @@ export interface SearchStatusOptions { status?: AssetStatus; withArchived?: boolean; withDeleted?: boolean; + withNullLocalDateTime?: boolean; + withNullFileCreatedAt?: boolean; + withNullFileModifiedAt?: boolean; } export interface SearchOneToOneRelationOptions { diff --git a/server/src/queries/access.repository.sql b/server/src/queries/access.repository.sql index dd58aebcb2..3fdf92561a 100644 --- a/server/src/queries/access.repository.sql +++ b/server/src/queries/access.repository.sql @@ -79,6 +79,9 @@ from inner join "albums_assets_assets" as "albumAssets" on "albums"."id" = "albumAssets"."albumsId" inner join "assets" on "assets"."id" = "albumAssets"."assetsId" and "assets"."deletedAt" is null + and "assets"."fileCreatedAt" is not null + and "assets"."fileModifiedAt" is not null + and "assets"."localDateTime" is not null left join "albums_shared_users_users" as "albumUsers" on "albumUsers"."albumsId" = "albums"."id" left join "users" on "users"."id" = "albumUsers"."usersId" and "users"."deletedAt" is null @@ -108,6 +111,9 @@ from and "sharedBy"."deletedAt" is null inner join "assets" on "assets"."ownerId" = "sharedBy"."id" and "assets"."deletedAt" is null + and "assets"."fileCreatedAt" is not null + and "assets"."fileModifiedAt" is not null + and "assets"."localDateTime" is not null where "partner"."sharedWithId" = $1 and "assets"."isArchived" = $2 @@ -126,6 +132,9 @@ from left join "shared_link__asset" on "shared_link__asset"."sharedLinksId" = "shared_links"."id" left join "assets" on "assets"."id" = "shared_link__asset"."assetsId" and "assets"."deletedAt" is null + and "assets"."fileCreatedAt" is not null + and "assets"."fileModifiedAt" is not null + and "assets"."localDateTime" is not null left join "albums_assets_assets" on "albums_assets_assets"."albumsId" = "albums"."id" left join "assets" as "albumAssets" on "albumAssets"."id" = "albums_assets_assets"."assetsId" and "albumAssets"."deletedAt" is null @@ -173,6 +182,9 @@ from "asset_faces" left join "assets" on "assets"."id" = "asset_faces"."assetId" and "assets"."deletedAt" is null + and "assets"."fileCreatedAt" is not null + and "assets"."fileModifiedAt" is not null + and "assets"."localDateTime" is not null where "asset_faces"."id" in ($1) and "assets"."ownerId" = $2 diff --git a/server/src/queries/activity.repository.sql b/server/src/queries/activity.repository.sql index 8e9bb11f25..782d4fb160 100644 --- a/server/src/queries/activity.repository.sql +++ b/server/src/queries/activity.repository.sql @@ -25,6 +25,9 @@ from "activity" left join "assets" on "assets"."id" = "activity"."assetId" and "assets"."deletedAt" is null + and "assets"."fileCreatedAt" is not null + and "assets"."fileModifiedAt" is not null + and "assets"."localDateTime" is not null where "activity"."albumId" = $1 order by @@ -43,3 +46,6 @@ where and "activity"."albumId" = $2 and "activity"."isLiked" = $3 and "assets"."deletedAt" is null + and "assets"."fileCreatedAt" is not null + and "assets"."fileModifiedAt" is not null + and "assets"."localDateTime" is not null diff --git a/server/src/queries/album.repository.sql b/server/src/queries/album.repository.sql index 08ea078f73..525a634d62 100644 --- a/server/src/queries/album.repository.sql +++ b/server/src/queries/album.repository.sql @@ -98,6 +98,9 @@ select where "albums_assets_assets"."albumsId" = "albums"."id" and "assets"."deletedAt" is null + and "assets"."fileCreatedAt" is not null + and "assets"."fileModifiedAt" is not null + and "assets"."localDateTime" is not null order by "assets"."fileCreatedAt" desc ) as "asset" @@ -212,6 +215,9 @@ from where "albums"."id" in ($1) and "assets"."deletedAt" is null + and "assets"."fileCreatedAt" is not null + and "assets"."fileModifiedAt" is not null + and "assets"."localDateTime" is not null group by "albums"."id" diff --git a/server/src/queries/asset.repository.sql b/server/src/queries/asset.repository.sql index 9bbed8dbe1..3431ce7540 100644 --- a/server/src/queries/asset.repository.sql +++ b/server/src/queries/asset.repository.sql @@ -37,8 +37,6 @@ with and (assets."localDateTime" at time zone 'UTC')::date = today.date and "assets"."ownerId" = any ($3::uuid[]) and "assets"."isVisible" = $4 - and "assets"."fileCreatedAt" is not null - and "assets"."fileModifiedAt" is not null and "assets"."isArchived" = $5 and exists ( select @@ -49,6 +47,9 @@ with and "asset_files"."type" = $6 ) and "assets"."deletedAt" is null + and "assets"."fileCreatedAt" is not null + and "assets"."fileModifiedAt" is not null + and "assets"."localDateTime" is not null limit $7 ) as "a" on true @@ -266,6 +267,7 @@ with and "assets"."isVisible" = $2 and "assets"."fileCreatedAt" is not null and "assets"."fileModifiedAt" is not null + and "assets"."localDateTime" is not null ) select "timeBucket", @@ -308,6 +310,7 @@ where and "assets"."isVisible" = $2 and "assets"."fileCreatedAt" is not null and "assets"."fileModifiedAt" is not null + and "assets"."localDateTime" is not null and date_trunc($3, "localDateTime" at time zone 'UTC') at time zone 'UTC' = $4 order by "assets"."localDateTime" desc @@ -336,6 +339,7 @@ with and "assets"."isVisible" = $2 and "assets"."fileCreatedAt" is not null and "assets"."fileModifiedAt" is not null + and "assets"."localDateTime" is not null group by "assets"."duplicateId" ), diff --git a/server/src/queries/memory.repository.sql b/server/src/queries/memory.repository.sql index 3144f314dd..98b4b206df 100644 --- a/server/src/queries/memory.repository.sql +++ b/server/src/queries/memory.repository.sql @@ -26,6 +26,9 @@ select where "memories_assets_assets"."memoriesId" = "memories"."id" and "assets"."deletedAt" is null + and "assets"."fileCreatedAt" is not null + and "assets"."fileModifiedAt" is not null + and "assets"."localDateTime" is not null ) as agg ) as "assets" from @@ -56,6 +59,9 @@ select where "memories_assets_assets"."memoriesId" = "memories"."id" and "assets"."deletedAt" is null + and "assets"."fileCreatedAt" is not null + and "assets"."fileModifiedAt" is not null + and "assets"."localDateTime" is not null ) as agg ) as "assets" from diff --git a/server/src/queries/person.repository.sql b/server/src/queries/person.repository.sql index 2c06d7c3f2..f89fe7b342 100644 --- a/server/src/queries/person.repository.sql +++ b/server/src/queries/person.repository.sql @@ -169,6 +169,9 @@ from and "asset_faces"."personId" = $1 and "assets"."isArchived" = $2 and "assets"."deletedAt" is null + and "assets"."fileCreatedAt" is not null + and "assets"."fileModifiedAt" is not null + and "assets"."localDateTime" is not null and "assets"."livePhotoVideoId" is null -- PersonRepository.getNumberOfPeople @@ -183,6 +186,9 @@ from inner join "asset_faces" on "asset_faces"."personId" = "person"."id" inner join "assets" on "assets"."id" = "asset_faces"."assetId" and "assets"."deletedAt" is null + and "assets"."fileCreatedAt" is not null + and "assets"."fileModifiedAt" is not null + and "assets"."localDateTime" is not null and "assets"."isArchived" = $2 where "person"."ownerId" = $3 diff --git a/server/src/queries/search.repository.sql b/server/src/queries/search.repository.sql index 72e8a6941d..add98d57ad 100644 --- a/server/src/queries/search.repository.sql +++ b/server/src/queries/search.repository.sql @@ -98,6 +98,9 @@ with "assets"."ownerId" = any ($2::uuid[]) and "assets"."deletedAt" is null and "assets"."isVisible" = $3 + and "assets"."fileCreatedAt" is not null + and "assets"."fileModifiedAt" is not null + and "assets"."localDateTime" is not null and "assets"."type" = $4 and "assets"."id" != $5::uuid order by @@ -126,6 +129,9 @@ with where "assets"."ownerId" = any ($2::uuid[]) and "assets"."deletedAt" is null + and "assets"."fileCreatedAt" is not null + and "assets"."fileModifiedAt" is not null + and "assets"."localDateTime" is not null order by face_search.embedding <=> $3 limit @@ -178,6 +184,9 @@ with recursive and "assets"."isArchived" = $3 and "assets"."type" = $4 and "assets"."deletedAt" is null + and "assets"."fileCreatedAt" is not null + and "assets"."fileModifiedAt" is not null + and "assets"."localDateTime" is not null order by "city" limit @@ -203,6 +212,9 @@ with recursive and "assets"."isArchived" = $8 and "assets"."type" = $9 and "assets"."deletedAt" is null + and "assets"."fileCreatedAt" is not null + and "assets"."fileModifiedAt" is not null + and "assets"."localDateTime" is not null and "exif"."city" > "cte"."city" order by "city" diff --git a/server/src/queries/shared.link.repository.sql b/server/src/queries/shared.link.repository.sql index 1861ed86e4..98b0334ea3 100644 --- a/server/src/queries/shared.link.repository.sql +++ b/server/src/queries/shared.link.repository.sql @@ -31,6 +31,9 @@ from where "shared_links"."id" = "shared_link__asset"."sharedLinksId" and "assets"."deletedAt" is null + and "assets"."fileCreatedAt" is not null + and "assets"."fileModifiedAt" is not null + and "assets"."localDateTime" is not null order by "assets"."fileCreatedAt" asc ) as "a" on true @@ -65,6 +68,9 @@ from where "albums_assets_assets"."assetsId" = "assets"."id" and "assets"."deletedAt" is null + and "assets"."fileCreatedAt" is not null + and "assets"."fileModifiedAt" is not null + and "assets"."localDateTime" is not null order by "assets"."fileCreatedAt" asc ) as "assets" on true @@ -112,6 +118,9 @@ from where "assets"."id" = "shared_link__asset"."assetsId" and "assets"."deletedAt" is null + and "assets"."fileCreatedAt" is not null + and "assets"."fileModifiedAt" is not null + and "assets"."localDateTime" is not null ) as "assets" on true left join lateral ( select diff --git a/server/src/queries/stack.repository.sql b/server/src/queries/stack.repository.sql index 0fd1b233be..8a95109f4d 100644 --- a/server/src/queries/stack.repository.sql +++ b/server/src/queries/stack.repository.sql @@ -23,6 +23,9 @@ select ) as "exifInfo" on true where "assets"."deletedAt" is null + and "assets"."fileCreatedAt" is not null + and "assets"."fileModifiedAt" is not null + and "assets"."localDateTime" is not null and "assets"."stackId" = "asset_stack"."id" ) as agg ) as "assets" @@ -68,6 +71,9 @@ select ) as "exifInfo" on true where "assets"."deletedAt" is null + and "assets"."fileCreatedAt" is not null + and "assets"."fileModifiedAt" is not null + and "assets"."localDateTime" is not null and "assets"."stackId" = "asset_stack"."id" ) as agg ) as "assets" @@ -113,6 +119,9 @@ select ) as "exifInfo" on true where "assets"."deletedAt" is null + and "assets"."fileCreatedAt" is not null + and "assets"."fileModifiedAt" is not null + and "assets"."localDateTime" is not null and "assets"."stackId" = "asset_stack"."id" ) as agg ) as "assets" diff --git a/server/src/repositories/access.repository.ts b/server/src/repositories/access.repository.ts index 9fa8b6243c..ebee1194f1 100644 --- a/server/src/repositories/access.repository.ts +++ b/server/src/repositories/access.repository.ts @@ -138,7 +138,12 @@ class AssetAccess { .selectFrom('albums') .innerJoin('albums_assets_assets as albumAssets', 'albums.id', 'albumAssets.albumsId') .innerJoin('assets', (join) => - join.onRef('assets.id', '=', 'albumAssets.assetsId').on('assets.deletedAt', 'is', null), + join + .onRef('assets.id', '=', 'albumAssets.assetsId') + .on('assets.deletedAt', 'is', null) + .on('assets.fileCreatedAt', 'is not', null) + .on('assets.fileModifiedAt', 'is not', null) + .on('assets.localDateTime', 'is not', null), ) .leftJoin('albums_shared_users_users as albumUsers', 'albumUsers.albumsId', 'albums.id') .leftJoin('users', (join) => join.onRef('users.id', '=', 'albumUsers.usersId').on('users.deletedAt', 'is', null)) @@ -194,7 +199,12 @@ class AssetAccess { join.onRef('sharedBy.id', '=', 'partner.sharedById').on('sharedBy.deletedAt', 'is', null), ) .innerJoin('assets', (join) => - join.onRef('assets.ownerId', '=', 'sharedBy.id').on('assets.deletedAt', 'is', null), + join + .onRef('assets.ownerId', '=', 'sharedBy.id') + .on('assets.deletedAt', 'is', null) + .on('assets.fileCreatedAt', 'is not', null) + .on('assets.fileModifiedAt', 'is not', null) + .on('assets.localDateTime', 'is not', null), ) .select('assets.id') .where('partner.sharedWithId', '=', userId) @@ -218,7 +228,12 @@ class AssetAccess { ) .leftJoin('shared_link__asset', 'shared_link__asset.sharedLinksId', 'shared_links.id') .leftJoin('assets', (join) => - join.onRef('assets.id', '=', 'shared_link__asset.assetsId').on('assets.deletedAt', 'is', null), + join + .onRef('assets.id', '=', 'shared_link__asset.assetsId') + .on('assets.deletedAt', 'is', null) + .on('assets.fileCreatedAt', 'is not', null) + .on('assets.fileModifiedAt', 'is not', null) + .on('assets.localDateTime', 'is not', null), ) .leftJoin('albums_assets_assets', 'albums_assets_assets.albumsId', 'albums.id') .leftJoin('assets as albumAssets', (join) => @@ -369,7 +384,12 @@ class PersonAccess { .selectFrom('asset_faces') .select('asset_faces.id') .leftJoin('assets', (join) => - join.onRef('assets.id', '=', 'asset_faces.assetId').on('assets.deletedAt', 'is', null), + join + .onRef('assets.id', '=', 'asset_faces.assetId') + .on('assets.deletedAt', 'is', null) + .on('assets.fileCreatedAt', 'is not', null) + .on('assets.fileModifiedAt', 'is not', null) + .on('assets.localDateTime', 'is not', null), ) .where('asset_faces.id', 'in', [...assetFaceIds]) .where('assets.ownerId', '=', userId) diff --git a/server/src/repositories/activity.repository.ts b/server/src/repositories/activity.repository.ts index 99d3192341..dd4fd41bc0 100644 --- a/server/src/repositories/activity.repository.ts +++ b/server/src/repositories/activity.repository.ts @@ -36,7 +36,14 @@ export class ActivityRepository { .selectFrom('activity') .selectAll('activity') .select(withUser) - .leftJoin('assets', (join) => join.onRef('assets.id', '=', 'activity.assetId').on('assets.deletedAt', 'is', null)) + .leftJoin('assets', (join) => + join + .onRef('assets.id', '=', 'activity.assetId') + .on('assets.deletedAt', 'is', null) + .on('assets.fileCreatedAt', 'is not', null) + .on('assets.fileModifiedAt', 'is not', null) + .on('assets.localDateTime', 'is not', null), + ) .$if(!!userId, (qb) => qb.where('activity.userId', '=', userId!)) .$if(assetId === null, (qb) => qb.where('assetId', 'is', null)) .$if(!!assetId, (qb) => qb.where('activity.assetId', '=', assetId!)) @@ -65,6 +72,9 @@ export class ActivityRepository { .where('activity.albumId', '=', albumId) .where('activity.isLiked', '=', false) .where('assets.deletedAt', 'is', null) + .where('assets.fileCreatedAt', 'is not', null) + .where('assets.fileModifiedAt', 'is not', null) + .where('assets.localDateTime', 'is not', null) .executeTakeFirstOrThrow(); return count as number; diff --git a/server/src/repositories/album.repository.ts b/server/src/repositories/album.repository.ts index 6c81395a58..29902f258c 100644 --- a/server/src/repositories/album.repository.ts +++ b/server/src/repositories/album.repository.ts @@ -63,6 +63,9 @@ const withAssets = (eb: ExpressionBuilder) => { .innerJoin('albums_assets_assets', 'albums_assets_assets.assetsId', 'assets.id') .whereRef('albums_assets_assets.albumsId', '=', 'albums.id') .where('assets.deletedAt', 'is', null) + .where('assets.fileCreatedAt', 'is not', null) + .where('assets.fileModifiedAt', 'is not', null) + .where('assets.localDateTime', 'is not', null) .orderBy('assets.fileCreatedAt', 'desc') .as('asset'), ) @@ -132,6 +135,9 @@ export class AlbumRepository implements IAlbumRepository { .select((eb) => sql`${eb.fn.count('assets.id')}::int`.as('assetCount')) .where('albums.id', 'in', ids) .where('assets.deletedAt', 'is', null) + .where('assets.fileCreatedAt', 'is not', null) + .where('assets.fileModifiedAt', 'is not', null) + .where('assets.localDateTime', 'is not', null) .groupBy('albums.id') .execute(); } @@ -371,7 +377,12 @@ export class AlbumRepository implements IAlbumRepository { return eb .selectFrom('albums_assets_assets as album_assets') .innerJoin('assets', (join) => - join.onRef('album_assets.assetsId', '=', 'assets.id').on('assets.deletedAt', 'is', null), + join + .onRef('album_assets.assetsId', '=', 'assets.id') + .on('assets.deletedAt', 'is', null) + .on('assets.fileCreatedAt', 'is not', null) + .on('assets.fileModifiedAt', 'is not', null) + .on('assets.localDateTime', 'is not', null), ) .whereRef('album_assets.albumsId', '=', 'albums.id'); } diff --git a/server/src/repositories/asset.repository.ts b/server/src/repositories/asset.repository.ts index 914f3929ff..f1eac47d02 100644 --- a/server/src/repositories/asset.repository.ts +++ b/server/src/repositories/asset.repository.ts @@ -112,8 +112,6 @@ export class AssetRepository implements IAssetRepository { .where(sql`(assets."localDateTime" at time zone 'UTC')::date`, '=', sql`today.date`) .where('assets.ownerId', '=', anyUuid(ownerIds)) .where('assets.isVisible', '=', true) - .where('assets.fileCreatedAt', 'is not', null) - .where('assets.fileModifiedAt', 'is not', null) .where('assets.isArchived', '=', false) .where((eb) => eb.exists((qb) => @@ -124,6 +122,9 @@ export class AssetRepository implements IAssetRepository { ), ) .where('assets.deletedAt', 'is', null) + .where('assets.fileCreatedAt', 'is not', null) + .where('assets.fileModifiedAt', 'is not', null) + .where('assets.localDateTime', 'is not', null) .limit(20) .as('a'), (join) => join.onTrue(), @@ -608,6 +609,7 @@ export class AssetRepository implements IAssetRepository { .where('assets.isVisible', '=', true) .where('assets.fileCreatedAt', 'is not', null) .where('assets.fileModifiedAt', 'is not', null) + .where('assets.localDateTime', 'is not', null) .$if(!!options.albumId, (qb) => qb .innerJoin('albums_assets_assets', 'assets.id', 'albums_assets_assets.assetsId') @@ -690,6 +692,7 @@ export class AssetRepository implements IAssetRepository { .where('assets.isVisible', '=', true) .where('assets.fileCreatedAt', 'is not', null) .where('assets.fileModifiedAt', 'is not', null) + .where('assets.localDateTime', 'is not', null) .where(truncatedDate(options.size), '=', timeBucket.replace(/^[+-]/, '')) .orderBy('assets.localDateTime', options.order ?? 'desc') .execute() as any as Promise; @@ -720,6 +723,7 @@ export class AssetRepository implements IAssetRepository { .where('assets.isVisible', '=', true) .where('assets.fileCreatedAt', 'is not', null) .where('assets.fileModifiedAt', 'is not', null) + .where('assets.localDateTime', 'is not', null) .groupBy('assets.duplicateId'), ) .with('unique', (qb) => diff --git a/server/src/repositories/memory.repository.ts b/server/src/repositories/memory.repository.ts index 042738fe4c..ba8b4eca68 100644 --- a/server/src/repositories/memory.repository.ts +++ b/server/src/repositories/memory.repository.ts @@ -105,7 +105,10 @@ export class MemoryRepository implements IBulkAsset { .selectAll('assets') .innerJoin('memories_assets_assets', 'assets.id', 'memories_assets_assets.assetsId') .whereRef('memories_assets_assets.memoriesId', '=', 'memories.id') - .where('assets.deletedAt', 'is', null), + .where('assets.deletedAt', 'is', null) + .where('assets.fileCreatedAt', 'is not', null) + .where('assets.fileModifiedAt', 'is not', null) + .where('assets.localDateTime', 'is not', null), ).as('assets'), ) .where('id', '=', id) diff --git a/server/src/repositories/person.repository.ts b/server/src/repositories/person.repository.ts index 7c2512aa26..fe240bca8b 100644 --- a/server/src/repositories/person.repository.ts +++ b/server/src/repositories/person.repository.ts @@ -128,7 +128,10 @@ export class PersonRepository implements IPersonRepository { join .onRef('asset_faces.assetId', '=', 'assets.id') .on('assets.isArchived', '=', false) - .on('assets.deletedAt', 'is', null), + .on('assets.deletedAt', 'is', null) + .on('assets.fileCreatedAt', 'is not', null) + .on('assets.fileModifiedAt', 'is not', null) + .on('assets.localDateTime', 'is not', null), ) .where('person.ownerId', '=', userId) .orderBy('person.isHidden', 'asc') @@ -284,6 +287,9 @@ export class PersonRepository implements IPersonRepository { .on('asset_faces.personId', '=', personId) .on('assets.isArchived', '=', false) .on('assets.deletedAt', 'is', null) + .on('assets.fileCreatedAt', 'is not', null) + .on('assets.fileModifiedAt', 'is not', null) + .on('assets.localDateTime', 'is not', null) .on('assets.livePhotoVideoId', 'is', null), ) .select((eb) => eb.fn.count(eb.fn('distinct', ['assets.id'])).as('count')) @@ -304,6 +310,9 @@ export class PersonRepository implements IPersonRepository { join .onRef('assets.id', '=', 'asset_faces.assetId') .on('assets.deletedAt', 'is', null) + .on('assets.fileCreatedAt', 'is not', null) + .on('assets.fileModifiedAt', 'is not', null) + .on('assets.localDateTime', 'is not', null) .on('assets.isArchived', '=', false), ) .select((eb) => eb.fn.count(eb.fn('distinct', ['person.id'])).as('total')) diff --git a/server/src/repositories/search.repository.ts b/server/src/repositories/search.repository.ts index 76b6653e3d..613ab37629 100644 --- a/server/src/repositories/search.repository.ts +++ b/server/src/repositories/search.repository.ts @@ -139,6 +139,9 @@ export class SearchRepository implements ISearchRepository { .where('assets.ownerId', '=', anyUuid(userIds)) .where('assets.deletedAt', 'is', null) .where('assets.isVisible', '=', true) + .where('assets.fileCreatedAt', 'is not', null) + .where('assets.fileModifiedAt', 'is not', null) + .where('assets.localDateTime', 'is not', null) .where('assets.type', '=', type) .where('assets.id', '!=', asUuid(assetId)) .orderBy(sql`smart_search.embedding <=> ${embedding}`) @@ -178,6 +181,9 @@ export class SearchRepository implements ISearchRepository { .innerJoin('face_search', 'face_search.faceId', 'asset_faces.id') .where('assets.ownerId', '=', anyUuid(userIds)) .where('assets.deletedAt', 'is', null) + .where('assets.fileCreatedAt', 'is not', null) + .where('assets.fileModifiedAt', 'is not', null) + .where('assets.localDateTime', 'is not', null) .$if(!!hasPerson, (qb) => qb.where('asset_faces.personId', 'is not', null)) .orderBy(sql`face_search.embedding <=> ${embedding}`) .limit(numResults), @@ -228,6 +234,9 @@ export class SearchRepository implements ISearchRepository { .where('assets.isArchived', '=', false) .where('assets.type', '=', 'IMAGE') .where('assets.deletedAt', 'is', null) + .where('assets.fileCreatedAt', 'is not', null) + .where('assets.fileModifiedAt', 'is not', null) + .where('assets.localDateTime', 'is not', null) .orderBy('city') .limit(1); @@ -245,6 +254,9 @@ export class SearchRepository implements ISearchRepository { .where('assets.isArchived', '=', false) .where('assets.type', '=', 'IMAGE') .where('assets.deletedAt', 'is', null) + .where('assets.fileCreatedAt', 'is not', null) + .where('assets.fileModifiedAt', 'is not', null) + .where('assets.localDateTime', 'is not', null) .whereRef('exif.city', '>', 'cte.city') .orderBy('city') .limit(1) diff --git a/server/src/repositories/shared-link.repository.ts b/server/src/repositories/shared-link.repository.ts index 6473100387..cb860b33ce 100644 --- a/server/src/repositories/shared-link.repository.ts +++ b/server/src/repositories/shared-link.repository.ts @@ -25,6 +25,9 @@ export class SharedLinkRepository implements ISharedLinkRepository { .whereRef('shared_links.id', '=', 'shared_link__asset.sharedLinksId') .innerJoin('assets', 'assets.id', 'shared_link__asset.assetsId') .where('assets.deletedAt', 'is', null) + .where('assets.fileCreatedAt', 'is not', null) + .where('assets.fileModifiedAt', 'is not', null) + .where('assets.localDateTime', 'is not', null) .selectAll('assets') .innerJoinLateral( (eb) => eb.selectFrom('exif').selectAll('exif').whereRef('exif.assetId', '=', 'assets.id').as('exifInfo'), @@ -50,6 +53,9 @@ export class SharedLinkRepository implements ISharedLinkRepository { .selectAll('assets') .whereRef('albums_assets_assets.assetsId', '=', 'assets.id') .where('assets.deletedAt', 'is', null) + .where('assets.fileCreatedAt', 'is not', null) + .where('assets.fileModifiedAt', 'is not', null) + .where('assets.localDateTime', 'is not', null) .innerJoinLateral( (eb) => eb @@ -105,6 +111,9 @@ export class SharedLinkRepository implements ISharedLinkRepository { .selectFrom('assets') .whereRef('assets.id', '=', 'shared_link__asset.assetsId') .where('assets.deletedAt', 'is', null) + .where('assets.fileCreatedAt', 'is not', null) + .where('assets.fileModifiedAt', 'is not', null) + .where('assets.localDateTime', 'is not', null) .selectAll('assets') .as('assets'), (join) => join.onTrue(), diff --git a/server/src/repositories/stack.repository.ts b/server/src/repositories/stack.repository.ts index 018d7e77a4..02bf5b9a66 100644 --- a/server/src/repositories/stack.repository.ts +++ b/server/src/repositories/stack.repository.ts @@ -30,6 +30,9 @@ const withAssets = (eb: ExpressionBuilder, withTags = false) ) .select((eb) => eb.fn.toJson('exifInfo').as('exifInfo')) .where('assets.deletedAt', 'is', null) + .where('assets.fileCreatedAt', 'is not', null) + .where('assets.fileModifiedAt', 'is not', null) + .where('assets.localDateTime', 'is not', null) .whereRef('assets.stackId', '=', 'asset_stack.id'), ).as('assets'); }; @@ -62,7 +65,10 @@ export class StackRepository implements IStackRepository { .selectFrom('assets') .select('assets.id') .whereRef('assets.stackId', '=', 'asset_stack.id') - .where('assets.deletedAt', 'is', null), + .where('assets.deletedAt', 'is', null) + .where('assets.fileCreatedAt', 'is not', null) + .where('assets.fileModifiedAt', 'is not', null) + .where('assets.localDateTime', 'is not', null), ).as('assets'), ) .execute(); diff --git a/server/src/services/asset-media.service.spec.ts b/server/src/services/asset-media.service.spec.ts index 9dcfa3cbd9..d9c649b922 100644 --- a/server/src/services/asset-media.service.spec.ts +++ b/server/src/services/asset-media.service.spec.ts @@ -541,6 +541,11 @@ describe(AssetMediaService.name, () => { it('should throw an error if the requested preview file does not exist', async () => { accessMock.asset.checkOwnerAccess.mockResolvedValue(new Set([assetStub.image.id])); + + if (!assetStub.image.fileCreatedAt) { + throw new Error('fileCreatedAt is missing'); + } + assetMock.getById.mockResolvedValue({ ...assetStub.image, files: [ @@ -561,6 +566,11 @@ describe(AssetMediaService.name, () => { it('should fall back to preview if the requested thumbnail file does not exist', async () => { accessMock.asset.checkOwnerAccess.mockResolvedValue(new Set([assetStub.image.id])); + + if (!assetStub.image.fileCreatedAt) { + throw new Error('fileCreatedAt is missing'); + } + assetMock.getById.mockResolvedValue({ ...assetStub.image, files: [ diff --git a/server/src/utils/date-time.ts b/server/src/utils/date-time.ts index e1578cbb19..bbeef465e4 100644 --- a/server/src/utils/date-time.ts +++ b/server/src/utils/date-time.ts @@ -1,5 +1,5 @@ import { AssetEntity } from 'src/entities/asset.entity'; -export const getAssetDateTime = (asset: AssetEntity | undefined) => { - return asset?.exifInfo?.dateTimeOriginal || asset?.fileCreatedAt; +export const getAssetDateTime = (asset: AssetEntity | undefined): Date | undefined => { + return (asset?.exifInfo?.dateTimeOriginal || asset?.fileCreatedAt) ?? undefined; };