mirror of
https://github.com/immich-app/immich.git
synced 2026-03-01 18:19:10 +03:00
feat(server): library refresh go brrr (#14456)
* feat: brr --------- Co-authored-by: mertalev <101130780+mertalev@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
bc61497461
commit
3af26ee94a
@@ -1,5 +1,5 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Insertable, Kysely, Updateable, sql } from 'kysely';
|
||||
import { Insertable, Kysely, UpdateResult, Updateable, sql } from 'kysely';
|
||||
import { isEmpty, isUndefined, omitBy } from 'lodash';
|
||||
import { InjectKysely } from 'nestjs-kysely';
|
||||
import { ASSET_FILE_CONFLICT_KEYS, EXIF_CONFLICT_KEYS, JOB_STATUS_CONFLICT_KEYS } from 'src/constants';
|
||||
@@ -24,7 +24,8 @@ import {
|
||||
} from 'src/entities/asset.entity';
|
||||
import { AssetFileType, AssetOrder, AssetStatus, AssetType } from 'src/enum';
|
||||
import { AssetSearchOptions, SearchExploreItem, SearchExploreItemSet } from 'src/repositories/search.repository';
|
||||
import { anyUuid, asUuid, mapUpsertColumns } from 'src/utils/database';
|
||||
import { anyUuid, asUuid, mapUpsertColumns, unnest } from 'src/utils/database';
|
||||
import { globToSqlPattern } from 'src/utils/misc';
|
||||
import { Paginated, PaginationOptions, paginationHelper } from 'src/utils/pagination';
|
||||
|
||||
export type AssetStats = Record<AssetType, number>;
|
||||
@@ -191,6 +192,10 @@ export class AssetRepository {
|
||||
.executeTakeFirst() as any as Promise<AssetEntityPlaceholder>;
|
||||
}
|
||||
|
||||
createAll(assets: Insertable<Assets>[]): Promise<AssetEntity[]> {
|
||||
return this.db.insertInto('assets').values(assets).returningAll().execute() as any as Promise<AssetEntity[]>;
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID, { day: 1, month: 1 }] })
|
||||
getByDayOfYear(ownerIds: string[], { day, month }: MonthDay) {
|
||||
return this.db
|
||||
@@ -384,6 +389,17 @@ export class AssetRepository {
|
||||
return paginationHelper(items as any as AssetEntity[], pagination.take);
|
||||
}
|
||||
|
||||
async getAllInLibrary(pagination: PaginationOptions, libraryId: string): Paginated<AssetEntity> {
|
||||
const builder = this.db
|
||||
.selectFrom('assets')
|
||||
.select('id')
|
||||
.where('libraryId', '=', asUuid(libraryId))
|
||||
.limit(pagination.take + 1)
|
||||
.offset(pagination.skip ?? 0);
|
||||
const items = await builder.execute();
|
||||
return paginationHelper(items as any as AssetEntity[], pagination.take);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get assets by device's Id on the database
|
||||
* @param ownerId
|
||||
@@ -470,6 +486,10 @@ export class AssetRepository {
|
||||
await this.db.updateTable('assets').set(options).where('id', '=', anyUuid(ids)).execute();
|
||||
}
|
||||
|
||||
async updateByLibraryId(libraryId: string, options: Updateable<Assets>): Promise<void> {
|
||||
await this.db.updateTable('assets').set(options).where('libraryId', '=', asUuid(libraryId)).execute();
|
||||
}
|
||||
|
||||
@GenerateSql({
|
||||
params: [{ targetDuplicateId: DummyValue.UUID, duplicateIds: [DummyValue.UUID], assetIds: [DummyValue.UUID] }],
|
||||
})
|
||||
@@ -939,4 +959,64 @@ export class AssetRepository {
|
||||
)
|
||||
.execute();
|
||||
}
|
||||
|
||||
@GenerateSql({
|
||||
params: [{ libraryId: DummyValue.UUID, importPaths: [DummyValue.STRING], exclusionPatterns: [DummyValue.STRING] }],
|
||||
})
|
||||
async detectOfflineExternalAssets(
|
||||
libraryId: string,
|
||||
importPaths: string[],
|
||||
exclusionPatterns: string[],
|
||||
): Promise<UpdateResult> {
|
||||
const paths = importPaths.map((importPath) => `${importPath}%`);
|
||||
const exclusions = exclusionPatterns.map((pattern) => globToSqlPattern(pattern));
|
||||
|
||||
return this.db
|
||||
.updateTable('assets')
|
||||
.set({
|
||||
isOffline: true,
|
||||
deletedAt: new Date(),
|
||||
})
|
||||
.where('isOffline', '=', false)
|
||||
.where('isExternal', '=', true)
|
||||
.where('libraryId', '=', asUuid(libraryId))
|
||||
.where((eb) =>
|
||||
eb.or([eb('originalPath', 'not like', paths.join('|')), eb('originalPath', 'like', exclusions.join('|'))]),
|
||||
)
|
||||
.executeTakeFirstOrThrow();
|
||||
}
|
||||
|
||||
@GenerateSql({
|
||||
params: [{ libraryId: DummyValue.UUID, paths: [DummyValue.STRING] }],
|
||||
})
|
||||
async filterNewExternalAssetPaths(libraryId: string, paths: string[]): Promise<string[]> {
|
||||
const result = await this.db
|
||||
.selectFrom(unnest(paths).as('path'))
|
||||
.select('path')
|
||||
.where((eb) =>
|
||||
eb.not(
|
||||
eb.exists(
|
||||
this.db
|
||||
.selectFrom('assets')
|
||||
.select('originalPath')
|
||||
.whereRef('assets.originalPath', '=', eb.ref('path'))
|
||||
.where('libraryId', '=', asUuid(libraryId))
|
||||
.where('isExternal', '=', true),
|
||||
),
|
||||
),
|
||||
)
|
||||
.execute();
|
||||
|
||||
return result.map((row) => row.path as string);
|
||||
}
|
||||
|
||||
async getLibraryAssetCount(libraryId: string): Promise<number> {
|
||||
const { count } = await this.db
|
||||
.selectFrom('assets')
|
||||
.select((eb) => eb.fn.countAll().as('count'))
|
||||
.where('libraryId', '=', asUuid(libraryId))
|
||||
.executeTakeFirstOrThrow();
|
||||
|
||||
return Number(count);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,13 @@ const userColumns = [
|
||||
'users.profileChangedAt',
|
||||
] as const;
|
||||
|
||||
export enum AssetSyncResult {
|
||||
DO_NOTHING,
|
||||
UPDATE,
|
||||
OFFLINE,
|
||||
CHECK_OFFLINE,
|
||||
}
|
||||
|
||||
const withOwner = (eb: ExpressionBuilder<DB, 'libraries'>) => {
|
||||
return jsonObjectFrom(eb.selectFrom('users').whereRef('users.id', '=', 'libraries.ownerId').select(userColumns)).as(
|
||||
'owner',
|
||||
|
||||
Reference in New Issue
Block a user