Skip to content

Commit

Permalink
Improving preiview handling (fixing DB case insensitive issue when se…
Browse files Browse the repository at this point in the history
…lecting preview that is on the same path, adding tests) #31
  • Loading branch information
bpatrik committed Mar 28, 2021
1 parent 09e2a07 commit 5020949
Show file tree
Hide file tree
Showing 9 changed files with 148 additions and 39 deletions.
45 changes: 37 additions & 8 deletions src/backend/model/database/sql/GalleryManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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]';

Expand Down Expand Up @@ -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');
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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();
Expand All @@ -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]);
}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/backend/model/database/sql/SQLConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
6 changes: 5 additions & 1 deletion src/backend/model/threading/DiskMangerWorker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion src/common/Utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
4 changes: 3 additions & 1 deletion src/common/entities/DirectoryDTO.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -61,6 +61,8 @@ export module DirectoryDTO {
});
}

return dir;

};
export const filterPhotos = (dir: DirectoryDTO): PhotoDTO[] => {
return <PhotoDTO[]>dir.media.filter(m => MediaDTO.isPhoto(m));
Expand Down
60 changes: 59 additions & 1 deletion test/backend/unit/model/sql/IndexingManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand Down
16 changes: 7 additions & 9 deletions test/backend/unit/model/sql/PersonManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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',
Expand All @@ -64,7 +63,6 @@ describe('PersonManager', (sqlHelper: SQLTestHelper) => {
});



it('should get person', async () => {
const pm = new PersonManager();
const person = Utils.clone(savedPerson[0]);
Expand Down
19 changes: 11 additions & 8 deletions test/backend/unit/model/sql/SearchManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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];
};
Expand Down Expand Up @@ -176,16 +176,19 @@ 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) {
delete (ret.metadata as PhotoMetadata).faces;
}
m.directory.directories = tmpD;
m.directory.media = tmpM;
m.directory.preview = tmpP;
m.directory.metaFile = tmpMT;
return ret;
};
Expand Down Expand Up @@ -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
}));
Expand Down
Loading

0 comments on commit 5020949

Please sign in to comment.