mirror of
https://github.com/bpatrik/pigallery2.git
synced 2025-02-03 13:22:05 +02:00
fixing sharp thumbnail creation
This commit is contained in:
parent
7f974a1c41
commit
8bf280dda7
@ -1,7 +1,10 @@
|
|||||||
import {PersonEntry} from '../sql/enitites/PersonEntry';
|
import {PersonEntry} from '../sql/enitites/PersonEntry';
|
||||||
|
import {MediaDTO} from '../../../common/entities/MediaDTO';
|
||||||
|
|
||||||
export interface IPersonManager {
|
export interface IPersonManager {
|
||||||
get(name: string): Promise<PersonEntry>;
|
get(name: string): Promise<PersonEntry>;
|
||||||
|
|
||||||
saveAll(names: string[]): Promise<void>;
|
saveAll(names: string[]): Promise<void>;
|
||||||
|
|
||||||
|
keywordsToPerson(media: MediaDTO[]): Promise<void>;
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ import {DiskManager} from '../DiskManger';
|
|||||||
import {PhotoEntity} from './enitites/PhotoEntity';
|
import {PhotoEntity} from './enitites/PhotoEntity';
|
||||||
import {Utils} from '../../../common/Utils';
|
import {Utils} from '../../../common/Utils';
|
||||||
import {FaceRegion, PhotoMetadata} from '../../../common/entities/PhotoDTO';
|
import {FaceRegion, PhotoMetadata} from '../../../common/entities/PhotoDTO';
|
||||||
import {Connection, Repository} from 'typeorm';
|
import {Connection, Repository} from 'typeorm';
|
||||||
import {MediaEntity} from './enitites/MediaEntity';
|
import {MediaEntity} from './enitites/MediaEntity';
|
||||||
import {MediaDTO} from '../../../common/entities/MediaDTO';
|
import {MediaDTO} from '../../../common/entities/MediaDTO';
|
||||||
import {VideoEntity} from './enitites/VideoEntity';
|
import {VideoEntity} from './enitites/VideoEntity';
|
||||||
@ -14,10 +14,11 @@ import {FileDTO} from '../../../common/entities/FileDTO';
|
|||||||
import {NotificationManager} from '../NotifocationManager';
|
import {NotificationManager} from '../NotifocationManager';
|
||||||
import {FaceRegionEntry} from './enitites/FaceRegionEntry';
|
import {FaceRegionEntry} from './enitites/FaceRegionEntry';
|
||||||
import {ObjectManagerRepository} from '../ObjectManagerRepository';
|
import {ObjectManagerRepository} from '../ObjectManagerRepository';
|
||||||
|
import {IIndexingManager} from '../interfaces/IIndexingManager';
|
||||||
|
|
||||||
const LOG_TAG = '[IndexingManager]';
|
const LOG_TAG = '[IndexingManager]';
|
||||||
|
|
||||||
export class IndexingManager {
|
export class IndexingManager implements IIndexingManager {
|
||||||
|
|
||||||
private savingQueue: DirectoryDTO[] = [];
|
private savingQueue: DirectoryDTO[] = [];
|
||||||
private isSaving = false;
|
private isSaving = false;
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import {IPersonManager} from '../interfaces/IPersonManager';
|
import {IPersonManager} from '../interfaces/IPersonManager';
|
||||||
import {PersonEntry} from './enitites/PersonEntry';
|
|
||||||
import {SQLConnection} from './SQLConnection';
|
import {SQLConnection} from './SQLConnection';
|
||||||
|
import {PersonEntry} from './enitites/PersonEntry';
|
||||||
|
import {MediaDTO} from '../../../common/entities/MediaDTO';
|
||||||
|
import {PhotoDTO} from '../../../common/entities/PhotoDTO';
|
||||||
|
|
||||||
const LOG_TAG = '[PersonManager]';
|
const LOG_TAG = '[PersonManager]';
|
||||||
|
|
||||||
@ -8,6 +10,38 @@ export class PersonManager implements IPersonManager {
|
|||||||
|
|
||||||
persons: PersonEntry[] = [];
|
persons: PersonEntry[] = [];
|
||||||
|
|
||||||
|
async loadAll(): Promise<void> {
|
||||||
|
const connection = await SQLConnection.getConnection();
|
||||||
|
const personRepository = connection.getRepository(PersonEntry);
|
||||||
|
this.persons = await personRepository.find();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO dead code, remove it
|
||||||
|
async keywordsToPerson(media: MediaDTO[]) {
|
||||||
|
await this.loadAll();
|
||||||
|
const personFilter = (keyword: string) => this.persons.find(p => p.name.toLowerCase() === keyword.toLowerCase());
|
||||||
|
(<PhotoDTO[]>media).forEach(m => {
|
||||||
|
if (!m.metadata.keywords || m.metadata.keywords.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const personKeywords = m.metadata.keywords.filter(k => personFilter(k));
|
||||||
|
if (personKeywords.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// remove persons
|
||||||
|
m.metadata.keywords = m.metadata.keywords.filter(k => !personFilter(k));
|
||||||
|
m.metadata.faces = m.metadata.faces || [];
|
||||||
|
personKeywords.forEach((pk: string) => {
|
||||||
|
m.metadata.faces.push({
|
||||||
|
name: pk
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async get(name: string): Promise<PersonEntry> {
|
async get(name: string): Promise<PersonEntry> {
|
||||||
|
|
||||||
let person = this.persons.find(p => p.name === name);
|
let person = this.persons.find(p => p.name === name);
|
||||||
@ -28,7 +62,7 @@ export class PersonManager implements IPersonManager {
|
|||||||
const toSave: { name: string }[] = [];
|
const toSave: { name: string }[] = [];
|
||||||
const connection = await SQLConnection.getConnection();
|
const connection = await SQLConnection.getConnection();
|
||||||
const personRepository = connection.getRepository(PersonEntry);
|
const personRepository = connection.getRepository(PersonEntry);
|
||||||
this.persons = await personRepository.find();
|
await this.loadAll();
|
||||||
|
|
||||||
for (let i = 0; i < names.length; i++) {
|
for (let i = 0; i < names.length; i++) {
|
||||||
|
|
||||||
@ -42,7 +76,7 @@ export class PersonManager implements IPersonManager {
|
|||||||
for (let i = 0; i < toSave.length / 200; i++) {
|
for (let i = 0; i < toSave.length / 200; i++) {
|
||||||
await personRepository.insert(toSave.slice(i * 200, (i + 1) * 200));
|
await personRepository.insert(toSave.slice(i * 200, (i + 1) * 200));
|
||||||
}
|
}
|
||||||
this.persons = await personRepository.find();
|
this.persons = await personRepository.find();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -187,48 +187,48 @@ export class MetadataLoader {
|
|||||||
|
|
||||||
metadata.creationDate = metadata.creationDate || 0;
|
metadata.creationDate = metadata.creationDate || 0;
|
||||||
|
|
||||||
|
if (Config.Client.Faces.enabled) {
|
||||||
|
try {
|
||||||
|
|
||||||
try {
|
const ret = ExifReader.load(data);
|
||||||
|
const faces: FaceRegion[] = [];
|
||||||
const ret = ExifReader.load(data);
|
if (ret.Regions && ret.Regions.value.RegionList && ret.Regions.value.RegionList.value) {
|
||||||
const faces: FaceRegion[] = [];
|
for (let i = 0; i < ret.Regions.value.RegionList.value.length; i++) {
|
||||||
if (ret.Regions && ret.Regions.value.RegionList && ret.Regions.value.RegionList.value) {
|
if (!ret.Regions.value.RegionList.value[i].value ||
|
||||||
for (let i = 0; i < ret.Regions.value.RegionList.value.length; i++) {
|
!ret.Regions.value.RegionList.value[i].value['rdf:Description'] ||
|
||||||
if (!ret.Regions.value.RegionList.value[i].value ||
|
!ret.Regions.value.RegionList.value[i].value['rdf:Description'].value ||
|
||||||
!ret.Regions.value.RegionList.value[i].value['rdf:Description'] ||
|
!ret.Regions.value.RegionList.value[i].value['rdf:Description'].value['mwg-rs:Area']) {
|
||||||
!ret.Regions.value.RegionList.value[i].value['rdf:Description'].value ||
|
continue;
|
||||||
!ret.Regions.value.RegionList.value[i].value['rdf:Description'].value['mwg-rs:Area']) {
|
}
|
||||||
continue;
|
const region = ret.Regions.value.RegionList.value[i].value['rdf:Description'];
|
||||||
|
const regionBox = ret.Regions.value.RegionList.value[i].value['rdf:Description'].value['mwg-rs:Area'].attributes;
|
||||||
|
if (region.attributes['mwg-rs:Type'] !== 'Face' ||
|
||||||
|
!region.attributes['mwg-rs:Name']) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const name = region.attributes['mwg-rs:Name'];
|
||||||
|
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)
|
||||||
|
};
|
||||||
|
faces.push({name: name, box: box});
|
||||||
}
|
}
|
||||||
const region = ret.Regions.value.RegionList.value[i].value['rdf:Description'];
|
|
||||||
const regionBox = ret.Regions.value.RegionList.value[i].value['rdf:Description'].value['mwg-rs:Area'].attributes;
|
|
||||||
if (region.attributes['mwg-rs:Type'] !== 'Face' ||
|
|
||||||
!region.attributes['mwg-rs:Name']) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const name = region.attributes['mwg-rs:Name'];
|
|
||||||
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)
|
|
||||||
};
|
|
||||||
faces.push({name: name, box: box});
|
|
||||||
}
|
}
|
||||||
|
if (Config.Client.Faces.keywordsToPersons && faces.length > 0) {
|
||||||
|
metadata.faces = faces; // save faces
|
||||||
|
// remove faces from keywords
|
||||||
|
metadata.faces.forEach(f => {
|
||||||
|
const index = metadata.keywords.indexOf(f.name);
|
||||||
|
if (index !== -1) {
|
||||||
|
metadata.keywords.splice(index, 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
}
|
}
|
||||||
if (faces.length > 0) {
|
|
||||||
metadata.faces = faces; // save faces
|
|
||||||
// remove faces from keywords
|
|
||||||
metadata.faces.forEach(f => {
|
|
||||||
const index = metadata.keywords.indexOf(f.name);
|
|
||||||
if (index !== -1) {
|
|
||||||
metadata.keywords.splice(index, 1);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return resolve(metadata);
|
return resolve(metadata);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return reject({file: fullPath, error: err});
|
return reject({file: fullPath, error: err});
|
||||||
|
@ -169,7 +169,7 @@ export class ImageRendererFactory {
|
|||||||
return async (input: RendererInput): Promise<void> => {
|
return async (input: RendererInput): Promise<void> => {
|
||||||
|
|
||||||
Logger.silly('[SharpThRenderer] rendering thumbnail:' + input.mediaPath);
|
Logger.silly('[SharpThRenderer] rendering thumbnail:' + input.mediaPath);
|
||||||
const image: Sharp = sharp(input.mediaPath);
|
const image: Sharp = sharp(input.mediaPath, {failOnError: false});
|
||||||
const metadata: Metadata = await image.metadata();
|
const metadata: Metadata = await image.metadata();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -65,6 +65,10 @@ export module ClientConfig {
|
|||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface FacesConfig {
|
||||||
|
enabled: boolean;
|
||||||
|
keywordsToPersons: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export interface Config {
|
export interface Config {
|
||||||
applicationTitle: string;
|
applicationTitle: string;
|
||||||
@ -81,6 +85,7 @@ export module ClientConfig {
|
|||||||
languages: string[];
|
languages: string[];
|
||||||
Video: VideoConfig;
|
Video: VideoConfig;
|
||||||
MetaFile: MetaFileConfig;
|
MetaFile: MetaFileConfig;
|
||||||
|
Faces: FacesConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -138,6 +143,10 @@ export class PublicConfigClass {
|
|||||||
showItemCount: true
|
showItemCount: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Faces: {
|
||||||
|
enabled: true,
|
||||||
|
keywordsToPersons: true
|
||||||
|
},
|
||||||
authenticationRequired: true,
|
authenticationRequired: true,
|
||||||
unAuthenticatedUserRole: UserRoles.Admin,
|
unAuthenticatedUserRole: UserRoles.Admin,
|
||||||
publicUrl: '',
|
publicUrl: '',
|
||||||
|
@ -20,7 +20,7 @@ export interface FaceRegionBox {
|
|||||||
|
|
||||||
export interface FaceRegion {
|
export interface FaceRegion {
|
||||||
name: string;
|
name: string;
|
||||||
box: FaceRegionBox;
|
box?: FaceRegionBox; // some faces don t have region ass they are coming from keywords
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PhotoMetadata extends MediaMetadata {
|
export interface PhotoMetadata extends MediaMetadata {
|
||||||
|
@ -35,6 +35,15 @@ export class FixOrientationPipe implements PipeTransform {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// transform context before drawing image
|
// transform context before drawing image
|
||||||
|
|
||||||
|
// transform function parameters:
|
||||||
|
// a Horizontal scaling
|
||||||
|
// b Horizontal skewing
|
||||||
|
// c Vertical skewing
|
||||||
|
// d Vertical scaling
|
||||||
|
// e Horizontal moving
|
||||||
|
// f Vertical moving
|
||||||
|
|
||||||
switch (orientation) {
|
switch (orientation) {
|
||||||
case OrientationTypes.TOP_RIGHT: // 2
|
case OrientationTypes.TOP_RIGHT: // 2
|
||||||
ctx.transform(-1, 0, 0, 1, width, 0);
|
ctx.transform(-1, 0, 0, 1, width, 0);
|
||||||
|
@ -59,6 +59,10 @@ export class SettingsService {
|
|||||||
showItemCount: true
|
showItemCount: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Faces: {
|
||||||
|
enabled: true,
|
||||||
|
keywordsToPersons: true
|
||||||
|
},
|
||||||
urlBase: '',
|
urlBase: '',
|
||||||
publicUrl: '',
|
publicUrl: '',
|
||||||
applicationTitle: '',
|
applicationTitle: '',
|
||||||
|
48
test/backend/unit/model/sql/PersonManager.ts
Normal file
48
test/backend/unit/model/sql/PersonManager.ts
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import {expect} from 'chai';
|
||||||
|
import {PersonManager} from '../../../../../backend/model/sql/PersonManager';
|
||||||
|
import {FaceRegion, PhotoDTO} from '../../../../../common/entities/PhotoDTO';
|
||||||
|
|
||||||
|
|
||||||
|
// to help WebStorm to handle the test cases
|
||||||
|
declare let describe: any;
|
||||||
|
declare const after: any;
|
||||||
|
declare const it: any;
|
||||||
|
|
||||||
|
|
||||||
|
describe('PersonManager', () => {
|
||||||
|
|
||||||
|
it('should upgrade keywords to person', async () => {
|
||||||
|
const pm = new PersonManager();
|
||||||
|
pm.loadAll = () => Promise.resolve();
|
||||||
|
pm.persons = [{name: 'Han Solo', id: 0, faces: []},
|
||||||
|
{name: 'Anakin', id: 2, faces: []}];
|
||||||
|
|
||||||
|
const p_noFaces = <PhotoDTO>{
|
||||||
|
metadata: {
|
||||||
|
keywords: ['Han Solo', 'just a keyword']
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const p_wFace = <PhotoDTO>{
|
||||||
|
metadata: {
|
||||||
|
keywords: ['Han Solo', 'Anakin'],
|
||||||
|
faces: [{name: 'Obivan'}]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const cmp = (a: FaceRegion, b: FaceRegion) => {
|
||||||
|
return a.name.localeCompare(b.name);
|
||||||
|
};
|
||||||
|
|
||||||
|
await pm.keywordsToPerson([p_noFaces]);
|
||||||
|
expect(p_noFaces.metadata.keywords).to.be.deep.equal(['just a keyword']);
|
||||||
|
expect(p_noFaces.metadata.faces.sort(cmp)).to.eql([{name: 'Han Solo'}].sort(cmp));
|
||||||
|
|
||||||
|
await pm.keywordsToPerson([p_wFace]);
|
||||||
|
expect(p_wFace.metadata.keywords).to.be.deep.equal([]);
|
||||||
|
expect(p_wFace.metadata.faces.sort(cmp)).to.be
|
||||||
|
.eql([{name: 'Han Solo'}, {name: 'Obivan'}, {name: 'Anakin'}].sort(cmp));
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user