mirror of
https://github.com/xuthus83/pigallery2.git
synced 2024-11-03 21:04:03 +08:00
fixing issue when indexing sub directory before parent directory
This commit is contained in:
parent
72025c7002
commit
83025f7b32
@ -12,8 +12,6 @@ export interface IPersonManager {
|
|||||||
|
|
||||||
saveAll(names: string[]): Promise<void>;
|
saveAll(names: string[]): Promise<void>;
|
||||||
|
|
||||||
keywordsToPerson(media: MediaDTO[]): Promise<void>;
|
|
||||||
|
|
||||||
onGalleryIndexUpdate(): Promise<void>;
|
onGalleryIndexUpdate(): Promise<void>;
|
||||||
|
|
||||||
updatePerson(name: string, partialPerson: PersonDTO): Promise<PersonEntry>;
|
updatePerson(name: string, partialPerson: PersonDTO): Promise<PersonEntry>;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import {IPersonManager} from '../interfaces/IPersonManager';
|
import {IPersonManager} from '../interfaces/IPersonManager';
|
||||||
import {MediaDTO} from '../../../common/entities/MediaDTO';
|
|
||||||
import {PhotoDTO} from '../../../common/entities/PhotoDTO';
|
import {PhotoDTO} from '../../../common/entities/PhotoDTO';
|
||||||
import {PersonDTO} from '../../../common/entities/PersonDTO';
|
import {PersonDTO} from '../../../common/entities/PersonDTO';
|
||||||
|
|
||||||
@ -13,10 +12,6 @@ export class PersonManager implements IPersonManager {
|
|||||||
throw new Error('Method not implemented.');
|
throw new Error('Method not implemented.');
|
||||||
}
|
}
|
||||||
|
|
||||||
keywordsToPerson(media: MediaDTO[]): Promise<void> {
|
|
||||||
throw new Error('Method not implemented.');
|
|
||||||
}
|
|
||||||
|
|
||||||
get(name: string): Promise<any> {
|
get(name: string): Promise<any> {
|
||||||
throw new Error('not supported by memory DB');
|
throw new Error('not supported by memory DB');
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ import {NotificationManager} from '../NotifocationManager';
|
|||||||
import {FaceRegionEntry} from './enitites/FaceRegionEntry';
|
import {FaceRegionEntry} from './enitites/FaceRegionEntry';
|
||||||
import {ObjectManagers} from '../ObjectManagers';
|
import {ObjectManagers} from '../ObjectManagers';
|
||||||
import {IIndexingManager} from '../interfaces/IIndexingManager';
|
import {IIndexingManager} from '../interfaces/IIndexingManager';
|
||||||
|
import {DiskMangerWorker} from '../threading/DiskMangerWorker';
|
||||||
|
|
||||||
const LOG_TAG = '[IndexingManager]';
|
const LOG_TAG = '[IndexingManager]';
|
||||||
|
|
||||||
@ -43,7 +44,7 @@ export class IndexingManager implements IIndexingManager {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Todo fix it, once typeorm support connection pools ofr sqlite
|
// Todo fix it, once typeorm support connection pools for sqlite
|
||||||
protected async queueForSave(scannedDirectory: DirectoryDTO) {
|
protected async queueForSave(scannedDirectory: DirectoryDTO) {
|
||||||
if (this.savingQueue.findIndex(dir => dir.name === scannedDirectory.name &&
|
if (this.savingQueue.findIndex(dir => dir.name === scannedDirectory.name &&
|
||||||
dir.path === scannedDirectory.path &&
|
dir.path === scannedDirectory.path &&
|
||||||
@ -89,30 +90,31 @@ export class IndexingManager implements IIndexingManager {
|
|||||||
|
|
||||||
protected async saveChildDirs(connection: Connection, currentDirId: number, scannedDirectory: DirectoryDTO) {
|
protected async saveChildDirs(connection: Connection, currentDirId: number, scannedDirectory: DirectoryDTO) {
|
||||||
const directoryRepository = connection.getRepository(DirectoryEntity);
|
const directoryRepository = connection.getRepository(DirectoryEntity);
|
||||||
// TODO: fix when first opened directory is not root
|
|
||||||
|
// update subdirectories that does not have a parent
|
||||||
|
await directoryRepository
|
||||||
|
.createQueryBuilder()
|
||||||
|
.update(DirectoryEntity)
|
||||||
|
.set({parent: <any>currentDirId})
|
||||||
|
.where('path = :path',
|
||||||
|
{path: DiskMangerWorker.pathFromParent(scannedDirectory)})
|
||||||
|
.andWhere('name NOT LIKE :root', {root: DiskMangerWorker.dirName('.')})
|
||||||
|
.andWhere('parent IS NULL')
|
||||||
|
.execute();
|
||||||
|
|
||||||
// save subdirectories
|
// save subdirectories
|
||||||
const childDirectories = await directoryRepository.createQueryBuilder('directory')
|
const childDirectories = await directoryRepository.createQueryBuilder('directory')
|
||||||
|
.leftJoinAndSelect('directory.parent', 'parent')
|
||||||
.where('directory.parent = :dir', {
|
.where('directory.parent = :dir', {
|
||||||
dir: currentDirId
|
dir: currentDirId
|
||||||
}).getMany();
|
}).getMany();
|
||||||
|
|
||||||
for (let i = 0; i < scannedDirectory.directories.length; i++) {
|
for (let i = 0; i < scannedDirectory.directories.length; i++) {
|
||||||
// Was this child Dir already indexed before?
|
// Was this child Dir already indexed before?
|
||||||
let directory: DirectoryEntity = null;
|
const dirIndex = childDirectories.findIndex(d => d.name === scannedDirectory.directories[i].name);
|
||||||
for (let j = 0; j < childDirectories.length; j++) {
|
|
||||||
if (childDirectories[j].name === scannedDirectory.directories[i].name) {
|
|
||||||
directory = childDirectories[j];
|
|
||||||
childDirectories.splice(j, 1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (directory != null) { // update existing directory
|
if (dirIndex !== -1) { // directory found
|
||||||
if (!directory.parent || !directory.parent.id) { // set parent if not set yet
|
childDirectories.splice(dirIndex, 1);
|
||||||
directory.parent = <any>{id: currentDirId};
|
|
||||||
delete directory.media;
|
|
||||||
await directoryRepository.save(directory);
|
|
||||||
}
|
|
||||||
} else { // dir does not exists yet
|
} else { // dir does not exists yet
|
||||||
scannedDirectory.directories[i].parent = <any>{id: currentDirId};
|
scannedDirectory.directories[i].parent = <any>{id: currentDirId};
|
||||||
(<DirectoryEntity>scannedDirectory.directories[i]).lastScanned = null; // new child dir, not fully scanned yet
|
(<DirectoryEntity>scannedDirectory.directories[i]).lastScanned = null; // new child dir, not fully scanned yet
|
||||||
@ -127,7 +129,7 @@ export class IndexingManager implements IIndexingManager {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async saveMetaFiles(connection: Connection, currentDirID: number, scannedDirectory: DirectoryDTO) {
|
protected async saveMetaFiles(connection: Connection, currentDirID: number, scannedDirectory: DirectoryDTO): Promise<void> {
|
||||||
const fileRepository = connection.getRepository(FileEntity);
|
const fileRepository = connection.getRepository(FileEntity);
|
||||||
// save files
|
// save files
|
||||||
const indexedMetaFiles = await fileRepository.createQueryBuilder('file')
|
const indexedMetaFiles = await fileRepository.createQueryBuilder('file')
|
||||||
@ -189,7 +191,6 @@ export class IndexingManager implements IIndexingManager {
|
|||||||
const scannedFaces = (<PhotoMetadata>media[i].metadata).faces || [];
|
const scannedFaces = (<PhotoMetadata>media[i].metadata).faces || [];
|
||||||
delete (<PhotoMetadata>media[i].metadata).faces;
|
delete (<PhotoMetadata>media[i].metadata).faces;
|
||||||
|
|
||||||
// let mediaItemId: number = null;
|
|
||||||
if (mediaItem == null) { // not in DB yet
|
if (mediaItem == null) { // not in DB yet
|
||||||
media[i].directory = null;
|
media[i].directory = null;
|
||||||
mediaItem = <any>Utils.clone(media[i]);
|
mediaItem = <any>Utils.clone(media[i]);
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import {IPersonManager} from '../interfaces/IPersonManager';
|
import {IPersonManager} from '../interfaces/IPersonManager';
|
||||||
import {SQLConnection} from './SQLConnection';
|
import {SQLConnection} from './SQLConnection';
|
||||||
import {PersonEntry} from './enitites/PersonEntry';
|
import {PersonEntry} from './enitites/PersonEntry';
|
||||||
import {MediaDTO} from '../../../common/entities/MediaDTO';
|
|
||||||
import {PhotoDTO} from '../../../common/entities/PhotoDTO';
|
import {PhotoDTO} from '../../../common/entities/PhotoDTO';
|
||||||
import {MediaEntity} from './enitites/MediaEntity';
|
import {MediaEntity} from './enitites/MediaEntity';
|
||||||
import {FaceRegionEntry} from './enitites/FaceRegionEntry';
|
import {FaceRegionEntry} from './enitites/FaceRegionEntry';
|
||||||
@ -70,30 +69,6 @@ export class PersonManager implements IPersonManager {
|
|||||||
return this.persons;
|
return this.persons;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 from keywords
|
|
||||||
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> {
|
||||||
|
|
||||||
|
@ -37,15 +37,31 @@ export class DiskMangerWorker {
|
|||||||
return Math.max(stat.ctime.getTime(), stat.mtime.getTime());
|
return Math.max(stat.ctime.getTime(), stat.mtime.getTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static normalizeDirPath(dirPath: string) {
|
public static normalizeDirPath(dirPath: string): string {
|
||||||
return path.normalize(path.join('.' + path.sep, dirPath));
|
return path.normalize(path.join('.' + path.sep, dirPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static pathFromRelativeDirName(relativeDirectoryName: string): string {
|
||||||
|
return path.join(path.dirname(this.normalizeDirPath(relativeDirectoryName)), path.sep);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static pathFromParent(parent: { path: string, name: string }): string {
|
||||||
|
return path.join(this.normalizeDirPath(path.join(parent.path, parent.name)), path.sep);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static dirName(name: string) {
|
||||||
|
if (name.trim().length === 0) {
|
||||||
|
return '.';
|
||||||
|
}
|
||||||
|
return path.basename(name);
|
||||||
|
}
|
||||||
|
|
||||||
public static scanDirectory(relativeDirectoryName: string, maxPhotos: number = null, photosOnly: boolean = false): Promise<DirectoryDTO> {
|
public static scanDirectory(relativeDirectoryName: string, maxPhotos: number = null, photosOnly: boolean = false): Promise<DirectoryDTO> {
|
||||||
return new Promise<DirectoryDTO>((resolve, reject) => {
|
return new Promise<DirectoryDTO>((resolve, reject) => {
|
||||||
relativeDirectoryName = this.normalizeDirPath(relativeDirectoryName);
|
relativeDirectoryName = this.normalizeDirPath(relativeDirectoryName);
|
||||||
const directoryName = path.basename(relativeDirectoryName);
|
const directoryName = DiskMangerWorker.dirName(relativeDirectoryName);
|
||||||
const directoryParent = path.join(path.dirname(relativeDirectoryName), path.sep);
|
const directoryParent = this.pathFromRelativeDirName(relativeDirectoryName);
|
||||||
const absoluteDirectoryName = path.join(ProjectPath.ImageFolder, relativeDirectoryName);
|
const absoluteDirectoryName = path.join(ProjectPath.ImageFolder, relativeDirectoryName);
|
||||||
|
|
||||||
const stat = fs.statSync(path.join(ProjectPath.ImageFolder, relativeDirectoryName));
|
const stat = fs.statSync(path.join(ProjectPath.ImageFolder, relativeDirectoryName));
|
||||||
|
@ -16,6 +16,7 @@ import {ObjectManagers} from '../../../../../backend/model/ObjectManagers';
|
|||||||
import {PersonManager} from '../../../../../backend/model/sql/PersonManager';
|
import {PersonManager} from '../../../../../backend/model/sql/PersonManager';
|
||||||
import {SQLTestHelper} from '../../../SQLTestHelper';
|
import {SQLTestHelper} from '../../../SQLTestHelper';
|
||||||
import {VersionManager} from '../../../../../backend/model/sql/VersionManager';
|
import {VersionManager} from '../../../../../backend/model/sql/VersionManager';
|
||||||
|
import {DiskMangerWorker} from '../../../../../backend/model/threading/DiskMangerWorker';
|
||||||
|
|
||||||
class GalleryManagerTest extends GalleryManager {
|
class GalleryManagerTest extends GalleryManager {
|
||||||
|
|
||||||
@ -182,6 +183,77 @@ describe('IndexingManager', (sqlHelper: SQLTestHelper) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should save parent after child', async () => {
|
||||||
|
const gm = new GalleryManagerTest();
|
||||||
|
const im = new IndexingManagerTest();
|
||||||
|
|
||||||
|
const parent = TestHelper.getRandomizedDirectoryEntry(null, 'parentDir');
|
||||||
|
const p1 = TestHelper.getRandomizedPhotoEntry(parent, 'Photo1');
|
||||||
|
|
||||||
|
const subDir = TestHelper.getRandomizedDirectoryEntry(null, 'subDir');
|
||||||
|
subDir.path = DiskMangerWorker.pathFromParent(parent);
|
||||||
|
const sp1 = TestHelper.getRandomizedPhotoEntry(subDir, 'subPhoto1', 0);
|
||||||
|
const sp2 = TestHelper.getRandomizedPhotoEntry(subDir, 'subPhoto2', 0);
|
||||||
|
|
||||||
|
|
||||||
|
DirectoryDTO.removeReferences(subDir);
|
||||||
|
await im.saveToDB(Utils.clone(subDir));
|
||||||
|
|
||||||
|
parent.directories.push(subDir);
|
||||||
|
|
||||||
|
|
||||||
|
DirectoryDTO.removeReferences(parent);
|
||||||
|
await im.saveToDB(Utils.clone(parent));
|
||||||
|
|
||||||
|
const conn = await SQLConnection.getConnection();
|
||||||
|
const selected = await gm.selectParentDir(conn, parent.name, parent.path);
|
||||||
|
await gm.fillParentDir(conn, selected);
|
||||||
|
|
||||||
|
DirectoryDTO.removeReferences(selected);
|
||||||
|
removeIds(selected);
|
||||||
|
subDir.isPartial = true;
|
||||||
|
delete subDir.directories;
|
||||||
|
delete subDir.metaFile;
|
||||||
|
expect(Utils.clone(Utils.removeNullOrEmptyObj(selected)))
|
||||||
|
.to.deep.equal(Utils.clone(Utils.removeNullOrEmptyObj(parent)));
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should save root parent after child', async () => {
|
||||||
|
const gm = new GalleryManagerTest();
|
||||||
|
const im = new IndexingManagerTest();
|
||||||
|
|
||||||
|
const parent = TestHelper.getRandomizedDirectoryEntry(null, '.');
|
||||||
|
const p1 = TestHelper.getRandomizedPhotoEntry(parent, 'Photo1');
|
||||||
|
|
||||||
|
const subDir = TestHelper.getRandomizedDirectoryEntry(null, 'subDir');
|
||||||
|
subDir.path = DiskMangerWorker.pathFromParent(parent);
|
||||||
|
const sp1 = TestHelper.getRandomizedPhotoEntry(subDir, 'subPhoto1', 0);
|
||||||
|
const sp2 = TestHelper.getRandomizedPhotoEntry(subDir, 'subPhoto2', 0);
|
||||||
|
|
||||||
|
|
||||||
|
DirectoryDTO.removeReferences(subDir);
|
||||||
|
await im.saveToDB(Utils.clone(subDir));
|
||||||
|
|
||||||
|
parent.directories.push(subDir);
|
||||||
|
|
||||||
|
|
||||||
|
DirectoryDTO.removeReferences(parent);
|
||||||
|
await im.saveToDB(Utils.clone(parent));
|
||||||
|
|
||||||
|
const conn = await SQLConnection.getConnection();
|
||||||
|
const selected = await gm.selectParentDir(conn, parent.name, parent.path);
|
||||||
|
await gm.fillParentDir(conn, selected);
|
||||||
|
|
||||||
|
DirectoryDTO.removeReferences(selected);
|
||||||
|
removeIds(selected);
|
||||||
|
subDir.isPartial = true;
|
||||||
|
delete subDir.directories;
|
||||||
|
delete subDir.metaFile;
|
||||||
|
expect(Utils.clone(Utils.removeNullOrEmptyObj(selected)))
|
||||||
|
.to.deep.equal(Utils.clone(Utils.removeNullOrEmptyObj(parent)));
|
||||||
|
});
|
||||||
|
|
||||||
it('should save parent directory', async () => {
|
it('should save parent directory', async () => {
|
||||||
const gm = new GalleryManagerTest();
|
const gm = new GalleryManagerTest();
|
||||||
const im = new IndexingManagerTest();
|
const im = new IndexingManagerTest();
|
||||||
|
@ -11,38 +11,5 @@ declare const it: any;
|
|||||||
|
|
||||||
describe('PersonManager', () => {
|
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: [], count: 0, isFavourite: false},
|
|
||||||
{name: 'Anakin', id: 2, faces: [], count: 0, isFavourite: false}];
|
|
||||||
|
|
||||||
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));
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -6,7 +6,6 @@ import {
|
|||||||
PhotoMetadataEntity,
|
PhotoMetadataEntity,
|
||||||
PositionMetaDataEntity
|
PositionMetaDataEntity
|
||||||
} from '../../../../../backend/model/sql/enitites/PhotoEntity';
|
} from '../../../../../backend/model/sql/enitites/PhotoEntity';
|
||||||
import * as path from 'path';
|
|
||||||
import {OrientationTypes} from 'ts-exif-parser';
|
import {OrientationTypes} from 'ts-exif-parser';
|
||||||
import {DirectoryEntity} from '../../../../../backend/model/sql/enitites/DirectoryEntity';
|
import {DirectoryEntity} from '../../../../../backend/model/sql/enitites/DirectoryEntity';
|
||||||
import {VideoEntity, VideoMetadataEntity} from '../../../../../backend/model/sql/enitites/VideoEntity';
|
import {VideoEntity, VideoMetadataEntity} from '../../../../../backend/model/sql/enitites/VideoEntity';
|
||||||
@ -14,6 +13,7 @@ import {MediaDimension} from '../../../../../common/entities/MediaDTO';
|
|||||||
import {CameraMetadata, FaceRegion, GPSMetadata, PhotoDTO, PhotoMetadata, PositionMetaData} from '../../../../../common/entities/PhotoDTO';
|
import {CameraMetadata, FaceRegion, GPSMetadata, PhotoDTO, PhotoMetadata, PositionMetaData} from '../../../../../common/entities/PhotoDTO';
|
||||||
import {DirectoryDTO} from '../../../../../common/entities/DirectoryDTO';
|
import {DirectoryDTO} from '../../../../../common/entities/DirectoryDTO';
|
||||||
import {FileDTO} from '../../../../../common/entities/FileDTO';
|
import {FileDTO} from '../../../../../common/entities/FileDTO';
|
||||||
|
import {DiskMangerWorker} from '../../../../../backend/model/threading/DiskMangerWorker';
|
||||||
|
|
||||||
export class TestHelper {
|
export class TestHelper {
|
||||||
|
|
||||||
@ -153,8 +153,8 @@ export class TestHelper {
|
|||||||
|
|
||||||
const dir: DirectoryDTO = {
|
const dir: DirectoryDTO = {
|
||||||
id: null,
|
id: null,
|
||||||
name: forceStr || Math.random().toString(36).substring(7),
|
name: DiskMangerWorker.dirName(forceStr || Math.random().toString(36).substring(7)),
|
||||||
path: '.',
|
path: DiskMangerWorker.pathFromParent({path: '', name: '.'}),
|
||||||
mediaCount: 0,
|
mediaCount: 0,
|
||||||
directories: [],
|
directories: [],
|
||||||
metaFile: [],
|
metaFile: [],
|
||||||
@ -164,7 +164,7 @@ export class TestHelper {
|
|||||||
parent: null
|
parent: null
|
||||||
};
|
};
|
||||||
if (parent !== null) {
|
if (parent !== null) {
|
||||||
dir.path = path.join(parent.path, parent.name);
|
dir.path = DiskMangerWorker.pathFromParent(parent);
|
||||||
parent.directories.push(dir);
|
parent.directories.push(dir);
|
||||||
}
|
}
|
||||||
return dir;
|
return dir;
|
||||||
|
Loading…
Reference in New Issue
Block a user