diff --git a/backend/middlewares/thumbnail/ThumbnailGeneratorMWs.ts b/backend/middlewares/thumbnail/ThumbnailGeneratorMWs.ts index e0a55a6..56897b8 100644 --- a/backend/middlewares/thumbnail/ThumbnailGeneratorMWs.ts +++ b/backend/middlewares/thumbnail/ThumbnailGeneratorMWs.ts @@ -85,7 +85,6 @@ export class ThumbnailGeneratorMWs { const persons: PersonWithPhoto[] = req.resultPipe; for (let i = 0; i < persons.length; i++) { - // load parameters const mediaPath = path.join(ProjectPath.ImageFolder, persons[i].samplePhoto.directory.path, @@ -151,8 +150,8 @@ export class ThumbnailGeneratorMWs { thPath: thPath, makeSquare: false, cut: { - left: Math.round(Math.max(0, photo.metadata.faces[0].box.x - margin.x / 2)), - top: Math.round(Math.max(0, photo.metadata.faces[0].box.y - margin.y / 2)), + left: Math.round(Math.max(0, photo.metadata.faces[0].box.left - margin.x / 2)), + top: Math.round(Math.max(0, photo.metadata.faces[0].box.top - margin.y / 2)), width: photo.metadata.faces[0].box.width + margin.x, height: photo.metadata.faces[0].box.height + margin.y }, @@ -208,7 +207,7 @@ export class ThumbnailGeneratorMWs { } public static generatePersonThumbnailName(mediaPath: string, faceRegion: FaceRegion, size: number): string { - return crypto.createHash('md5').update(mediaPath + '_' + faceRegion.name + '_' + faceRegion.box.x + '_' + faceRegion.box.y) + return crypto.createHash('md5').update(mediaPath + '_' + faceRegion.name + '_' + faceRegion.box.left + '_' + faceRegion.box.top) .digest('hex') + '_' + size + '.jpg'; } diff --git a/backend/model/interfaces/IPersonManager.ts b/backend/model/interfaces/IPersonManager.ts index 12458dc..1afcd91 100644 --- a/backend/model/interfaces/IPersonManager.ts +++ b/backend/model/interfaces/IPersonManager.ts @@ -14,7 +14,7 @@ export interface IPersonManager { keywordsToPerson(media: MediaDTO[]): Promise; - updateCounts(): Promise; + onGalleryIndexUpdate(): Promise; updatePerson(name: string, partialPerson: PersonDTO): Promise; } diff --git a/backend/model/memory/PersonManager.ts b/backend/model/memory/PersonManager.ts index a5d618d..2f93db6 100644 --- a/backend/model/memory/PersonManager.ts +++ b/backend/model/memory/PersonManager.ts @@ -25,7 +25,7 @@ export class PersonManager implements IPersonManager { throw new Error('not supported by memory DB'); } - updateCounts(): Promise { + onGalleryIndexUpdate(): Promise { throw new Error('not supported by memory DB'); } diff --git a/backend/model/sql/GalleryManager.ts b/backend/model/sql/GalleryManager.ts index 3e36042..cfc69a5 100644 --- a/backend/model/sql/GalleryManager.ts +++ b/backend/model/sql/GalleryManager.ts @@ -274,8 +274,8 @@ export class GalleryManager implements IGalleryManager, ISQLGalleryManager { directory: dir.id }) .leftJoinAndSelect('face.person', 'person') - .select(['face.id', 'face.box.x', - 'face.box.y', 'face.box.width', 'face.box.height', + .select(['face.id', 'face.box.left', + 'face.box.top', 'face.box.width', 'face.box.height', 'media.id', 'person.name', 'person.id']) .getMany(); for (let i = 0; i < dir.media.length; i++) { diff --git a/backend/model/sql/IndexingManager.ts b/backend/model/sql/IndexingManager.ts index faf92b5..164aabe 100644 --- a/backend/model/sql/IndexingManager.ts +++ b/backend/model/sql/IndexingManager.ts @@ -260,8 +260,8 @@ export class IndexingManager implements IIndexingManager { for (let j = 0; j < indexedFaces.length; j++) { if (indexedFaces[j].box.height === scannedFaces[i].box.height && indexedFaces[j].box.width === scannedFaces[i].box.width && - indexedFaces[j].box.x === scannedFaces[i].box.x && - indexedFaces[j].box.y === scannedFaces[i].box.y && + indexedFaces[j].box.left === scannedFaces[i].box.left && + indexedFaces[j].box.top === scannedFaces[i].box.top && indexedFaces[j].person.name === scannedFaces[i].name) { face = indexedFaces[j]; indexedFaces.splice(j, 1); @@ -289,7 +289,7 @@ export class IndexingManager implements IIndexingManager { await this.saveChildDirs(connection, currentDirId, scannedDirectory); await this.saveMedia(connection, currentDirId, scannedDirectory.media); await this.saveMetaFiles(connection, currentDirId, scannedDirectory); - await ObjectManagers.getInstance().PersonManager.updateCounts(); + await ObjectManagers.getInstance().PersonManager.onGalleryIndexUpdate(); await ObjectManagers.getInstance().VersionManager.updateDataVersion(); } catch (e) { throw e; diff --git a/backend/model/sql/PersonManager.ts b/backend/model/sql/PersonManager.ts index 2dff926..d515445 100644 --- a/backend/model/sql/PersonManager.ts +++ b/backend/model/sql/PersonManager.ts @@ -6,10 +6,12 @@ import {PhotoDTO} from '../../../common/entities/PhotoDTO'; import {MediaEntity} from './enitites/MediaEntity'; import {FaceRegionEntry} from './enitites/FaceRegionEntry'; import {PersonDTO} from '../../../common/entities/PersonDTO'; +import {Utils} from '../../../common/Utils'; const LOG_TAG = '[PersonManager]'; export class PersonManager implements IPersonManager { + samplePhotos: { [key: string]: PhotoDTO } = {}; persons: PersonEntry[] = []; async updatePerson(name: string, partialPerson: PersonDTO): Promise { @@ -34,22 +36,25 @@ export class PersonManager implements IPersonManager { } async getSamplePhoto(name: string): Promise { - const connection = await SQLConnection.getConnection(); - const rawAndEntities = await connection.getRepository(MediaEntity).createQueryBuilder('media') - .limit(1) - .leftJoinAndSelect('media.directory', 'directory') - .leftJoinAndSelect('media.metadata.faces', 'faces') - .leftJoinAndSelect('faces.person', 'person') - .where('person.name LIKE :name COLLATE utf8_general_ci', {name: name}).getRawAndEntities(); + if (!this.samplePhotos[name]) { + const connection = await SQLConnection.getConnection(); + const rawAndEntities = await connection.getRepository(MediaEntity).createQueryBuilder('media') + .limit(1) + .leftJoinAndSelect('media.directory', 'directory') + .leftJoinAndSelect('media.metadata.faces', 'faces') + .leftJoin('faces.person', 'person') + .where('person.name LIKE :name COLLATE utf8_general_ci', {name: name}).getRawAndEntities(); - if (rawAndEntities.entities.length === 0) { - return null; + if (rawAndEntities.entities.length === 0) { + return null; + } + const media: PhotoDTO = Utils.clone(rawAndEntities.entities[0]); + + media.metadata.faces = [FaceRegionEntry.fromRawToDTO(rawAndEntities.raw[0])]; + + this.samplePhotos[name] = media; } - const media: PhotoDTO = rawAndEntities.entities[0]; - - media.metadata.faces = [FaceRegionEntry.fromRawToDTO(rawAndEntities.raw[0])]; - - return media; + return this.samplePhotos[name]; } @@ -129,6 +134,12 @@ export class PersonManager implements IPersonManager { } + + public async onGalleryIndexUpdate() { + await this.updateCounts(); + this.samplePhotos = {}; + } + public async updateCounts() { const connection = await SQLConnection.getConnection(); await connection.query('update person_entry set count = ' + diff --git a/backend/model/sql/enitites/FaceRegionEntry.ts b/backend/model/sql/enitites/FaceRegionEntry.ts index 411be7f..aaa4473 100644 --- a/backend/model/sql/enitites/FaceRegionEntry.ts +++ b/backend/model/sql/enitites/FaceRegionEntry.ts @@ -9,9 +9,9 @@ export class FaceRegionBoxEntry implements FaceRegionBox { @Column('int') width: number; @Column('int') - x: number; + left: number; @Column('int') - y: number; + top: number; } /** @@ -42,13 +42,13 @@ export class FaceRegionEntry { faces_personId: number, faces_boxHeight: number, faces_boxWidth: number, - faces_boxX: number, - faces_boxY: number, + faces_boxLeft: number, + faces_boxTop: number, person_id: number, person_name: string }): FaceRegion { return { - box: {width: raw.faces_boxWidth, height: raw.faces_boxHeight, x: raw.faces_boxX, y: raw.faces_boxY}, + box: {width: raw.faces_boxWidth, height: raw.faces_boxHeight, left: raw.faces_boxLeft, top: raw.faces_boxTop}, name: raw.person_name }; } diff --git a/backend/model/threading/MetadataLoader.ts b/backend/model/threading/MetadataLoader.ts index b8decde..ebb8b04 100644 --- a/backend/model/threading/MetadataLoader.ts +++ b/backend/model/threading/MetadataLoader.ts @@ -211,12 +211,12 @@ export class MetadataLoader { const box = { width: Math.round(regionBox['stArea:w'] * metadata.size.width), height: Math.round(regionBox['stArea:h'] * metadata.size.height), - x: Math.round(regionBox['stArea:x'] * metadata.size.width), - y: Math.round(regionBox['stArea:y'] * metadata.size.height) + left: Math.round(regionBox['stArea:x'] * metadata.size.width), + top: Math.round(regionBox['stArea:y'] * metadata.size.height) }; // convert center base box to corner based box - box.x = Math.max(0, box.x - box.width / 2); - box.y = Math.max(0, box.y - box.height / 2); + box.left = Math.max(0, box.left - box.width / 2); + box.top = Math.max(0, box.top - box.height / 2); faces.push({name: name, box: box}); } } diff --git a/backend/routes/PersonRouter.ts b/backend/routes/PersonRouter.ts index d95b451..456ac89 100644 --- a/backend/routes/PersonRouter.ts +++ b/backend/routes/PersonRouter.ts @@ -11,7 +11,7 @@ export class PersonRouter { public static route(app: Express) { this.updatePerson(app); - this.addPersons(app); + this.addGetPersons(app); this.getPersonThumbnail(app); } @@ -29,7 +29,7 @@ export class PersonRouter { ); } - private static addPersons(app: Express) { + private static addGetPersons(app: Express) { app.get(['/api/person'], // common part AuthenticationMWs.authenticate, diff --git a/common/entities/PhotoDTO.ts b/common/entities/PhotoDTO.ts index a6d5e98..130d8fb 100644 --- a/common/entities/PhotoDTO.ts +++ b/common/entities/PhotoDTO.ts @@ -14,8 +14,8 @@ export interface PhotoDTO extends MediaDTO { export interface FaceRegionBox { width: number; height: number; - x: number; - y: number; + left: number; + top: number; } export interface FaceRegion { diff --git a/frontend/app/ui/faces/face/face.component.ts b/frontend/app/ui/faces/face/face.component.ts index e6bfcca..7e64275 100644 --- a/frontend/app/ui/faces/face/face.component.ts +++ b/frontend/app/ui/faces/face/face.component.ts @@ -55,7 +55,6 @@ export class FaceComponent implements OnInit, OnDestroy { $event.preventDefault(); $event.stopPropagation(); await this.faceService.setFavourite(this.person, !this.person.isFavourite).catch(console.error); - this.faceService.getPersons(); } } diff --git a/frontend/app/ui/gallery/grid/GridMedia.ts b/frontend/app/ui/gallery/grid/GridMedia.ts index 03d82e2..f258949 100644 --- a/frontend/app/ui/gallery/grid/GridMedia.ts +++ b/frontend/app/ui/gallery/grid/GridMedia.ts @@ -26,6 +26,9 @@ export class GridMedia extends Media { get Video(): VideoDTO { return this.media; } + get Photo(): PhotoDTO { + return this.media; + } } diff --git a/test/backend/unit/model/sql/TestHelper.ts b/test/backend/unit/model/sql/TestHelper.ts index 05c985e..8a3bdd5 100644 --- a/test/backend/unit/model/sql/TestHelper.ts +++ b/test/backend/unit/model/sql/TestHelper.ts @@ -194,8 +194,8 @@ export class TestHelper { const f: FaceRegion = { name: rndStr() + '.jpg', box: { - x: rndInt(), - y: rndInt(), + left: rndInt(), + top: rndInt(), width: rndInt(), height: rndInt() }