Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
etnoy committed Jan 21, 2025
1 parent 24de62f commit 0830e9d
Show file tree
Hide file tree
Showing 19 changed files with 274 additions and 54 deletions.
1 change: 1 addition & 0 deletions mobile/openapi/README.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions mobile/openapi/lib/api.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions mobile/openapi/lib/api/libraries_api.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions mobile/openapi/lib/api_client.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

123 changes: 123 additions & 0 deletions mobile/openapi/lib/model/library_stats_response_dto.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 29 additions & 1 deletion open-api/immich-openapi-specs.json
Original file line number Diff line number Diff line change
Expand Up @@ -2907,7 +2907,7 @@
"content": {
"application/json": {
"schema": {
"type": "number"
"$ref": "#/components/schemas/LibraryStatsResponseDto"
}
}
},
Expand Down Expand Up @@ -9540,6 +9540,34 @@
],
"type": "object"
},
"LibraryStatsResponseDto": {
"properties": {
"photos": {
"default": 0,
"type": "integer"
},
"total": {
"default": 0,
"type": "integer"
},
"usage": {
"default": 0,
"format": "int64",
"type": "integer"
},
"videos": {
"default": 0,
"type": "integer"
}
},
"required": [
"photos",
"total",
"usage",
"videos"
],
"type": "object"
},
"LicenseKeyDto": {
"properties": {
"activationKey": {
Expand Down
8 changes: 7 additions & 1 deletion open-api/typescript-sdk/src/fetch-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,12 @@ export type UpdateLibraryDto = {
importPaths?: string[];
name?: string;
};
export type LibraryStatsResponseDto = {
photos: number;
total: number;
usage: number;
videos: number;
};
export type ValidateLibraryDto = {
exclusionPatterns?: string[];
importPaths?: string[];
Expand Down Expand Up @@ -2106,7 +2112,7 @@ export function getLibraryStatistics({ id }: {
}, opts?: Oazapfts.RequestOpts) {
return oazapfts.ok(oazapfts.fetchJson<{
status: 200;
data: number;
data: LibraryStatsResponseDto;
}>(`/libraries/${encodeURIComponent(id)}/statistics`, {
...opts
}));
Expand Down
3 changes: 2 additions & 1 deletion server/src/controllers/library.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ApiTags } from '@nestjs/swagger';
import {
CreateLibraryDto,
LibraryResponseDto,
LibraryStatsResponseDto,
UpdateLibraryDto,
ValidateLibraryDto,
ValidateLibraryResponseDto,
Expand Down Expand Up @@ -58,7 +59,7 @@ export class LibraryController {

@Get(':id/statistics')
@Authenticated({ permission: Permission.LIBRARY_STATISTICS, admin: true })
getLibraryStatistics(@Param() { id }: UUIDParamDto): Promise<number> {
getLibraryStatistics(@Param() { id }: UUIDParamDto): Promise<LibraryStatsResponseDto> {
return this.service.getStatistics(id);
}

Expand Down
7 changes: 5 additions & 2 deletions server/src/interfaces/asset.interface.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Insertable, Updateable, UpdateResult } from 'kysely';
import { AssetJobStatus, Assets, Exif } from 'src/db';
import { AssetEntity } from 'src/entities/asset.entity';
import { LibraryEntity } from 'src/entities/library.entity';
import { AssetFileType, AssetOrder, AssetStatus, AssetType } from 'src/enum';
import { AssetSearchOptions, SearchExploreItem } from 'src/interfaces/search.interface';
import { Paginated, PaginationOptions } from 'src/utils/pagination';
Expand Down Expand Up @@ -171,7 +170,11 @@ export interface IAssetRepository {
getChangedDeltaSync(options: AssetDeltaSyncOptions): Promise<AssetEntity[]>;
upsertFile(file: UpsertFileOptions): Promise<void>;
upsertFiles(files: UpsertFileOptions[]): Promise<void>;
detectOfflineExternalAssets(library: LibraryEntity): Promise<UpdateResult>;
detectOfflineExternalAssets(
libraryId: string,
importPaths: string[],
exclusionPatterns: string[],
): Promise<UpdateResult>;
filterNewExternalAssetPaths(libraryId: string, paths: string[]): Promise<string[]>;
getLibraryAssetCount(options: AssetSearchOptions): Promise<number | undefined>;
}
2 changes: 1 addition & 1 deletion server/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ if (immichApp) {
let apiProcess: ChildProcess | undefined;

const onError = (name: string, error: Error) => {
console.error(`${name} worker error: ${error}`);
console.error(`${name} worker error: ${error}, stack: ${error.stack}`);
};

const onExit = (name: string, exitCode: number | null) => {
Expand Down
12 changes: 7 additions & 5 deletions server/src/queries/asset.repository.sql
Original file line number Diff line number Diff line change
Expand Up @@ -262,8 +262,9 @@ with
from
"assets"
where
"assets"."deletedAt" is null
and "assets"."isVisible" = $2
"assets"."fileCreatedAt" <= $2
and "assets"."deletedAt" is null
and "assets"."isVisible" = $3
)
select
"timeBucket",
Expand All @@ -283,9 +284,10 @@ from
"assets"
left join "exif" on "assets"."id" = "exif"."assetId"
where
"assets"."deletedAt" is null
and "assets"."isVisible" = $1
and date_trunc($2, "localDateTime" at time zone 'UTC') at time zone 'UTC' = $3
"assets"."fileCreatedAt" <= $1
and "assets"."deletedAt" is null
and "assets"."isVisible" = $2
and date_trunc($3, "localDateTime" at time zone 'UTC') at time zone 'UTC' = $4
order by
"assets"."localDateTime" desc

Expand Down
2 changes: 1 addition & 1 deletion server/src/queries/library.repository.sql
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ select
from
"libraries"
inner join "assets" on "assets"."libraryId" = "libraries"."id"
inner join "exif" on "exif"."assetId" = "assets"."id"
left join "exif" on "exif"."assetId" = "assets"."id"
where
"libraries"."id" = $6
group by
Expand Down
24 changes: 19 additions & 5 deletions server/src/repositories/asset.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import {
withStack,
withTags,
} from 'src/entities/asset.entity';
import { LibraryEntity } from 'src/entities/library.entity';
import { AssetFileType, AssetStatus, AssetType } from 'src/enum';
import {
AssetDeltaSyncOptions,
Expand Down Expand Up @@ -50,6 +49,8 @@ import { anyUuid, asUuid, mapUpsertColumns } from 'src/utils/database';
import { globToSqlPattern } from 'src/utils/misc';
import { Paginated, PaginationOptions, paginationHelper } from 'src/utils/pagination';

const ASSET_CUTOFF_DATE = new Date('9000-01-01');

@Injectable()
export class AssetRepository implements IAssetRepository {
constructor(@InjectKysely() private db: Kysely<DB>) {}
Expand Down Expand Up @@ -527,6 +528,7 @@ export class AssetRepository implements IAssetRepository {
return this.db
.selectFrom('assets')
.selectAll('assets')
.where('assets.fileCreatedAt', '<=', ASSET_CUTOFF_DATE)
.$call(withExif)
.where('ownerId', '=', anyUuid(userIds))
.where('isVisible', '=', true)
Expand All @@ -543,6 +545,7 @@ export class AssetRepository implements IAssetRepository {
.with('assets', (qb) =>
qb
.selectFrom('assets')
.where('assets.fileCreatedAt', '<=', ASSET_CUTOFF_DATE)
.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)
Expand Down Expand Up @@ -592,6 +595,7 @@ export class AssetRepository implements IAssetRepository {
async getTimeBucket(timeBucket: string, options: TimeBucketOptions): Promise<AssetEntity[]> {
return hasPeople(this.db, options.personId ? [options.personId] : undefined)
.selectAll('assets')
.where('assets.fileCreatedAt', '<=', ASSET_CUTOFF_DATE)
.$call(withExif)
.$if(!!options.albumId, (qb) => withAlbums(qb, { albumId: options.albumId }))
.$if(!!options.userIds, (qb) => qb.where('assets.ownerId', '=', anyUuid(options.userIds!)))
Expand Down Expand Up @@ -748,9 +752,16 @@ export class AssetRepository implements IAssetRepository {
.execute();
}

async detectOfflineExternalAssets(library: LibraryEntity): Promise<UpdateResult> {
const paths = library.importPaths.map((importPath) => `${importPath}%`);
const exclusions = library.exclusionPatterns.map((pattern) => globToSqlPattern(pattern));
@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')
Expand All @@ -760,13 +771,16 @@ export class AssetRepository implements IAssetRepository {
})
.where('isOffline', '=', false)
.where('isExternal', '=', true)
.where('libraryId', '=', asUuid(library.id))
.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(
Expand Down
Loading

0 comments on commit 0830e9d

Please sign in to comment.