mirror of
https://github.com/immich-app/immich.git
synced 2026-03-01 18:19:10 +03:00
feat(server): visibility column (#17939)
* feat: private view * pr feedback * sql generation * feat: visibility column * fix: set visibility value as the same as the still part after unlinked live photos * fix: test * pr feedback
This commit is contained in:
@@ -3,7 +3,7 @@ import { Kysely, sql } from 'kysely';
|
||||
import { InjectKysely } from 'nestjs-kysely';
|
||||
import { DB } from 'src/db';
|
||||
import { ChunkedSet, DummyValue, GenerateSql } from 'src/decorators';
|
||||
import { AlbumUserRole } from 'src/enum';
|
||||
import { AlbumUserRole, AssetVisibility } from 'src/enum';
|
||||
import { asUuid } from 'src/utils/database';
|
||||
|
||||
class ActivityAccess {
|
||||
@@ -199,7 +199,13 @@ class AssetAccess {
|
||||
)
|
||||
.select('assets.id')
|
||||
.where('partner.sharedWithId', '=', userId)
|
||||
.where('assets.isArchived', '=', false)
|
||||
.where((eb) =>
|
||||
eb.or([
|
||||
eb('assets.visibility', '=', sql.lit(AssetVisibility.TIMELINE)),
|
||||
eb('assets.visibility', '=', sql.lit(AssetVisibility.HIDDEN)),
|
||||
]),
|
||||
)
|
||||
|
||||
.where('assets.id', 'in', [...assetIds])
|
||||
.execute()
|
||||
.then((assets) => new Set(assets.map((asset) => asset.id)));
|
||||
|
||||
@@ -5,7 +5,7 @@ import { InjectKysely } from 'nestjs-kysely';
|
||||
import { Asset, columns } from 'src/database';
|
||||
import { DB } from 'src/db';
|
||||
import { DummyValue, GenerateSql } from 'src/decorators';
|
||||
import { AssetFileType, AssetType } from 'src/enum';
|
||||
import { AssetFileType, AssetType, AssetVisibility } from 'src/enum';
|
||||
import { StorageAsset } from 'src/types';
|
||||
import {
|
||||
anyUuid,
|
||||
@@ -34,7 +34,7 @@ export class AssetJobRepository {
|
||||
'ownerId',
|
||||
'duplicateId',
|
||||
'stackId',
|
||||
'isVisible',
|
||||
'visibility',
|
||||
'smart_search.embedding',
|
||||
withFiles(eb, AssetFileType.PREVIEW),
|
||||
])
|
||||
@@ -70,7 +70,7 @@ export class AssetJobRepository {
|
||||
.select(['assets.id', 'assets.thumbhash'])
|
||||
.select(withFiles)
|
||||
.where('assets.deletedAt', 'is', null)
|
||||
.where('assets.isVisible', '=', true)
|
||||
.where('assets.visibility', '!=', AssetVisibility.HIDDEN)
|
||||
.$if(!force, (qb) =>
|
||||
qb
|
||||
// If there aren't any entries, metadata extraction hasn't run yet which is required for thumbnails
|
||||
@@ -102,7 +102,7 @@ export class AssetJobRepository {
|
||||
.selectFrom('assets')
|
||||
.select([
|
||||
'assets.id',
|
||||
'assets.isVisible',
|
||||
'assets.visibility',
|
||||
'assets.originalFileName',
|
||||
'assets.originalPath',
|
||||
'assets.ownerId',
|
||||
@@ -138,7 +138,7 @@ export class AssetJobRepository {
|
||||
private assetsWithPreviews() {
|
||||
return this.db
|
||||
.selectFrom('assets')
|
||||
.where('assets.isVisible', '=', true)
|
||||
.where('assets.visibility', '!=', AssetVisibility.HIDDEN)
|
||||
.where('assets.deletedAt', 'is', null)
|
||||
.innerJoin('asset_job_status as job_status', 'assetId', 'assets.id')
|
||||
.where('job_status.previewAt', 'is not', null);
|
||||
@@ -169,7 +169,7 @@ export class AssetJobRepository {
|
||||
getForClipEncoding(id: string) {
|
||||
return this.db
|
||||
.selectFrom('assets')
|
||||
.select(['assets.id', 'assets.isVisible'])
|
||||
.select(['assets.id', 'assets.visibility'])
|
||||
.select((eb) => withFiles(eb, AssetFileType.PREVIEW))
|
||||
.where('assets.id', '=', id)
|
||||
.executeTakeFirst();
|
||||
@@ -179,7 +179,7 @@ export class AssetJobRepository {
|
||||
getForDetectFacesJob(id: string) {
|
||||
return this.db
|
||||
.selectFrom('assets')
|
||||
.select(['assets.id', 'assets.isVisible'])
|
||||
.select(['assets.id', 'assets.visibility'])
|
||||
.$call(withExifInner)
|
||||
.select((eb) => withFaces(eb, true))
|
||||
.select((eb) => withFiles(eb, AssetFileType.PREVIEW))
|
||||
@@ -209,7 +209,7 @@ export class AssetJobRepository {
|
||||
.selectFrom('assets')
|
||||
.select([
|
||||
'assets.id',
|
||||
'assets.isVisible',
|
||||
'assets.visibility',
|
||||
'assets.libraryId',
|
||||
'assets.ownerId',
|
||||
'assets.livePhotoVideoId',
|
||||
@@ -228,7 +228,7 @@ export class AssetJobRepository {
|
||||
.select(['asset_stack.id', 'asset_stack.primaryAssetId'])
|
||||
.select((eb) => eb.fn<Asset[]>('array_agg', [eb.table('stacked')]).as('assets'))
|
||||
.where('stacked.deletedAt', 'is not', null)
|
||||
.where('stacked.isArchived', '=', false)
|
||||
.where('stacked.visibility', '!=', AssetVisibility.ARCHIVE)
|
||||
.whereRef('stacked.stackId', '=', 'asset_stack.id')
|
||||
.groupBy('asset_stack.id')
|
||||
.as('stacked_assets'),
|
||||
@@ -248,7 +248,7 @@ export class AssetJobRepository {
|
||||
.$if(!force, (qb) =>
|
||||
qb
|
||||
.where((eb) => eb.or([eb('assets.encodedVideoPath', 'is', null), eb('assets.encodedVideoPath', '=', '')]))
|
||||
.where('assets.isVisible', '=', true),
|
||||
.where('assets.visibility', '!=', AssetVisibility.HIDDEN),
|
||||
)
|
||||
.where('assets.deletedAt', 'is', null)
|
||||
.stream();
|
||||
@@ -275,7 +275,7 @@ export class AssetJobRepository {
|
||||
.where((eb) =>
|
||||
eb.or([eb('asset_job_status.metadataExtractedAt', 'is', null), eb('asset_job_status.assetId', 'is', null)]),
|
||||
)
|
||||
.where('assets.isVisible', '=', true),
|
||||
.where('assets.visibility', '!=', AssetVisibility.HIDDEN),
|
||||
)
|
||||
.where('assets.deletedAt', 'is', null)
|
||||
.stream();
|
||||
@@ -331,7 +331,7 @@ export class AssetJobRepository {
|
||||
.$if(!force, (qb) =>
|
||||
qb.where((eb) => eb.or([eb('assets.sidecarPath', '=', ''), eb('assets.sidecarPath', 'is', null)])),
|
||||
)
|
||||
.where('assets.isVisible', '=', true)
|
||||
.where('assets.visibility', '!=', AssetVisibility.HIDDEN)
|
||||
.stream();
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import { Stack } from 'src/database';
|
||||
import { AssetFiles, AssetJobStatus, Assets, DB, Exif } from 'src/db';
|
||||
import { Chunked, ChunkedArray, DummyValue, GenerateSql } from 'src/decorators';
|
||||
import { MapAsset } from 'src/dtos/asset-response.dto';
|
||||
import { AssetFileType, AssetOrder, AssetStatus, AssetType } from 'src/enum';
|
||||
import { AssetFileType, AssetOrder, AssetStatus, AssetType, AssetVisibility } from 'src/enum';
|
||||
import {
|
||||
anyUuid,
|
||||
asUuid,
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
removeUndefinedKeys,
|
||||
truncatedDate,
|
||||
unnest,
|
||||
withDefaultVisibility,
|
||||
withExif,
|
||||
withFaces,
|
||||
withFacesAndPeople,
|
||||
@@ -30,8 +31,8 @@ export type AssetStats = Record<AssetType, number>;
|
||||
|
||||
export interface AssetStatsOptions {
|
||||
isFavorite?: boolean;
|
||||
isArchived?: boolean;
|
||||
isTrashed?: boolean;
|
||||
visibility?: AssetVisibility;
|
||||
}
|
||||
|
||||
export interface LivePhotoSearchOptions {
|
||||
@@ -52,7 +53,6 @@ export enum TimeBucketSize {
|
||||
}
|
||||
|
||||
export interface AssetBuilderOptions {
|
||||
isArchived?: boolean;
|
||||
isFavorite?: boolean;
|
||||
isTrashed?: boolean;
|
||||
isDuplicate?: boolean;
|
||||
@@ -64,6 +64,7 @@ export interface AssetBuilderOptions {
|
||||
exifInfo?: boolean;
|
||||
status?: AssetStatus;
|
||||
assetType?: AssetType;
|
||||
visibility?: AssetVisibility;
|
||||
}
|
||||
|
||||
export interface TimeBucketOptions extends AssetBuilderOptions {
|
||||
@@ -258,8 +259,7 @@ export class AssetRepository {
|
||||
.where('asset_job_status.previewAt', 'is not', null)
|
||||
.where(sql`(assets."localDateTime" at time zone 'UTC')::date`, '=', sql`today.date`)
|
||||
.where('assets.ownerId', '=', anyUuid(ownerIds))
|
||||
.where('assets.isVisible', '=', true)
|
||||
.where('assets.isArchived', '=', false)
|
||||
.where('assets.visibility', '=', AssetVisibility.TIMELINE)
|
||||
.where((eb) =>
|
||||
eb.exists((qb) =>
|
||||
qb
|
||||
@@ -348,7 +348,7 @@ export class AssetRepository {
|
||||
.select(['deviceAssetId'])
|
||||
.where('ownerId', '=', asUuid(ownerId))
|
||||
.where('deviceId', '=', deviceId)
|
||||
.where('isVisible', '=', true)
|
||||
.where('visibility', '!=', AssetVisibility.HIDDEN)
|
||||
.where('deletedAt', 'is', null)
|
||||
.execute();
|
||||
|
||||
@@ -393,7 +393,7 @@ export class AssetRepository {
|
||||
.whereRef('stacked.stackId', '=', 'asset_stack.id')
|
||||
.whereRef('stacked.id', '!=', 'asset_stack.primaryAssetId')
|
||||
.where('stacked.deletedAt', 'is', null)
|
||||
.where('stacked.isArchived', '=', false)
|
||||
.where('stacked.visibility', '=', AssetVisibility.TIMELINE)
|
||||
.groupBy('asset_stack.id')
|
||||
.as('stacked_assets'),
|
||||
(join) => join.on('asset_stack.id', 'is not', null),
|
||||
@@ -503,7 +503,7 @@ export class AssetRepository {
|
||||
.executeTakeFirst();
|
||||
}
|
||||
|
||||
getStatistics(ownerId: string, { isArchived, isFavorite, isTrashed }: AssetStatsOptions): Promise<AssetStats> {
|
||||
getStatistics(ownerId: string, { visibility, isFavorite, isTrashed }: AssetStatsOptions): Promise<AssetStats> {
|
||||
return this.db
|
||||
.selectFrom('assets')
|
||||
.select((eb) => eb.fn.countAll<number>().filterWhere('type', '=', AssetType.AUDIO).as(AssetType.AUDIO))
|
||||
@@ -511,8 +511,8 @@ export class AssetRepository {
|
||||
.select((eb) => eb.fn.countAll<number>().filterWhere('type', '=', AssetType.VIDEO).as(AssetType.VIDEO))
|
||||
.select((eb) => eb.fn.countAll<number>().filterWhere('type', '=', AssetType.OTHER).as(AssetType.OTHER))
|
||||
.where('ownerId', '=', asUuid(ownerId))
|
||||
.where('isVisible', '=', true)
|
||||
.$if(isArchived !== undefined, (qb) => qb.where('isArchived', '=', isArchived!))
|
||||
.$if(visibility === undefined, withDefaultVisibility)
|
||||
.$if(!!visibility, (qb) => qb.where('assets.visibility', '=', visibility!))
|
||||
.$if(isFavorite !== undefined, (qb) => qb.where('isFavorite', '=', isFavorite!))
|
||||
.$if(!!isTrashed, (qb) => qb.where('assets.status', '!=', AssetStatus.DELETED))
|
||||
.where('deletedAt', isTrashed ? 'is not' : 'is', null)
|
||||
@@ -525,7 +525,7 @@ export class AssetRepository {
|
||||
.selectAll('assets')
|
||||
.$call(withExif)
|
||||
.where('ownerId', '=', anyUuid(userIds))
|
||||
.where('isVisible', '=', true)
|
||||
.where('visibility', '!=', AssetVisibility.HIDDEN)
|
||||
.where('deletedAt', 'is', null)
|
||||
.orderBy((eb) => eb.fn('random'))
|
||||
.limit(take)
|
||||
@@ -542,7 +542,8 @@ export class AssetRepository {
|
||||
.select(truncatedDate<Date>(options.size).as('timeBucket'))
|
||||
.$if(!!options.isTrashed, (qb) => qb.where('assets.status', '!=', AssetStatus.DELETED))
|
||||
.where('assets.deletedAt', options.isTrashed ? 'is not' : 'is', null)
|
||||
.where('assets.isVisible', '=', true)
|
||||
.$if(options.visibility === undefined, withDefaultVisibility)
|
||||
.$if(!!options.visibility, (qb) => qb.where('assets.visibility', '=', options.visibility!))
|
||||
.$if(!!options.albumId, (qb) =>
|
||||
qb
|
||||
.innerJoin('albums_assets_assets', 'assets.id', 'albums_assets_assets.assetsId')
|
||||
@@ -559,7 +560,6 @@ export class AssetRepository {
|
||||
.where((eb) => eb.or([eb('assets.stackId', 'is', null), eb(eb.table('asset_stack'), 'is not', null)])),
|
||||
)
|
||||
.$if(!!options.userIds, (qb) => qb.where('assets.ownerId', '=', anyUuid(options.userIds!)))
|
||||
.$if(options.isArchived !== undefined, (qb) => qb.where('assets.isArchived', '=', options.isArchived!))
|
||||
.$if(options.isFavorite !== undefined, (qb) => qb.where('assets.isFavorite', '=', options.isFavorite!))
|
||||
.$if(!!options.assetType, (qb) => qb.where('assets.type', '=', options.assetType!))
|
||||
.$if(options.isDuplicate !== undefined, (qb) =>
|
||||
@@ -594,7 +594,6 @@ export class AssetRepository {
|
||||
)
|
||||
.$if(!!options.personId, (qb) => hasPeople(qb, [options.personId!]))
|
||||
.$if(!!options.userIds, (qb) => qb.where('assets.ownerId', '=', anyUuid(options.userIds!)))
|
||||
.$if(options.isArchived !== undefined, (qb) => qb.where('assets.isArchived', '=', options.isArchived!))
|
||||
.$if(options.isFavorite !== undefined, (qb) => qb.where('assets.isFavorite', '=', options.isFavorite!))
|
||||
.$if(!!options.withStacked, (qb) =>
|
||||
qb
|
||||
@@ -610,7 +609,7 @@ export class AssetRepository {
|
||||
.select((eb) => eb.fn.count(eb.table('stacked')).as('assetCount'))
|
||||
.whereRef('stacked.stackId', '=', 'asset_stack.id')
|
||||
.where('stacked.deletedAt', 'is', null)
|
||||
.where('stacked.isArchived', '=', false)
|
||||
.where('stacked.visibility', '!=', AssetVisibility.ARCHIVE)
|
||||
.groupBy('asset_stack.id')
|
||||
.as('stacked_assets'),
|
||||
(join) => join.on('asset_stack.id', 'is not', null),
|
||||
@@ -624,7 +623,8 @@ export class AssetRepository {
|
||||
.$if(!!options.isTrashed, (qb) => qb.where('assets.status', '!=', AssetStatus.DELETED))
|
||||
.$if(!!options.tagId, (qb) => withTagId(qb, options.tagId!))
|
||||
.where('assets.deletedAt', options.isTrashed ? 'is not' : 'is', null)
|
||||
.where('assets.isVisible', '=', true)
|
||||
.$if(options.visibility == undefined, withDefaultVisibility)
|
||||
.$if(!!options.visibility, (qb) => qb.where('assets.visibility', '=', options.visibility!))
|
||||
.where(truncatedDate(options.size), '=', timeBucket.replace(/^[+-]/, ''))
|
||||
.orderBy('assets.localDateTime', options.order ?? 'desc')
|
||||
.execute();
|
||||
@@ -658,7 +658,7 @@ export class AssetRepository {
|
||||
.where('assets.duplicateId', 'is not', null)
|
||||
.$narrowType<{ duplicateId: NotNull }>()
|
||||
.where('assets.deletedAt', 'is', null)
|
||||
.where('assets.isVisible', '=', true)
|
||||
.where('assets.visibility', '!=', AssetVisibility.HIDDEN)
|
||||
.where('assets.stackId', 'is', null)
|
||||
.groupBy('assets.duplicateId'),
|
||||
)
|
||||
@@ -703,8 +703,7 @@ export class AssetRepository {
|
||||
.select(['assetId as data', 'exif.city as value'])
|
||||
.$narrowType<{ value: NotNull }>()
|
||||
.where('ownerId', '=', asUuid(ownerId))
|
||||
.where('isVisible', '=', true)
|
||||
.where('isArchived', '=', false)
|
||||
.where('visibility', '=', AssetVisibility.TIMELINE)
|
||||
.where('type', '=', AssetType.IMAGE)
|
||||
.where('deletedAt', 'is', null)
|
||||
.limit(maxFields)
|
||||
@@ -743,7 +742,7 @@ export class AssetRepository {
|
||||
)
|
||||
.select((eb) => eb.fn.toJson(eb.table('stacked_assets')).$castTo<Stack | null>().as('stack'))
|
||||
.where('assets.ownerId', '=', asUuid(ownerId))
|
||||
.where('assets.isVisible', '=', true)
|
||||
.where('assets.visibility', '!=', AssetVisibility.HIDDEN)
|
||||
.where('assets.updatedAt', '<=', updatedUntil)
|
||||
.$if(!!lastId, (qb) => qb.where('assets.id', '>', lastId!))
|
||||
.orderBy('assets.id')
|
||||
@@ -771,7 +770,7 @@ export class AssetRepository {
|
||||
)
|
||||
.select((eb) => eb.fn.toJson(eb.table('stacked_assets').$castTo<Stack | null>()).as('stack'))
|
||||
.where('assets.ownerId', '=', anyUuid(options.userIds))
|
||||
.where('assets.isVisible', '=', true)
|
||||
.where('assets.visibility', '!=', AssetVisibility.HIDDEN)
|
||||
.where('assets.updatedAt', '>', options.updatedAfter)
|
||||
.limit(options.limit)
|
||||
.execute();
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Injectable } from '@nestjs/common';
|
||||
import { Kysely } from 'kysely';
|
||||
import { InjectKysely } from 'nestjs-kysely';
|
||||
import { DB } from 'src/db';
|
||||
import { AssetVisibility } from 'src/enum';
|
||||
import { anyUuid } from 'src/utils/database';
|
||||
|
||||
const builder = (db: Kysely<DB>) =>
|
||||
@@ -31,6 +32,9 @@ export class DownloadRepository {
|
||||
}
|
||||
|
||||
downloadUserId(userId: string) {
|
||||
return builder(this.db).where('assets.ownerId', '=', userId).where('assets.isVisible', '=', true).stream();
|
||||
return builder(this.db)
|
||||
.where('assets.ownerId', '=', userId)
|
||||
.where('assets.visibility', '!=', AssetVisibility.HIDDEN)
|
||||
.stream();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { InjectKysely } from 'nestjs-kysely';
|
||||
import { DB, Libraries } from 'src/db';
|
||||
import { DummyValue, GenerateSql } from 'src/decorators';
|
||||
import { LibraryStatsResponseDto } from 'src/dtos/library.dto';
|
||||
import { AssetType } from 'src/enum';
|
||||
import { AssetType, AssetVisibility } from 'src/enum';
|
||||
|
||||
export enum AssetSyncResult {
|
||||
DO_NOTHING,
|
||||
@@ -77,13 +77,17 @@ export class LibraryRepository {
|
||||
.select((eb) =>
|
||||
eb.fn
|
||||
.countAll<number>()
|
||||
.filterWhere((eb) => eb.and([eb('assets.type', '=', AssetType.IMAGE), eb('assets.isVisible', '=', true)]))
|
||||
.filterWhere((eb) =>
|
||||
eb.and([eb('assets.type', '=', AssetType.IMAGE), eb('assets.visibility', '!=', AssetVisibility.HIDDEN)]),
|
||||
)
|
||||
.as('photos'),
|
||||
)
|
||||
.select((eb) =>
|
||||
eb.fn
|
||||
.countAll<number>()
|
||||
.filterWhere((eb) => eb.and([eb('assets.type', '=', AssetType.VIDEO), eb('assets.isVisible', '=', true)]))
|
||||
.filterWhere((eb) =>
|
||||
eb.and([eb('assets.type', '=', AssetType.VIDEO), eb('assets.visibility', '!=', AssetVisibility.HIDDEN)]),
|
||||
)
|
||||
.as('videos'),
|
||||
)
|
||||
.select((eb) => eb.fn.coalesce((eb) => eb.fn.sum('exif.fileSizeInByte'), eb.val(0)).as('usage'))
|
||||
|
||||
@@ -8,7 +8,7 @@ import readLine from 'node:readline';
|
||||
import { citiesFile } from 'src/constants';
|
||||
import { DB, GeodataPlaces, NaturalearthCountries } from 'src/db';
|
||||
import { DummyValue, GenerateSql } from 'src/decorators';
|
||||
import { SystemMetadataKey } from 'src/enum';
|
||||
import { AssetVisibility, SystemMetadataKey } from 'src/enum';
|
||||
import { ConfigRepository } from 'src/repositories/config.repository';
|
||||
import { LoggingRepository } from 'src/repositories/logging.repository';
|
||||
import { SystemMetadataRepository } from 'src/repositories/system-metadata.repository';
|
||||
@@ -75,9 +75,11 @@ export class MapRepository {
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [[DummyValue.UUID], [DummyValue.UUID]] })
|
||||
getMapMarkers(ownerIds: string[], albumIds: string[], options: MapMarkerSearchOptions = {}) {
|
||||
const { isArchived, isFavorite, fileCreatedAfter, fileCreatedBefore } = options;
|
||||
|
||||
getMapMarkers(
|
||||
ownerIds: string[],
|
||||
albumIds: string[],
|
||||
{ isArchived, isFavorite, fileCreatedAfter, fileCreatedBefore }: MapMarkerSearchOptions = {},
|
||||
) {
|
||||
return this.db
|
||||
.selectFrom('assets')
|
||||
.innerJoin('exif', (builder) =>
|
||||
@@ -88,8 +90,17 @@ export class MapRepository {
|
||||
)
|
||||
.select(['id', 'exif.latitude as lat', 'exif.longitude as lon', 'exif.city', 'exif.state', 'exif.country'])
|
||||
.$narrowType<{ lat: NotNull; lon: NotNull }>()
|
||||
.where('isVisible', '=', true)
|
||||
.$if(isArchived !== undefined, (q) => q.where('isArchived', '=', isArchived!))
|
||||
.$if(isArchived === true, (qb) =>
|
||||
qb.where((eb) =>
|
||||
eb.or([
|
||||
eb('assets.visibility', '=', AssetVisibility.TIMELINE),
|
||||
eb('assets.visibility', '=', AssetVisibility.ARCHIVE),
|
||||
]),
|
||||
),
|
||||
)
|
||||
.$if(isArchived === false || isArchived === undefined, (qb) =>
|
||||
qb.where('assets.visibility', '=', AssetVisibility.TIMELINE),
|
||||
)
|
||||
.$if(isFavorite !== undefined, (q) => q.where('isFavorite', '=', isFavorite!))
|
||||
.$if(fileCreatedAfter !== undefined, (q) => q.where('fileCreatedAt', '>=', fileCreatedAfter!))
|
||||
.$if(fileCreatedBefore !== undefined, (q) => q.where('fileCreatedAt', '<=', fileCreatedBefore!))
|
||||
|
||||
@@ -4,7 +4,7 @@ import { jsonObjectFrom } from 'kysely/helpers/postgres';
|
||||
import { InjectKysely } from 'nestjs-kysely';
|
||||
import { AssetFaces, DB, FaceSearch, Person } from 'src/db';
|
||||
import { ChunkedArray, DummyValue, GenerateSql } from 'src/decorators';
|
||||
import { AssetFileType, SourceType } from 'src/enum';
|
||||
import { AssetFileType, AssetVisibility, SourceType } from 'src/enum';
|
||||
import { removeUndefinedKeys } from 'src/utils/database';
|
||||
import { paginationHelper, PaginationOptions } from 'src/utils/pagination';
|
||||
|
||||
@@ -157,7 +157,7 @@ export class PersonRepository {
|
||||
.innerJoin('assets', (join) =>
|
||||
join
|
||||
.onRef('asset_faces.assetId', '=', 'assets.id')
|
||||
.on('assets.isArchived', '=', false)
|
||||
.on('assets.visibility', '!=', AssetVisibility.ARCHIVE)
|
||||
.on('assets.deletedAt', 'is', null),
|
||||
)
|
||||
.where('person.ownerId', '=', userId)
|
||||
@@ -248,7 +248,7 @@ export class PersonRepository {
|
||||
jsonObjectFrom(
|
||||
eb
|
||||
.selectFrom('assets')
|
||||
.select(['assets.ownerId', 'assets.isArchived', 'assets.fileCreatedAt'])
|
||||
.select(['assets.ownerId', 'assets.visibility', 'assets.fileCreatedAt'])
|
||||
.whereRef('assets.id', '=', 'asset_faces.assetId'),
|
||||
).as('asset'),
|
||||
)
|
||||
@@ -346,7 +346,7 @@ export class PersonRepository {
|
||||
join
|
||||
.onRef('assets.id', '=', 'asset_faces.assetId')
|
||||
.on('asset_faces.personId', '=', personId)
|
||||
.on('assets.isArchived', '=', false)
|
||||
.on('assets.visibility', '!=', AssetVisibility.ARCHIVE)
|
||||
.on('assets.deletedAt', 'is', null),
|
||||
)
|
||||
.select((eb) => eb.fn.count(eb.fn('distinct', ['assets.id'])).as('count'))
|
||||
@@ -369,7 +369,7 @@ export class PersonRepository {
|
||||
join
|
||||
.onRef('assets.id', '=', 'asset_faces.assetId')
|
||||
.on('assets.deletedAt', 'is', null)
|
||||
.on('assets.isArchived', '=', false),
|
||||
.on('assets.visibility', '!=', AssetVisibility.ARCHIVE),
|
||||
)
|
||||
.select((eb) => eb.fn.count(eb.fn('distinct', ['person.id'])).as('total'))
|
||||
.select((eb) =>
|
||||
|
||||
@@ -5,7 +5,7 @@ import { randomUUID } from 'node:crypto';
|
||||
import { DB, Exif } from 'src/db';
|
||||
import { DummyValue, GenerateSql } from 'src/decorators';
|
||||
import { MapAsset } from 'src/dtos/asset-response.dto';
|
||||
import { AssetStatus, AssetType } from 'src/enum';
|
||||
import { AssetStatus, AssetType, AssetVisibility } from 'src/enum';
|
||||
import { ConfigRepository } from 'src/repositories/config.repository';
|
||||
import { anyUuid, asUuid, searchAssetBuilder, vectorIndexQuery } from 'src/utils/database';
|
||||
import { paginationHelper } from 'src/utils/pagination';
|
||||
@@ -26,17 +26,16 @@ export interface SearchUserIdOptions {
|
||||
export type SearchIdOptions = SearchAssetIdOptions & SearchUserIdOptions;
|
||||
|
||||
export interface SearchStatusOptions {
|
||||
isArchived?: boolean;
|
||||
isEncoded?: boolean;
|
||||
isFavorite?: boolean;
|
||||
isMotion?: boolean;
|
||||
isOffline?: boolean;
|
||||
isVisible?: boolean;
|
||||
isNotInAlbum?: boolean;
|
||||
type?: AssetType;
|
||||
status?: AssetStatus;
|
||||
withArchived?: boolean;
|
||||
withDeleted?: boolean;
|
||||
visibility?: AssetVisibility;
|
||||
}
|
||||
|
||||
export interface SearchOneToOneRelationOptions {
|
||||
@@ -276,7 +275,7 @@ export class SearchRepository {
|
||||
.innerJoin('smart_search', 'assets.id', 'smart_search.assetId')
|
||||
.where('assets.ownerId', '=', anyUuid(userIds))
|
||||
.where('assets.deletedAt', 'is', null)
|
||||
.where('assets.isVisible', '=', true)
|
||||
.where('assets.visibility', '!=', AssetVisibility.HIDDEN)
|
||||
.where('assets.type', '=', type)
|
||||
.where('assets.id', '!=', asUuid(assetId))
|
||||
.where('assets.stackId', 'is', null)
|
||||
@@ -367,8 +366,7 @@ export class SearchRepository {
|
||||
.select(['city', 'assetId'])
|
||||
.innerJoin('assets', 'assets.id', 'exif.assetId')
|
||||
.where('assets.ownerId', '=', anyUuid(userIds))
|
||||
.where('assets.isVisible', '=', true)
|
||||
.where('assets.isArchived', '=', false)
|
||||
.where('assets.visibility', '=', AssetVisibility.TIMELINE)
|
||||
.where('assets.type', '=', AssetType.IMAGE)
|
||||
.where('assets.deletedAt', 'is', null)
|
||||
.orderBy('city')
|
||||
@@ -384,8 +382,7 @@ export class SearchRepository {
|
||||
.select(['city', 'assetId'])
|
||||
.innerJoin('assets', 'assets.id', 'exif.assetId')
|
||||
.where('assets.ownerId', '=', anyUuid(userIds))
|
||||
.where('assets.isVisible', '=', true)
|
||||
.where('assets.isArchived', '=', false)
|
||||
.where('assets.visibility', '=', AssetVisibility.TIMELINE)
|
||||
.where('assets.type', '=', AssetType.IMAGE)
|
||||
.where('assets.deletedAt', 'is', null)
|
||||
.whereRef('exif.city', '>', 'cte.city')
|
||||
@@ -518,7 +515,7 @@ export class SearchRepository {
|
||||
.distinctOn(field)
|
||||
.innerJoin('assets', 'assets.id', 'exif.assetId')
|
||||
.where('ownerId', '=', anyUuid(userIds))
|
||||
.where('isVisible', '=', true)
|
||||
.where('visibility', '!=', AssetVisibility.HIDDEN)
|
||||
.where('deletedAt', 'is', null)
|
||||
.where(field, 'is not', null);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { InjectKysely } from 'nestjs-kysely';
|
||||
import { columns } from 'src/database';
|
||||
import { DB, UserMetadata as DbUserMetadata } from 'src/db';
|
||||
import { DummyValue, GenerateSql } from 'src/decorators';
|
||||
import { AssetType, UserStatus } from 'src/enum';
|
||||
import { AssetType, AssetVisibility, UserStatus } from 'src/enum';
|
||||
import { UserTable } from 'src/schema/tables/user.table';
|
||||
import { UserMetadata, UserMetadataItem } from 'src/types';
|
||||
import { asUuid } from 'src/utils/database';
|
||||
@@ -205,13 +205,19 @@ export class UserRepository {
|
||||
eb.fn
|
||||
.countAll<number>()
|
||||
.filterWhere((eb) =>
|
||||
eb.and([eb('assets.type', '=', sql.lit(AssetType.IMAGE)), eb('assets.isVisible', '=', sql.lit(true))]),
|
||||
eb.and([
|
||||
eb('assets.type', '=', sql.lit(AssetType.IMAGE)),
|
||||
eb('assets.visibility', '!=', sql.lit(AssetVisibility.HIDDEN)),
|
||||
]),
|
||||
)
|
||||
.as('photos'),
|
||||
eb.fn
|
||||
.countAll<number>()
|
||||
.filterWhere((eb) =>
|
||||
eb.and([eb('assets.type', '=', sql.lit(AssetType.VIDEO)), eb('assets.isVisible', '=', sql.lit(true))]),
|
||||
eb.and([
|
||||
eb('assets.type', '=', sql.lit(AssetType.VIDEO)),
|
||||
eb('assets.visibility', '!=', sql.lit(AssetVisibility.HIDDEN)),
|
||||
]),
|
||||
)
|
||||
.as('videos'),
|
||||
eb.fn
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Kysely } from 'kysely';
|
||||
import { InjectKysely } from 'nestjs-kysely';
|
||||
import { DB } from 'src/db';
|
||||
import { DummyValue, GenerateSql } from 'src/decorators';
|
||||
import { AssetVisibility } from 'src/enum';
|
||||
import { asUuid, withExif } from 'src/utils/database';
|
||||
|
||||
export class ViewRepository {
|
||||
@@ -14,8 +15,7 @@ export class ViewRepository {
|
||||
.select((eb) => eb.fn<string>('substring', ['assets.originalPath', eb.val('^(.*/)[^/]*$')]).as('directoryPath'))
|
||||
.distinct()
|
||||
.where('ownerId', '=', asUuid(userId))
|
||||
.where('isVisible', '=', true)
|
||||
.where('isArchived', '=', false)
|
||||
.where('visibility', '=', AssetVisibility.TIMELINE)
|
||||
.where('deletedAt', 'is', null)
|
||||
.where('fileCreatedAt', 'is not', null)
|
||||
.where('fileModifiedAt', 'is not', null)
|
||||
@@ -34,8 +34,7 @@ export class ViewRepository {
|
||||
.selectAll('assets')
|
||||
.$call(withExif)
|
||||
.where('ownerId', '=', asUuid(userId))
|
||||
.where('isVisible', '=', true)
|
||||
.where('isArchived', '=', false)
|
||||
.where('visibility', '=', AssetVisibility.TIMELINE)
|
||||
.where('deletedAt', 'is', null)
|
||||
.where('fileCreatedAt', 'is not', null)
|
||||
.where('fileModifiedAt', 'is not', null)
|
||||
|
||||
Reference in New Issue
Block a user