1
0
mirror of https://github.com/immich-app/immich.git synced 2024-11-30 09:47:31 +02:00

fix(server, web): reassigning faces (#9265)

* fix: reassiging faces

* fix: rename
This commit is contained in:
martin 2024-05-05 20:16:44 +02:00 committed by GitHub
parent 090592e5ae
commit cf79bc9ed7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 50 additions and 27 deletions

View File

@ -5,7 +5,7 @@ import { ExifEntity } from 'src/entities/exif.entity';
import { ReverseGeocodeResult } from 'src/interfaces/metadata.interface'; import { ReverseGeocodeResult } from 'src/interfaces/metadata.interface';
import { AssetSearchOptions, SearchExploreItem } from 'src/interfaces/search.interface'; import { AssetSearchOptions, SearchExploreItem } from 'src/interfaces/search.interface';
import { Paginated, PaginationOptions } from 'src/utils/pagination'; import { Paginated, PaginationOptions } from 'src/utils/pagination';
import { FindOptionsRelations, FindOptionsSelect } from 'typeorm'; import { FindOptionsOrder, FindOptionsRelations, FindOptionsSelect } from 'typeorm';
export type AssetStats = Record<AssetType, number>; export type AssetStats = Record<AssetType, number>;
@ -162,7 +162,11 @@ export interface IAssetRepository {
getUploadAssetIdByChecksum(ownerId: string, checksum: Buffer): Promise<string | undefined>; getUploadAssetIdByChecksum(ownerId: string, checksum: Buffer): Promise<string | undefined>;
getByAlbumId(pagination: PaginationOptions, albumId: string): Paginated<AssetEntity>; getByAlbumId(pagination: PaginationOptions, albumId: string): Paginated<AssetEntity>;
getByUserId(pagination: PaginationOptions, userId: string, options?: AssetSearchOptions): Paginated<AssetEntity>; getByUserId(pagination: PaginationOptions, userId: string, options?: AssetSearchOptions): Paginated<AssetEntity>;
getById(id: string, relations?: FindOptionsRelations<AssetEntity>): Promise<AssetEntity | null>; getById(
id: string,
relations?: FindOptionsRelations<AssetEntity>,
order?: FindOptionsOrder<AssetEntity>,
): Promise<AssetEntity | null>;
getWithout(pagination: PaginationOptions, property: WithoutProperty): Paginated<AssetEntity>; getWithout(pagination: PaginationOptions, property: WithoutProperty): Paginated<AssetEntity>;
getWith(pagination: PaginationOptions, property: WithProperty, libraryId?: string): Paginated<AssetEntity>; getWith(pagination: PaginationOptions, property: WithProperty, libraryId?: string): Paginated<AssetEntity>;
getRandom(userId: string, count: number): Promise<AssetEntity[]>; getRandom(userId: string, count: number): Promise<AssetEntity[]>;

View File

@ -85,6 +85,8 @@ FROM
LEFT JOIN "person" "AssetFaceEntity__AssetFaceEntity_person" ON "AssetFaceEntity__AssetFaceEntity_person"."id" = "AssetFaceEntity"."personId" LEFT JOIN "person" "AssetFaceEntity__AssetFaceEntity_person" ON "AssetFaceEntity__AssetFaceEntity_person"."id" = "AssetFaceEntity"."personId"
WHERE WHERE
(("AssetFaceEntity"."assetId" = $1)) (("AssetFaceEntity"."assetId" = $1))
ORDER BY
"AssetFaceEntity"."boundingBoxX1" ASC
-- PersonRepository.getFaceById -- PersonRepository.getFaceById
SELECT DISTINCT SELECT DISTINCT

View File

@ -36,6 +36,7 @@ import { Instrumentation } from 'src/utils/instrumentation';
import { Paginated, PaginationMode, PaginationOptions, paginate, paginatedBuilder } from 'src/utils/pagination'; import { Paginated, PaginationMode, PaginationOptions, paginate, paginatedBuilder } from 'src/utils/pagination';
import { import {
Brackets, Brackets,
FindOptionsOrder,
FindOptionsRelations, FindOptionsRelations,
FindOptionsSelect, FindOptionsSelect,
FindOptionsWhere, FindOptionsWhere,
@ -236,12 +237,17 @@ export class AssetRepository implements IAssetRepository {
} }
@GenerateSql({ params: [DummyValue.UUID] }) @GenerateSql({ params: [DummyValue.UUID] })
getById(id: string, relations: FindOptionsRelations<AssetEntity>): Promise<AssetEntity | null> { getById(
id: string,
relations: FindOptionsRelations<AssetEntity>,
order?: FindOptionsOrder<AssetEntity>,
): Promise<AssetEntity | null> {
return this.repository.findOne({ return this.repository.findOne({
where: { id }, where: { id },
relations, relations,
// We are specifically asking for this asset. Return it even if it is soft deleted // We are specifically asking for this asset. Return it even if it is soft deleted
withDeleted: true, withDeleted: true,
order,
}); });
} }

View File

@ -104,6 +104,9 @@ export class PersonRepository implements IPersonRepository {
relations: { relations: {
person: true, person: true,
}, },
order: {
boundingBoxX1: 'ASC',
},
}); });
} }

View File

@ -227,21 +227,29 @@ export class AssetService {
async get(auth: AuthDto, id: string): Promise<AssetResponseDto | SanitizedAssetResponseDto> { async get(auth: AuthDto, id: string): Promise<AssetResponseDto | SanitizedAssetResponseDto> {
await this.access.requirePermission(auth, Permission.ASSET_READ, id); await this.access.requirePermission(auth, Permission.ASSET_READ, id);
const asset = await this.assetRepository.getById(id, { const asset = await this.assetRepository.getById(
exifInfo: true, id,
tags: true, {
sharedLinks: true, exifInfo: true,
smartInfo: true, tags: true,
owner: true, sharedLinks: true,
faces: { smartInfo: true,
person: true, owner: true,
}, faces: {
stack: { person: true,
assets: { },
exifInfo: true, stack: {
assets: {
exifInfo: true,
},
}, },
}, },
}); {
faces: {
boundingBoxX1: 'ASC',
},
},
);
if (!asset) { if (!asset) {
throw new BadRequestException('Asset not found'); throw new BadRequestException('Asset not found');

View File

@ -36,6 +36,7 @@
let selectedPersonToReassign: Record<string, PersonResponseDto> = {}; let selectedPersonToReassign: Record<string, PersonResponseDto> = {};
let selectedPersonToCreate: Record<string, string> = {}; let selectedPersonToCreate: Record<string, string> = {};
let editedPerson: PersonResponseDto; let editedPerson: PersonResponseDto;
let editedFace: AssetFaceResponseDto;
// loading spinners // loading spinners
let isShowLoadingDone = false; let isShowLoadingDone = false;
@ -155,24 +156,23 @@
}; };
const handleCreatePerson = (newFeaturePhoto: string | null) => { const handleCreatePerson = (newFeaturePhoto: string | null) => {
const personToUpdate = peopleWithFaces.find((face) => face.person?.id === editedPerson.id); if (newFeaturePhoto) {
if (newFeaturePhoto && personToUpdate) { selectedPersonToCreate[editedFace.id] = newFeaturePhoto;
selectedPersonToCreate[personToUpdate.id] = newFeaturePhoto;
} }
showSelectedFaces = false; showSelectedFaces = false;
}; };
const handleReassignFace = (person: PersonResponseDto | null) => { const handleReassignFace = (person: PersonResponseDto | null) => {
const personToUpdate = peopleWithFaces.find((face) => face.person?.id === editedPerson.id); if (person) {
if (person && personToUpdate) { selectedPersonToReassign[editedFace.id] = person;
selectedPersonToReassign[personToUpdate.id] = person;
showSelectedFaces = false;
} }
showSelectedFaces = false;
}; };
const handlePersonPicker = (person: PersonResponseDto | null) => { const handleFacePicker = (face: AssetFaceResponseDto) => {
if (person) { if (face.person) {
editedPerson = person; editedFace = face;
editedPerson = face.person;
showSelectedFaces = true; showSelectedFaces = true;
} }
}; };
@ -285,7 +285,7 @@
size="18" size="18"
padding="1" padding="1"
class="absolute left-1/2 top-1/2 translate-x-[-50%] translate-y-[-50%] transform" class="absolute left-1/2 top-1/2 translate-x-[-50%] translate-y-[-50%] transform"
on:click={() => handlePersonPicker(face.person)} on:click={() => handleFacePicker(face)}
/> />
{/if} {/if}
</div> </div>