1
0
Fork 0
mirror of https://github.com/immich-app/immich.git synced 2025-02-03 01:22:44 +01:00
This commit is contained in:
Jonathan Jogenfors 2025-01-27 23:53:15 +01:00
parent 41800d6278
commit b632b84fc3
25 changed files with 209 additions and 26 deletions

View file

@ -298,6 +298,7 @@ describe('/libraries', () => {
expect(status).toBe(204);
await utils.waitForQueueFinish(admin.accessToken, 'library');
await utils.waitForQueueFinish(admin.accessToken, 'metadataExtraction');
const { assets } = await utils.searchAssets(admin.accessToken, {
originalPath: `${testAssetDirInternal}/temp/directoryA/assetA.png`,

View file

@ -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,

View file

@ -399,5 +399,8 @@ export function searchAssetBuilder(kysely: Kysely<DB>, 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));
}

View file

@ -63,6 +63,9 @@ export interface SearchStatusOptions {
status?: AssetStatus;
withArchived?: boolean;
withDeleted?: boolean;
withNullLocalDateTime?: boolean;
withNullFileCreatedAt?: boolean;
withNullFileModifiedAt?: boolean;
}
export interface SearchOneToOneRelationOptions {

View file

@ -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

View file

@ -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

View file

@ -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"

View file

@ -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"
),

View file

@ -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

View file

@ -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

View file

@ -13,6 +13,9 @@ where
and "assets"."isFavorite" = $4
and "assets"."isArchived" = $5
and "assets"."deletedAt" is null
and "assets"."fileModifiedAt" is not null
and "assets"."fileCreatedAt" is not null
and "assets"."localDateTime" is not null
order by
"assets"."fileCreatedAt" desc
limit
@ -34,6 +37,9 @@ offset
and "assets"."isFavorite" = $4
and "assets"."isArchived" = $5
and "assets"."deletedAt" is null
and "assets"."fileModifiedAt" is not null
and "assets"."fileCreatedAt" is not null
and "assets"."localDateTime" is not null
and "assets"."id" < $6
order by
random()
@ -54,6 +60,9 @@ union all
and "assets"."isFavorite" = $11
and "assets"."isArchived" = $12
and "assets"."deletedAt" is null
and "assets"."fileModifiedAt" is not null
and "assets"."fileCreatedAt" is not null
and "assets"."localDateTime" is not null
and "assets"."id" > $13
order by
random()
@ -77,6 +86,9 @@ where
and "assets"."isFavorite" = $4
and "assets"."isArchived" = $5
and "assets"."deletedAt" is null
and "assets"."fileModifiedAt" is not null
and "assets"."fileCreatedAt" is not null
and "assets"."localDateTime" is not null
order by
smart_search.embedding <=> $6
limit
@ -98,6 +110,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 +141,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 +196,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 +224,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"

View file

@ -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

View file

@ -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"

View file

@ -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)

View file

@ -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;

View file

@ -63,6 +63,9 @@ const withAssets = (eb: ExpressionBuilder<DB, 'albums'>) => {
.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<number>`${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');
}

View file

@ -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<AssetEntity[]>;
@ -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) =>

View file

@ -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)

View file

@ -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'))

View file

@ -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)

View file

@ -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(),

View file

@ -30,6 +30,9 @@ const withAssets = (eb: ExpressionBuilder<DB, 'asset_stack'>, 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();

View file

@ -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: [

View file

@ -461,14 +461,6 @@ export class MetadataService extends BaseService {
} else {
const motionAssetId = this.cryptoRepository.randomUUID();
if (!asset.fileCreatedAt) {
asset.fileCreatedAt = stat.birthtime;
}
if (!asset.fileModifiedAt) {
asset.fileModifiedAt = stat.mtime;
}
const dates = this.getDates(asset, tags);
motionAsset = await this.assetRepository.create({
id: motionAssetId,
@ -589,11 +581,11 @@ export class MetadataService extends BaseService {
private getDates(asset: AssetEntity, exifTags: ImmichTags): AssetDatesDto {
// We first assert that fileCreatedAt and fileModifiedAt are not null since that should be set to a non-null value before calling this function
if (asset.fileCreatedAt == null) {
if (asset.fileCreatedAt === null) {
this.logger.warn(`Asset ${asset.id} has no file creation date`);
throw new BadRequestException(`Asset ${asset.id} has no file creation date`);
}
if (asset.fileModifiedAt == null) {
if (asset.fileModifiedAt === null) {
this.logger.warn(`Asset ${asset.id} has no file modification date`);
throw new BadRequestException(`Asset ${asset.id} has no file modification date`);
}

View file

@ -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;
};