mirror of
https://github.com/bpatrik/pigallery2.git
synced 2025-02-01 13:17:55 +02:00
Improving preiview handling (fixing DB case insensitive issue when selecting preview that is on the same path, adding tests) #31
This commit is contained in:
parent
09e2a07c5e
commit
5020949a88
@ -18,6 +18,7 @@ import {FaceRegionEntry} from './enitites/FaceRegionEntry';
|
||||
import {ObjectManagers} from '../../ObjectManagers';
|
||||
import {DuplicatesDTO} from '../../../../common/entities/DuplicatesDTO';
|
||||
import {ServerConfig} from '../../../../common/config/private/PrivateConfig';
|
||||
import DatabaseType = ServerConfig.DatabaseType;
|
||||
|
||||
const LOG_TAG = '[GalleryManager]';
|
||||
|
||||
@ -202,7 +203,8 @@ export class GalleryManager implements IGalleryManager, ISQLGalleryManager {
|
||||
path: directoryParent
|
||||
})
|
||||
.leftJoinAndSelect('directory.directories', 'directories')
|
||||
.leftJoinAndSelect('directory.media', 'media');
|
||||
.leftJoinAndSelect('directory.media', 'media')
|
||||
.orderBy('media.metadata.creationDate', 'DESC');
|
||||
|
||||
if (Config.Client.MetaFile.enabled === true) {
|
||||
query.leftJoinAndSelect('directory.metaFile', 'metaFile');
|
||||
@ -211,6 +213,33 @@ export class GalleryManager implements IGalleryManager, ISQLGalleryManager {
|
||||
return await query.getOne();
|
||||
}
|
||||
|
||||
protected async fillPreviewFromSubDir(connection: Connection, dir: DirectoryEntity): Promise<void> {
|
||||
dir.media = [];
|
||||
const query = connection
|
||||
.getRepository(MediaEntity)
|
||||
.createQueryBuilder('media')
|
||||
.innerJoinAndSelect('media.directory', 'directory');
|
||||
|
||||
if (Config.Server.Database.type === DatabaseType.mysql) {
|
||||
query.where('directory.path like :path || \'%\'', {
|
||||
path: (DiskMangerWorker.pathFromParent(dir))
|
||||
});
|
||||
} else {
|
||||
query.where('directory.path GLOB :path', {
|
||||
path: DiskMangerWorker.pathFromParent(dir) + '*'
|
||||
});
|
||||
}
|
||||
dir.preview = await query.orderBy('media.metadata.creationDate', 'DESC')
|
||||
.limit(1)
|
||||
.getOne();
|
||||
|
||||
if (dir.preview) {
|
||||
dir.preview.directory = dir;
|
||||
dir.preview.readyThumbnails = [];
|
||||
dir.preview.readyIcon = false;
|
||||
}
|
||||
}
|
||||
|
||||
protected async fillParentDir(connection: Connection, dir: DirectoryEntity): Promise<void> {
|
||||
if (dir.media) {
|
||||
const indexedFaces = await connection.getRepository(FaceRegionEntry)
|
||||
@ -232,14 +261,15 @@ export class GalleryManager implements IGalleryManager, ISQLGalleryManager {
|
||||
.filter(fe => fe.media.id === dir.media[i].id)
|
||||
.map(f => ({box: f.box, name: f.person.name}));
|
||||
}
|
||||
|
||||
if (dir.media.length > 0) {
|
||||
dir.preview = dir.media[0];
|
||||
} else {
|
||||
await this.fillPreviewFromSubDir(connection, dir);
|
||||
}
|
||||
}
|
||||
if (dir.directories) {
|
||||
for (let i = 0; i < dir.directories.length; i++) {
|
||||
|
||||
const _path = dir.path;
|
||||
const currentRoot = (_path === './' ? '' : _path);
|
||||
const dirName = currentRoot + dir.name + path.sep;
|
||||
dir.directories[i].media = [];
|
||||
dir.directories[i].preview = await connection
|
||||
.getRepository(MediaEntity)
|
||||
@ -248,9 +278,6 @@ export class GalleryManager implements IGalleryManager, ISQLGalleryManager {
|
||||
.where('media.directory = :dir', {
|
||||
dir: dir.directories[i].id
|
||||
})
|
||||
.orWhere('directory.path like :parentPath||\'%\'', {
|
||||
parentPath: dirName
|
||||
})
|
||||
.orderBy('media.metadata.creationDate', 'DESC')
|
||||
.limit(1)
|
||||
.getOne();
|
||||
@ -260,6 +287,8 @@ export class GalleryManager implements IGalleryManager, ISQLGalleryManager {
|
||||
dir.directories[i].preview.directory = dir.directories[i];
|
||||
dir.directories[i].preview.readyThumbnails = [];
|
||||
dir.directories[i].preview.readyIcon = false;
|
||||
} else {
|
||||
await this.fillPreviewFromSubDir(connection, dir.directories[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ import {PersonEntry} from './enitites/PersonEntry';
|
||||
import {Utils} from '../../../../common/Utils';
|
||||
import * as path from 'path';
|
||||
import {ServerConfig} from '../../../../common/config/private/PrivateConfig';
|
||||
import DatabaseType = ServerConfig.DatabaseType;
|
||||
|
||||
|
||||
export class SQLConnection {
|
||||
|
@ -148,10 +148,14 @@ export class DiskMangerWorker {
|
||||
if (!directory.preview) {
|
||||
directory.preview = photo;
|
||||
}
|
||||
// add the preview photo to the list of media, so it will be saved to the DB
|
||||
// and can be queried to populate previews,
|
||||
// otherwise we do not return media list that is only partial
|
||||
directory.media.push(photo);
|
||||
|
||||
if (settings.previewOnly === true) {
|
||||
break;
|
||||
}
|
||||
directory.media.push(photo);
|
||||
|
||||
} else if (VideoProcessing.isVideo(fullFilePath)) {
|
||||
if (Config.Client.Media.Video.enabled === false || settings.noVideo === true || settings.previewOnly === true) {
|
||||
|
@ -24,7 +24,7 @@ export class Utils {
|
||||
}
|
||||
|
||||
|
||||
static removeNullOrEmptyObj(obj: any) {
|
||||
static removeNullOrEmptyObj<T extends any>(obj: T): T {
|
||||
if (typeof obj !== 'object' || obj == null) {
|
||||
return obj;
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ export module DirectoryDTO {
|
||||
}
|
||||
};
|
||||
|
||||
export const removeReferences = (dir: DirectoryDTO): void => {
|
||||
export const removeReferences = (dir: DirectoryDTO): DirectoryDTO => {
|
||||
if (dir.media) {
|
||||
dir.media.forEach((media: MediaDTO) => {
|
||||
media.directory = null;
|
||||
@ -61,6 +61,8 @@ export module DirectoryDTO {
|
||||
});
|
||||
}
|
||||
|
||||
return dir;
|
||||
|
||||
};
|
||||
export const filterPhotos = (dir: DirectoryDTO): PhotoDTO[] => {
|
||||
return <PhotoDTO[]>dir.media.filter(m => MediaDTO.isPhoto(m));
|
||||
|
@ -69,7 +69,9 @@ describe('IndexingManager', (sqlHelper: SQLTestHelper) => {
|
||||
});
|
||||
|
||||
const setPartial = (dir: DirectoryDTO) => {
|
||||
dir.preview = dir.media[0];
|
||||
if (!dir.preview && dir.media && dir.media.length > 0) {
|
||||
dir.preview = dir.media[0];
|
||||
}
|
||||
dir.isPartial = true;
|
||||
delete dir.directories;
|
||||
delete dir.metaFile;
|
||||
@ -191,6 +193,62 @@ describe('IndexingManager', (sqlHelper: SQLTestHelper) => {
|
||||
});
|
||||
|
||||
|
||||
it('should select preview', async () => {
|
||||
const selectDirectory = async (_gm: GalleryManagerTest, dir: DirectoryDTO) => {
|
||||
const conn = await SQLConnection.getConnection();
|
||||
const selected = await _gm.selectParentDir(conn, dir.name, dir.path);
|
||||
await _gm.fillParentDir(conn, selected);
|
||||
|
||||
DirectoryDTO.removeReferences(selected);
|
||||
removeIds(selected);
|
||||
return selected;
|
||||
};
|
||||
|
||||
const gm = new GalleryManagerTest();
|
||||
const im = new IndexingManagerTest();
|
||||
|
||||
|
||||
const parent = TestHelper.getRandomizedDirectoryEntry(null, 'parent');
|
||||
|
||||
|
||||
const checkParent = async () => {
|
||||
const selected = await selectDirectory(gm, parent);
|
||||
const cloned = Utils.removeNullOrEmptyObj(Utils.clone(parent));
|
||||
if (cloned.directories) {
|
||||
cloned.directories.forEach(d => setPartial(d));
|
||||
}
|
||||
expect(Utils.clone(Utils.removeNullOrEmptyObj(selected)))
|
||||
.to.deep.equalInAnyOrder(cloned);
|
||||
};
|
||||
|
||||
const saveToDBAndCheck = async (dir: DirectoryDTO) => {
|
||||
DirectoryDTO.removeReferences(parent);
|
||||
await im.saveToDB(Utils.clone(dir));
|
||||
await checkParent();
|
||||
DirectoryDTO.addReferences(parent);
|
||||
};
|
||||
|
||||
await saveToDBAndCheck(parent);
|
||||
|
||||
const subDir1 = TestHelper.getRandomizedDirectoryEntry(parent, 'subDir');
|
||||
await saveToDBAndCheck(parent);
|
||||
|
||||
const p1 = TestHelper.getRandomizedPhotoEntry(subDir1, 'subPhoto1', 0);
|
||||
await saveToDBAndCheck(subDir1);
|
||||
|
||||
const subDir2 = TestHelper.getRandomizedDirectoryEntry(parent, 'subDir2');
|
||||
await saveToDBAndCheck(parent);
|
||||
|
||||
const p2 = TestHelper.getRandomizedPhotoEntry(subDir2, 'subPhoto2', 0);
|
||||
await saveToDBAndCheck(subDir2);
|
||||
|
||||
const p = TestHelper.getRandomizedPhotoEntry(parent, 'photo', 0);
|
||||
await saveToDBAndCheck(parent);
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
it('should save parent after child', async () => {
|
||||
const gm = new GalleryManagerTest();
|
||||
const im = new IndexingManagerTest();
|
||||
|
@ -2,14 +2,13 @@ import {expect} from 'chai';
|
||||
import {PersonManager} from '../../../../../src/backend/model/database/sql/PersonManager';
|
||||
import {SQLTestHelper} from '../../../SQLTestHelper';
|
||||
import {TestHelper} from './TestHelper';
|
||||
import {PhotoDTO, PhotoMetadata} from '../../../../../src/common/entities/PhotoDTO';
|
||||
import {PhotoDTO} from '../../../../../src/common/entities/PhotoDTO';
|
||||
import {Utils} from '../../../../../src/common/Utils';
|
||||
import {PersonWithSampleRegion} from '../../../../../src/common/entities/PersonDTO';
|
||||
import {DirectoryDTO} from '../../../../../src/common/entities/DirectoryDTO';
|
||||
import {VideoDTO} from '../../../../../src/common/entities/VideoDTO';
|
||||
import {SQLConnection} from '../../../../../src/backend/model/database/sql/SQLConnection';
|
||||
import {PersonEntry} from '../../../../../src/backend/model/database/sql/enitites/PersonEntry';
|
||||
import {MediaDTO} from '../../../../../src/common/entities/MediaDTO';
|
||||
|
||||
|
||||
// to help WebStorm to handle the test cases
|
||||
@ -36,17 +35,17 @@ describe('PersonManager', (sqlHelper: SQLTestHelper) => {
|
||||
const setUpSqlDB = async () => {
|
||||
await sqlHelper.initDB();
|
||||
const directory: DirectoryDTO = TestHelper.getDirectoryEntry();
|
||||
TestHelper.getPhotoEntry1(directory);
|
||||
TestHelper.getPhotoEntry2(directory);
|
||||
p = TestHelper.getPhotoEntry1(directory);
|
||||
p2 = TestHelper.getPhotoEntry2(directory);
|
||||
const pFaceLess = TestHelper.getPhotoEntry3(directory);
|
||||
delete pFaceLess.metadata.faces;
|
||||
TestHelper.getVideoEntry1(directory);
|
||||
v = TestHelper.getVideoEntry1(directory);
|
||||
|
||||
dir = await SQLTestHelper.persistTestDir(directory);
|
||||
p = <any>dir.media[0];
|
||||
p2 = <any>dir.media[1];
|
||||
p = <any>dir.media.filter(m => m.name === p.name)[0];
|
||||
p2 = <any>dir.media.filter(m => m.name === p2.name)[0];
|
||||
p_faceLess = <any>dir.media[2];
|
||||
v = <any>dir.media[3];
|
||||
v = <any>dir.media.filter(m => m.name === v.name)[0];
|
||||
savedPerson = await (await SQLConnection.getConnection()).getRepository(PersonEntry).find({
|
||||
relations: ['sampleRegion',
|
||||
'sampleRegion.media',
|
||||
@ -64,7 +63,6 @@ describe('PersonManager', (sqlHelper: SQLTestHelper) => {
|
||||
});
|
||||
|
||||
|
||||
|
||||
it('should get person', async () => {
|
||||
const pm = new PersonManager();
|
||||
const person = Utils.clone(savedPerson[0]);
|
||||
|
@ -90,17 +90,17 @@ describe('SearchManager', (sqlHelper: SQLTestHelper) => {
|
||||
const directory: DirectoryDTO = TestHelper.getDirectoryEntry();
|
||||
const subDir = TestHelper.getDirectoryEntry(directory, 'The Phantom Menace');
|
||||
const subDir2 = TestHelper.getDirectoryEntry(directory, 'Return of the Jedi');
|
||||
TestHelper.getPhotoEntry1(directory);
|
||||
TestHelper.getPhotoEntry2(directory);
|
||||
TestHelper.getPhotoEntry4(subDir2);
|
||||
p = TestHelper.getPhotoEntry1(directory);
|
||||
p2 = TestHelper.getPhotoEntry2(directory);
|
||||
p4 = TestHelper.getPhotoEntry4(subDir2);
|
||||
const pFaceLess = TestHelper.getPhotoEntry3(subDir);
|
||||
delete pFaceLess.metadata.faces;
|
||||
TestHelper.getVideoEntry1(directory);
|
||||
v = TestHelper.getVideoEntry1(directory);
|
||||
|
||||
dir = await SQLTestHelper.persistTestDir(directory);
|
||||
p = <any>dir.media[0];
|
||||
p2 = <any>dir.media[1];
|
||||
v = <any>dir.media[2];
|
||||
p = <any>dir.media.filter(m => m.name === p.name)[0];
|
||||
p2 = <any>dir.media.filter(m => m.name === p2.name)[0];
|
||||
v = <any>dir.media.filter(m => m.name === v.name)[0];
|
||||
p4 = <any>dir.directories[1].media[0];
|
||||
p_faceLess = <any>dir.directories[0].media[0];
|
||||
};
|
||||
@ -176,9 +176,11 @@ describe('SearchManager', (sqlHelper: SQLTestHelper) => {
|
||||
const searchifyMedia = (m: MediaDTO): MediaDTO => {
|
||||
const tmpM = m.directory.media;
|
||||
const tmpD = m.directory.directories;
|
||||
const tmpP = m.directory.preview;
|
||||
const tmpMT = m.directory.metaFile;
|
||||
delete m.directory.directories;
|
||||
delete m.directory.media;
|
||||
delete m.directory.preview;
|
||||
delete m.directory.metaFile;
|
||||
const ret = Utils.clone(m);
|
||||
if ((ret.metadata as PhotoMetadata).faces && !(ret.metadata as PhotoMetadata).faces.length) {
|
||||
@ -186,6 +188,7 @@ describe('SearchManager', (sqlHelper: SQLTestHelper) => {
|
||||
}
|
||||
m.directory.directories = tmpD;
|
||||
m.directory.media = tmpM;
|
||||
m.directory.preview = tmpP;
|
||||
m.directory.metaFile = tmpMT;
|
||||
return ret;
|
||||
};
|
||||
@ -946,7 +949,7 @@ describe('SearchManager', (sqlHelper: SQLTestHelper) => {
|
||||
.to.deep.equalInAnyOrder(removeDir(<SearchResultDTO>{
|
||||
searchQuery: query,
|
||||
directories: [],
|
||||
media: [p, p2, p_faceLess, v],
|
||||
media: [p, p2, p_faceLess, v],
|
||||
metaFile: [],
|
||||
resultOverflow: false
|
||||
}));
|
||||
|
@ -24,6 +24,8 @@ import {DiskMangerWorker} from '../../../../../src/backend/model/threading/DiskM
|
||||
|
||||
export class TestHelper {
|
||||
|
||||
static creationCounter = 0;
|
||||
|
||||
public static getDirectoryEntry(parent: DirectoryDTO = null, name = 'wars dir') {
|
||||
|
||||
const dir = new DirectoryEntity();
|
||||
@ -156,7 +158,6 @@ export class TestHelper {
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
public static getPhotoEntry2(dir: DirectoryDTO) {
|
||||
const p = TestHelper.getPhotoEntry(dir);
|
||||
|
||||
@ -249,7 +250,6 @@ export class TestHelper {
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
public static getRandomizedDirectoryEntry(parent: DirectoryDTO = null, forceStr: string = null) {
|
||||
|
||||
const dir: DirectoryDTO = {
|
||||
@ -263,7 +263,7 @@ export class TestHelper {
|
||||
media: [],
|
||||
lastModified: Date.now(),
|
||||
lastScanned: null,
|
||||
parent: null
|
||||
parent: parent
|
||||
};
|
||||
if (parent !== null) {
|
||||
dir.path = DiskMangerWorker.pathFromParent(parent);
|
||||
@ -272,7 +272,6 @@ export class TestHelper {
|
||||
return dir;
|
||||
}
|
||||
|
||||
|
||||
public static getRandomizedGPXEntry(dir: DirectoryDTO, forceStr: string = null): FileDTO {
|
||||
const d: FileDTO = {
|
||||
id: null,
|
||||
@ -283,7 +282,6 @@ export class TestHelper {
|
||||
return d;
|
||||
}
|
||||
|
||||
|
||||
public static getRandomizedFace(media: PhotoDTO, forceStr: string = null) {
|
||||
const rndStr = () => {
|
||||
return forceStr + '_' + Math.random().toString(36).substring(7);
|
||||
@ -348,7 +346,7 @@ export class TestHelper {
|
||||
cameraData: cd,
|
||||
positionData: pd,
|
||||
size: sd,
|
||||
creationDate: Date.now(),
|
||||
creationDate: Date.now() + ++TestHelper.creationCounter,
|
||||
fileSize: rndInt(10000),
|
||||
orientation: OrientationTypes.TOP_LEFT,
|
||||
caption: rndStr(),
|
||||
@ -356,7 +354,7 @@ export class TestHelper {
|
||||
};
|
||||
|
||||
|
||||
const d: PhotoDTO = {
|
||||
const p: PhotoDTO = {
|
||||
id: null,
|
||||
name: rndStr() + '.jpg',
|
||||
directory: dir,
|
||||
@ -366,11 +364,27 @@ export class TestHelper {
|
||||
};
|
||||
|
||||
for (let i = 0; i < faces; i++) {
|
||||
this.getRandomizedFace(d, 'Person ' + i);
|
||||
this.getRandomizedFace(p, 'Person ' + i);
|
||||
}
|
||||
|
||||
dir.media.push(p);
|
||||
TestHelper.updatePreview(dir);
|
||||
return p;
|
||||
}
|
||||
|
||||
static updatePreview(dir: DirectoryDTO) {
|
||||
if (dir.media.length > 0) {
|
||||
dir.preview = dir.media.sort((a, b) => b.metadata.creationDate - a.metadata.creationDate)[0];
|
||||
} else {
|
||||
const filtered = dir.directories.filter(d => d.preview).map(d => d.preview);
|
||||
if (filtered.length > 0) {
|
||||
dir.preview = filtered.sort((a, b) => b.metadata.creationDate - a.metadata.creationDate)[0];
|
||||
}
|
||||
}
|
||||
if (dir.parent) {
|
||||
TestHelper.updatePreview(dir.parent);
|
||||
}
|
||||
|
||||
dir.media.push(d);
|
||||
return d;
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user