mirror of
https://github.com/immich-app/immich
synced 2025-11-14 17:36:12 +00:00
feat(server): random assets API (#4184)
* feat(server): get random assets API * Fix tests * Use correct validation annotation * Fix offset use in query * Update API specs * Fix typo * Random assets e2e tests * Improve e2e tests
This commit is contained in:
parent
fc64be6603
commit
014d164d99
14 changed files with 429 additions and 3 deletions
|
|
@ -78,6 +78,7 @@ export interface IAssetRepository {
|
|||
getByUserId(pagination: PaginationOptions, userId: string): Paginated<AssetEntity>;
|
||||
getWithout(pagination: PaginationOptions, property: WithoutProperty): Paginated<AssetEntity>;
|
||||
getWith(pagination: PaginationOptions, property: WithProperty, libraryId?: string): Paginated<AssetEntity>;
|
||||
getRandom(userId: string, count: number): Promise<AssetEntity[]>;
|
||||
getFirstAssetForAlbumId(albumId: string): Promise<AssetEntity | null>;
|
||||
getLastUpdatedAssetForAlbumId(albumId: string): Promise<AssetEntity | null>;
|
||||
getByLibraryId(libraryIds: string[]): Promise<AssetEntity[]>;
|
||||
|
|
|
|||
|
|
@ -284,6 +284,11 @@ export class AssetService {
|
|||
return mapStats(stats);
|
||||
}
|
||||
|
||||
async getRandom(authUser: AuthUserDto, count: number): Promise<AssetResponseDto[]> {
|
||||
const assets = await this.assetRepository.getRandom(authUser.id, count);
|
||||
return assets.map((a) => mapAsset(a));
|
||||
}
|
||||
|
||||
async update(authUser: AuthUserDto, id: string, dto: UpdateAssetDto): Promise<AssetResponseDto> {
|
||||
await this.access.requirePermission(authUser, Permission.ASSET_UPDATE, id);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { IsBoolean, IsString } from 'class-validator';
|
||||
import { Type } from 'class-transformer';
|
||||
import { IsBoolean, IsInt, IsPositive, IsString } from 'class-validator';
|
||||
import { Optional } from '../../domain.util';
|
||||
import { BulkIdsDto } from '../response-dto';
|
||||
|
||||
|
|
@ -25,3 +26,11 @@ export class UpdateAssetDto {
|
|||
@IsString()
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export class RandomAssetsDto {
|
||||
@Optional()
|
||||
@IsInt()
|
||||
@IsPositive()
|
||||
@Type(() => Number)
|
||||
count?: number;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import {
|
|||
MapMarkerResponseDto,
|
||||
MemoryLaneDto,
|
||||
MemoryLaneResponseDto,
|
||||
RandomAssetsDto,
|
||||
TimeBucketAssetDto,
|
||||
TimeBucketDto,
|
||||
TimeBucketResponseDto,
|
||||
|
|
@ -41,6 +42,11 @@ export class AssetController {
|
|||
return this.service.getMemoryLane(authUser, dto);
|
||||
}
|
||||
|
||||
@Get('random')
|
||||
getRandom(@AuthUser() authUser: AuthUserDto, @Query() dto: RandomAssetsDto): Promise<AssetResponseDto[]> {
|
||||
return this.service.getRandom(authUser, dto.count ?? 1);
|
||||
}
|
||||
|
||||
@SharedLinkRoute()
|
||||
@Post('download/info')
|
||||
getDownloadInfo(@AuthUser() authUser: AuthUserDto, @Body() dto: DownloadInfoDto): Promise<DownloadResponseDto> {
|
||||
|
|
|
|||
|
|
@ -429,6 +429,17 @@ export class AssetRepository implements IAssetRepository {
|
|||
return result;
|
||||
}
|
||||
|
||||
getRandom(ownerId: string, count: number): Promise<AssetEntity[]> {
|
||||
// can't use queryBuilder because of custom OFFSET clause
|
||||
return this.repository.query(
|
||||
`SELECT *
|
||||
FROM assets
|
||||
WHERE "ownerId" = $1
|
||||
OFFSET FLOOR(RANDOM() * (SELECT GREATEST(COUNT(*) - 1, 0) FROM ASSETS)) LIMIT $2`,
|
||||
[ownerId, count],
|
||||
);
|
||||
}
|
||||
|
||||
getTimeBuckets(options: TimeBucketOptions): Promise<TimeBucketItem[]> {
|
||||
const truncateValue = truncateMap[options.size];
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue