1
0
Fork 0
mirror of https://github.com/immich-app/immich.git synced 2025-01-10 13:56:47 +01:00
immich/server/src/domain/person/person.service.ts

122 lines
4.2 KiB
TypeScript
Raw Normal View History

import { PersonEntity } from '@app/infra/entities';
2023-05-17 19:07:17 +02:00
import { BadRequestException, Inject, Injectable, Logger, NotFoundException } from '@nestjs/common';
import { AssetResponseDto, mapAsset } from '../asset';
import { AuthUserDto } from '../auth';
import { mimeTypes } from '../domain.constant';
2023-05-17 19:07:17 +02:00
import { IJobRepository, JobName } from '../job';
import { ImmichReadStream, IStorageRepository } from '../storage';
2023-07-01 03:52:40 +02:00
import { mapPerson, PersonResponseDto, PersonUpdateDto } from './person.dto';
2023-05-17 19:07:17 +02:00
import { IPersonRepository } from './person.repository';
@Injectable()
export class PersonService {
readonly logger = new Logger(PersonService.name);
constructor(
@Inject(IPersonRepository) private repository: IPersonRepository,
@Inject(IStorageRepository) private storageRepository: IStorageRepository,
@Inject(IJobRepository) private jobRepository: IJobRepository,
) {}
async getAll(authUser: AuthUserDto): Promise<PersonResponseDto[]> {
const people = await this.repository.getAll(authUser.id, { minimumFaceCount: 1 });
const named = people.filter((person) => !!person.name);
const unnamed = people.filter((person) => !person.name);
return (
[...named, ...unnamed]
// with thumbnails
.filter((person) => !!person.thumbnailPath)
.map((person) => mapPerson(person))
);
}
async getById(authUser: AuthUserDto, personId: string): Promise<PersonResponseDto> {
const person = await this.repository.getById(authUser.id, personId);
if (!person) {
throw new BadRequestException();
}
return mapPerson(person);
}
async getThumbnail(authUser: AuthUserDto, personId: string): Promise<ImmichReadStream> {
const person = await this.repository.getById(authUser.id, personId);
if (!person || !person.thumbnailPath) {
throw new NotFoundException();
}
return this.storageRepository.createReadStream(person.thumbnailPath, mimeTypes.lookup(person.thumbnailPath));
2023-05-17 19:07:17 +02:00
}
async getAssets(authUser: AuthUserDto, personId: string): Promise<AssetResponseDto[]> {
const assets = await this.repository.getAssets(authUser.id, personId);
return assets.map(mapAsset);
}
async update(authUser: AuthUserDto, personId: string, dto: PersonUpdateDto): Promise<PersonResponseDto> {
let person = await this.repository.getById(authUser.id, personId);
if (!person) {
2023-05-17 19:07:17 +02:00
throw new BadRequestException();
}
if (dto.name) {
person = await this.updateName(authUser, personId, dto.name);
}
if (dto.featureFaceAssetId) {
await this.updateFaceThumbnail(personId, dto.featureFaceAssetId);
}
return mapPerson(person);
}
private async updateName(authUser: AuthUserDto, personId: string, name: string): Promise<PersonEntity> {
const person = await this.repository.update({ id: personId, name });
2023-05-17 19:07:17 +02:00
const relatedAsset = await this.getAssets(authUser, personId);
const assetIds = relatedAsset.map((asset) => asset.id);
await this.jobRepository.queue({ name: JobName.SEARCH_INDEX_ASSET, data: { ids: assetIds } });
return person;
}
private async updateFaceThumbnail(personId: string, assetId: string): Promise<void> {
const face = await this.repository.getFaceById({ assetId, personId });
if (!face) {
throw new BadRequestException();
}
return await this.jobRepository.queue({
name: JobName.GENERATE_FACE_THUMBNAIL,
data: {
assetId: assetId,
personId,
boundingBox: {
x1: face.boundingBoxX1,
x2: face.boundingBoxX2,
y1: face.boundingBoxY1,
y2: face.boundingBoxY2,
},
imageHeight: face.imageHeight,
imageWidth: face.imageWidth,
},
});
2023-05-17 19:07:17 +02:00
}
async handlePersonCleanup() {
2023-05-17 19:07:17 +02:00
const people = await this.repository.getAllWithoutFaces();
for (const person of people) {
this.logger.debug(`Person ${person.name || person.id} no longer has any faces, deleting.`);
try {
await this.repository.delete(person);
await this.jobRepository.queue({ name: JobName.DELETE_FILES, data: { files: [person.thumbnailPath] } });
} catch (error: Error | any) {
this.logger.error(`Unable to delete person: ${error}`, error?.stack);
}
}
return true;
2023-05-17 19:07:17 +02:00
}
}