2019-01-14 00:38:39 +08:00
|
|
|
import {SQLConnection} from './SQLConnection';
|
2019-02-05 06:46:27 +08:00
|
|
|
import {PersonEntry} from './enitites/PersonEntry';
|
2019-12-15 00:27:01 +08:00
|
|
|
import {PhotoDTO} from '../../../../common/entities/PhotoDTO';
|
2019-02-15 07:25:55 +08:00
|
|
|
import {MediaEntity} from './enitites/MediaEntity';
|
|
|
|
import {FaceRegionEntry} from './enitites/FaceRegionEntry';
|
2019-12-15 00:27:01 +08:00
|
|
|
import {PersonDTO} from '../../../../common/entities/PersonDTO';
|
|
|
|
import {Utils} from '../../../../common/Utils';
|
2020-01-01 22:57:16 +08:00
|
|
|
import {SelectQueryBuilder} from 'typeorm';
|
2020-12-31 04:54:07 +08:00
|
|
|
import {ISQLPersonManager} from './IPersonManager';
|
2019-01-14 00:38:39 +08:00
|
|
|
|
|
|
|
|
2020-12-31 04:54:07 +08:00
|
|
|
export class PersonManager implements ISQLPersonManager {
|
2019-03-11 03:57:27 +08:00
|
|
|
samplePhotos: { [key: string]: PhotoDTO } = {};
|
2019-01-14 00:38:39 +08:00
|
|
|
persons: PersonEntry[] = [];
|
|
|
|
|
2019-03-04 04:17:42 +08:00
|
|
|
async updatePerson(name: string, partialPerson: PersonDTO): Promise<PersonEntry> {
|
|
|
|
const connection = await SQLConnection.getConnection();
|
|
|
|
const repository = connection.getRepository(PersonEntry);
|
|
|
|
const person = await repository.createQueryBuilder('person')
|
|
|
|
.limit(1)
|
|
|
|
.where('person.name LIKE :name COLLATE utf8_general_ci', {name: name}).getOne();
|
|
|
|
|
|
|
|
|
|
|
|
if (typeof partialPerson.name !== 'undefined') {
|
|
|
|
person.name = partialPerson.name;
|
|
|
|
}
|
|
|
|
if (typeof partialPerson.isFavourite !== 'undefined') {
|
|
|
|
person.isFavourite = partialPerson.isFavourite;
|
|
|
|
}
|
|
|
|
await repository.save(person);
|
|
|
|
|
|
|
|
await this.loadAll();
|
|
|
|
|
|
|
|
return person;
|
|
|
|
}
|
|
|
|
|
2019-02-15 07:25:55 +08:00
|
|
|
async getSamplePhoto(name: string): Promise<PhotoDTO> {
|
2020-01-01 22:57:16 +08:00
|
|
|
return (await this.getSamplePhotos([name]))[name];
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async getSamplePhotos(names: string[]): Promise<{ [key: string]: PhotoDTO }> {
|
|
|
|
const hasAll = names.reduce((prev, name) => prev && !!this.samplePhotos[name], true);
|
|
|
|
if (!hasAll) {
|
2019-03-11 03:57:27 +08:00
|
|
|
const connection = await SQLConnection.getConnection();
|
2020-01-02 03:26:59 +08:00
|
|
|
const namesObj: any = {};
|
|
|
|
let queryStr = '';
|
|
|
|
names.forEach((n, i) => {
|
|
|
|
if (i > 0) {
|
|
|
|
queryStr += ', ';
|
|
|
|
}
|
|
|
|
queryStr += ':n' + i + ' COLLATE utf8_general_ci';
|
|
|
|
namesObj['n' + i] = n;
|
|
|
|
});
|
2020-01-02 06:24:22 +08:00
|
|
|
const query: SelectQueryBuilder<MediaEntity> = await (connection
|
2020-01-01 22:57:16 +08:00
|
|
|
.getRepository(MediaEntity)
|
|
|
|
.createQueryBuilder('media') as SelectQueryBuilder<MediaEntity>)
|
|
|
|
.select(['media.name', 'media.id', 'person.name', 'directory.name',
|
|
|
|
'directory.path', 'media.metadata.size.width', 'media.metadata.size.height'])
|
|
|
|
.leftJoin('media.directory', 'directory')
|
2019-03-11 03:57:27 +08:00
|
|
|
.leftJoinAndSelect('media.metadata.faces', 'faces')
|
|
|
|
.leftJoin('faces.person', 'person')
|
2020-01-02 06:24:22 +08:00
|
|
|
.groupBy('person.name, media.name, media.id, directory.name, faces.id');
|
|
|
|
// TODO: improve it. SQLITE does not support case-insensitive special characters like ÁÉÚŐ
|
|
|
|
for (let i = 0; i < names.length; ++i) {
|
|
|
|
const opt: any = {};
|
|
|
|
opt['n' + i] = names[i];
|
|
|
|
query.orWhere(`person.name LIKE :n${i} COLLATE utf8_general_ci`, opt);
|
|
|
|
}
|
2019-02-15 07:25:55 +08:00
|
|
|
|
2020-01-02 06:24:22 +08:00
|
|
|
const rawAndEntities = await query.getRawAndEntities();
|
2020-01-01 22:57:16 +08:00
|
|
|
for (let i = 0; i < rawAndEntities.raw.length; ++i) {
|
2020-01-02 03:26:59 +08:00
|
|
|
this.samplePhotos[rawAndEntities.raw[i].person_name.toLowerCase()] =
|
2020-01-01 22:57:16 +08:00
|
|
|
Utils.clone(rawAndEntities.entities.find(m => m.name === rawAndEntities.raw[i].media_name));
|
2020-01-03 05:11:59 +08:00
|
|
|
this.samplePhotos[rawAndEntities.raw[i].person_name.toLowerCase()].metadata.faces =
|
|
|
|
[FaceRegionEntry.fromRawToDTO(rawAndEntities.raw[i])];
|
2020-01-01 22:57:16 +08:00
|
|
|
}
|
2019-03-11 03:57:27 +08:00
|
|
|
}
|
2019-02-15 07:25:55 +08:00
|
|
|
|
2020-01-01 22:57:16 +08:00
|
|
|
const photoMap: { [key: string]: PhotoDTO } = {};
|
2020-01-02 03:26:59 +08:00
|
|
|
names.forEach(n => photoMap[n] = this.samplePhotos[n.toLowerCase()]);
|
2020-01-01 22:57:16 +08:00
|
|
|
return photoMap;
|
2019-02-15 07:25:55 +08:00
|
|
|
}
|
|
|
|
|
2020-01-01 22:57:16 +08:00
|
|
|
|
2019-02-05 06:46:27 +08:00
|
|
|
async loadAll(): Promise<void> {
|
|
|
|
const connection = await SQLConnection.getConnection();
|
|
|
|
const personRepository = connection.getRepository(PersonEntry);
|
|
|
|
this.persons = await personRepository.find();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2019-02-15 07:25:55 +08:00
|
|
|
async getAll(): Promise<PersonEntry[]> {
|
|
|
|
await this.loadAll();
|
|
|
|
return this.persons;
|
|
|
|
}
|
|
|
|
|
2019-02-05 06:46:27 +08:00
|
|
|
|
2020-12-31 04:54:07 +08:00
|
|
|
async countFaces(): Promise<number> {
|
|
|
|
const connection = await SQLConnection.getConnection();
|
|
|
|
return await connection.getRepository(FaceRegionEntry)
|
|
|
|
.createQueryBuilder('faceRegion')
|
|
|
|
.getCount();
|
|
|
|
}
|
|
|
|
|
2019-01-14 00:38:39 +08:00
|
|
|
async get(name: string): Promise<PersonEntry> {
|
|
|
|
|
|
|
|
let person = this.persons.find(p => p.name === name);
|
|
|
|
if (!person) {
|
|
|
|
const connection = await SQLConnection.getConnection();
|
|
|
|
const personRepository = connection.getRepository(PersonEntry);
|
|
|
|
person = await personRepository.findOne({name: name});
|
|
|
|
if (!person) {
|
|
|
|
person = await personRepository.save(<PersonEntry>{name: name});
|
|
|
|
}
|
|
|
|
this.persons.push(person);
|
|
|
|
}
|
|
|
|
return person;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async saveAll(names: string[]): Promise<void> {
|
|
|
|
const toSave: { name: string }[] = [];
|
|
|
|
const connection = await SQLConnection.getConnection();
|
|
|
|
const personRepository = connection.getRepository(PersonEntry);
|
2019-02-05 06:46:27 +08:00
|
|
|
await this.loadAll();
|
2019-01-14 00:38:39 +08:00
|
|
|
|
|
|
|
for (let i = 0; i < names.length; i++) {
|
|
|
|
|
|
|
|
const person = this.persons.find(p => p.name === names[i]);
|
|
|
|
if (!person) {
|
|
|
|
toSave.push({name: names[i]});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (toSave.length > 0) {
|
|
|
|
for (let i = 0; i < toSave.length / 200; i++) {
|
|
|
|
await personRepository.insert(toSave.slice(i * 200, (i + 1) * 200));
|
|
|
|
}
|
2019-02-05 06:46:27 +08:00
|
|
|
this.persons = await personRepository.find();
|
2019-01-14 00:38:39 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2019-03-11 03:57:27 +08:00
|
|
|
|
|
|
|
public async onGalleryIndexUpdate() {
|
|
|
|
await this.updateCounts();
|
|
|
|
this.samplePhotos = {};
|
|
|
|
}
|
|
|
|
|
2019-02-15 07:25:55 +08:00
|
|
|
public async updateCounts() {
|
|
|
|
const connection = await SQLConnection.getConnection();
|
|
|
|
await connection.query('update person_entry set count = ' +
|
|
|
|
' (select COUNT(1) from face_region_entry where face_region_entry.personId = person_entry.id)');
|
2019-02-16 02:26:01 +08:00
|
|
|
|
|
|
|
// remove persons without photo
|
|
|
|
await connection.getRepository(PersonEntry)
|
|
|
|
.createQueryBuilder()
|
|
|
|
.where('count = 0')
|
|
|
|
.delete()
|
|
|
|
.execute();
|
2019-02-15 07:25:55 +08:00
|
|
|
}
|
|
|
|
|
2019-01-14 00:38:39 +08:00
|
|
|
}
|