mirror of
https://github.com/bpatrik/pigallery2.git
synced 2025-01-12 04:23:09 +02:00
improving directory indexing race condition bug
This commit is contained in:
parent
38f36891bd
commit
d035f167ee
@ -52,17 +52,22 @@ export class ThumbnailGeneratorMWs {
|
||||
return next();
|
||||
}
|
||||
|
||||
const cw: ContentWrapper = req.resultPipe;
|
||||
if (cw.notModified === true) {
|
||||
return next();
|
||||
}
|
||||
if (cw.directory) {
|
||||
ThumbnailGeneratorMWs.addThInfoTODir(<DirectoryDTO>cw.directory);
|
||||
}
|
||||
if (cw.searchResult) {
|
||||
ThumbnailGeneratorMWs.addThInfoToPhotos(cw.searchResult.media);
|
||||
}
|
||||
try {
|
||||
const cw: ContentWrapper = req.resultPipe;
|
||||
if (cw.notModified === true) {
|
||||
return next();
|
||||
}
|
||||
if (cw.directory) {
|
||||
ThumbnailGeneratorMWs.addThInfoTODir(cw.directory);
|
||||
}
|
||||
if (cw.searchResult && cw.searchResult.media) {
|
||||
ThumbnailGeneratorMWs.addThInfoToPhotos(cw.searchResult.media);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
return next(new ErrorDTO(ErrorCodes.SERVER_ERROR, 'error during postprocessing result', error.toString()));
|
||||
|
||||
}
|
||||
|
||||
return next();
|
||||
|
||||
@ -102,18 +107,14 @@ export class ThumbnailGeneratorMWs {
|
||||
}
|
||||
|
||||
private static addThInfoTODir(directory: DirectoryDTO) {
|
||||
if (typeof directory.media === 'undefined') {
|
||||
directory.media = [];
|
||||
if (typeof directory.media !== 'undefined') {
|
||||
ThumbnailGeneratorMWs.addThInfoToPhotos(directory.media);
|
||||
}
|
||||
if (typeof directory.directories === 'undefined') {
|
||||
directory.directories = [];
|
||||
if (typeof directory.directories !== 'undefined') {
|
||||
for (let i = 0; i < directory.directories.length; i++) {
|
||||
ThumbnailGeneratorMWs.addThInfoTODir(directory.directories[i]);
|
||||
}
|
||||
}
|
||||
ThumbnailGeneratorMWs.addThInfoToPhotos(directory.media);
|
||||
|
||||
for (let i = 0; i < directory.directories.length; i++) {
|
||||
ThumbnailGeneratorMWs.addThInfoTODir(directory.directories[i]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static addThInfoToPhotos(photos: MediaDTO[]) {
|
||||
@ -135,7 +136,6 @@ export class ThumbnailGeneratorMWs {
|
||||
if (fs.existsSync(iconPath) === true) {
|
||||
photos[i].readyIcon = true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,7 @@ import {ISQLGalleryManager} from './IGalleryManager';
|
||||
import {DatabaseType, ReIndexingSensitivity} from '../../../common/config/private/IPrivateConfig';
|
||||
import {PhotoDTO} from '../../../common/entities/PhotoDTO';
|
||||
import {OrientationType} from '../../../common/entities/RandomQueryDTO';
|
||||
import {Brackets, Connection} from 'typeorm';
|
||||
import {Brackets, Connection, Transaction, TransactionRepository, Repository} from 'typeorm';
|
||||
import {MediaEntity} from './enitites/MediaEntity';
|
||||
import {MediaDTO} from '../../../common/entities/MediaDTO';
|
||||
import {VideoEntity} from './enitites/VideoEntity';
|
||||
@ -23,6 +23,9 @@ import {NotificationManager} from '../NotifocationManager';
|
||||
|
||||
export class GalleryManager implements IGalleryManager, ISQLGalleryManager {
|
||||
|
||||
private savingQueue: DirectoryDTO[] = [];
|
||||
private isSaving = false;
|
||||
|
||||
protected async selectParentDir(connection: Connection, directoryName: string, directoryParent: string): Promise<DirectoryEntity> {
|
||||
const query = connection
|
||||
.getRepository(DirectoryEntity)
|
||||
@ -34,7 +37,7 @@ export class GalleryManager implements IGalleryManager, ISQLGalleryManager {
|
||||
.leftJoinAndSelect('directory.directories', 'directories')
|
||||
.leftJoinAndSelect('directory.media', 'media');
|
||||
|
||||
if (Config.Client.MetaFile.enabled == true) {
|
||||
if (Config.Client.MetaFile.enabled === true) {
|
||||
query.leftJoinAndSelect('directory.metaFile', 'metaFile');
|
||||
}
|
||||
|
||||
@ -75,15 +78,17 @@ export class GalleryManager implements IGalleryManager, ISQLGalleryManager {
|
||||
public async listDirectory(relativeDirectoryName: string,
|
||||
knownLastModified?: number,
|
||||
knownLastScanned?: number): Promise<DirectoryDTO> {
|
||||
|
||||
relativeDirectoryName = path.normalize(path.join('.' + path.sep, relativeDirectoryName));
|
||||
const directoryName = path.basename(relativeDirectoryName);
|
||||
const directoryParent = path.join(path.dirname(relativeDirectoryName), path.sep);
|
||||
const connection = await SQLConnection.getConnection();
|
||||
const stat = fs.statSync(path.join(ProjectPath.ImageFolder, relativeDirectoryName));
|
||||
const lastModified = Math.max(stat.ctime.getTime(), stat.mtime.getTime());
|
||||
const dir = await this.selectParentDir(connection, directoryName, directoryParent);
|
||||
|
||||
|
||||
const dir = await this.selectParentDir(connection, directoryName, directoryParent);
|
||||
|
||||
if (dir && dir.lastScanned != null) {
|
||||
// If it seems that the content did not changed, do not work on it
|
||||
if (knownLastModified && knownLastScanned
|
||||
@ -135,7 +140,7 @@ export class GalleryManager implements IGalleryManager, ISQLGalleryManager {
|
||||
scannedDirectory.media.forEach(p => p.readyThumbnails = []);
|
||||
resolve(scannedDirectory);
|
||||
|
||||
await this.saveToDB(scannedDirectory);
|
||||
this.queueForSave(scannedDirectory).catch(console.error);
|
||||
|
||||
} catch (error) {
|
||||
NotificationManager.warning('Unknown indexing error for: ' + relativeDirectoryName, error.toString());
|
||||
@ -204,139 +209,151 @@ export class GalleryManager implements IGalleryManager, ISQLGalleryManager {
|
||||
|
||||
}
|
||||
|
||||
// Todo fix it, once typeorm support connection pools ofr sqlite
|
||||
protected async queueForSave(scannedDirectory: DirectoryDTO) {
|
||||
if (this.savingQueue.findIndex(dir => dir.name === scannedDirectory.name &&
|
||||
dir.path === scannedDirectory.path &&
|
||||
dir.lastModified === scannedDirectory.lastModified &&
|
||||
dir.lastScanned === scannedDirectory.lastScanned &&
|
||||
(dir.media || dir.media.length) === (scannedDirectory.media || scannedDirectory.media.length) &&
|
||||
(dir.metaFile || dir.metaFile.length) === (scannedDirectory.metaFile || scannedDirectory.metaFile.length)) !== -1) {
|
||||
return;
|
||||
}
|
||||
this.savingQueue.push(scannedDirectory);
|
||||
while (this.isSaving === false && this.savingQueue.length > 0) {
|
||||
await this.saveToDB(this.savingQueue[0]);
|
||||
this.savingQueue.shift();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected async saveToDB(scannedDirectory: DirectoryDTO) {
|
||||
const connection = await SQLConnection.getConnection();
|
||||
this.isSaving = true;
|
||||
try {
|
||||
const connection = await SQLConnection.getConnection();
|
||||
|
||||
// saving to db
|
||||
const directoryRepository = connection.getRepository(DirectoryEntity);
|
||||
const mediaRepository = connection.getRepository(MediaEntity);
|
||||
const fileRepository = connection.getRepository(FileEntity);
|
||||
// saving to db
|
||||
const directoryRepository = connection.getRepository(DirectoryEntity);
|
||||
const mediaRepository = connection.getRepository(MediaEntity);
|
||||
const fileRepository = connection.getRepository(FileEntity);
|
||||
|
||||
|
||||
let currentDir: DirectoryEntity = await directoryRepository.createQueryBuilder('directory')
|
||||
.where('directory.name = :name AND directory.path = :path', {
|
||||
name: scannedDirectory.name,
|
||||
path: scannedDirectory.path
|
||||
}).getOne();
|
||||
|
||||
if (!!currentDir) {// Updated parent dir (if it was in the DB previously)
|
||||
currentDir.lastModified = scannedDirectory.lastModified;
|
||||
currentDir.lastScanned = scannedDirectory.lastScanned;
|
||||
// const media: MediaEntity[] = currentDir.media;
|
||||
// delete currentDir.media;
|
||||
currentDir = await directoryRepository.save(currentDir);
|
||||
/*if (media) {
|
||||
media.forEach(m => m.directory = currentDir);
|
||||
currentDir.media = await this.saveMedia(connection, media);
|
||||
}*/
|
||||
} else {
|
||||
// const media = scannedDirectory.media;
|
||||
// delete scannedDirectory.media;
|
||||
(<DirectoryEntity>scannedDirectory).lastScanned = scannedDirectory.lastScanned;
|
||||
currentDir = await directoryRepository.save(<DirectoryEntity>scannedDirectory);
|
||||
/* if (media) {
|
||||
media.forEach(m => m.directory = currentDir);
|
||||
currentDir.media = await this.saveMedia(connection, media);
|
||||
}*/
|
||||
}
|
||||
|
||||
// save subdirectories
|
||||
const childDirectories = await directoryRepository.createQueryBuilder('directory')
|
||||
.where('directory.parent = :dir', {
|
||||
dir: currentDir.id
|
||||
}).getMany();
|
||||
|
||||
for (let i = 0; i < scannedDirectory.directories.length; i++) {
|
||||
// Was this child Dir already indexed before?
|
||||
let directory: DirectoryEntity = null;
|
||||
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 (!directory.parent || !directory.parent.id) { // set parent if not set yet
|
||||
directory.parent = currentDir;
|
||||
delete directory.media;
|
||||
await directoryRepository.save(directory);
|
||||
}
|
||||
let currentDir: DirectoryEntity = await directoryRepository.createQueryBuilder('directory')
|
||||
.where('directory.name = :name AND directory.path = :path', {
|
||||
name: scannedDirectory.name,
|
||||
path: scannedDirectory.path
|
||||
}).getOne();
|
||||
if (!!currentDir) {// Updated parent dir (if it was in the DB previously)
|
||||
currentDir.lastModified = scannedDirectory.lastModified;
|
||||
currentDir.lastScanned = scannedDirectory.lastScanned;
|
||||
currentDir = await directoryRepository.save(currentDir);
|
||||
} else {
|
||||
scannedDirectory.directories[i].parent = currentDir;
|
||||
(<DirectoryEntity>scannedDirectory.directories[i]).lastScanned = null; // new child dir, not fully scanned yet
|
||||
const d = await directoryRepository.save(<DirectoryEntity>scannedDirectory.directories[i]);
|
||||
for (let j = 0; j < scannedDirectory.directories[i].media.length; j++) {
|
||||
scannedDirectory.directories[i].media[j].directory = d;
|
||||
(<DirectoryEntity>scannedDirectory).lastScanned = scannedDirectory.lastScanned;
|
||||
currentDir = await directoryRepository.save(<DirectoryEntity>scannedDirectory);
|
||||
}
|
||||
|
||||
|
||||
// save subdirectories
|
||||
const childDirectories = await directoryRepository.createQueryBuilder('directory')
|
||||
.where('directory.parent = :dir', {
|
||||
dir: currentDir.id
|
||||
}).getMany();
|
||||
|
||||
for (let i = 0; i < scannedDirectory.directories.length; i++) {
|
||||
// Was this child Dir already indexed before?
|
||||
let directory: DirectoryEntity = null;
|
||||
for (let j = 0; j < childDirectories.length; j++) {
|
||||
if (childDirectories[j].name === scannedDirectory.directories[i].name) {
|
||||
directory = childDirectories[j];
|
||||
childDirectories.splice(j, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
await this.saveMedia(connection, scannedDirectory.directories[i].media);
|
||||
}
|
||||
}
|
||||
if (directory != null) { // update existing directory
|
||||
if (!directory.parent || !directory.parent.id) { // set parent if not set yet
|
||||
directory.parent = currentDir;
|
||||
delete directory.media;
|
||||
await directoryRepository.save(directory);
|
||||
}
|
||||
} else {
|
||||
scannedDirectory.directories[i].parent = currentDir;
|
||||
(<DirectoryEntity>scannedDirectory.directories[i]).lastScanned = null; // new child dir, not fully scanned yet
|
||||
const d = await directoryRepository.save(<DirectoryEntity>scannedDirectory.directories[i]);
|
||||
for (let j = 0; j < scannedDirectory.directories[i].media.length; j++) {
|
||||
scannedDirectory.directories[i].media[j].directory = d;
|
||||
}
|
||||
|
||||
// Remove child Dirs that are not anymore in the parent dir
|
||||
await directoryRepository.remove(childDirectories);
|
||||
|
||||
// save media
|
||||
const indexedMedia = await mediaRepository.createQueryBuilder('media')
|
||||
.where('media.directory = :dir', {
|
||||
dir: currentDir.id
|
||||
}).getMany();
|
||||
|
||||
|
||||
const mediaToSave = [];
|
||||
for (let i = 0; i < scannedDirectory.media.length; i++) {
|
||||
let media: MediaDTO = null;
|
||||
for (let j = 0; j < indexedMedia.length; j++) {
|
||||
if (indexedMedia[j].name === scannedDirectory.media[i].name) {
|
||||
media = indexedMedia[j];
|
||||
indexedMedia.splice(j, 1);
|
||||
break;
|
||||
await this.saveMedia(connection, scannedDirectory.directories[i].media);
|
||||
}
|
||||
}
|
||||
if (media == null) { //not in DB yet
|
||||
scannedDirectory.media[i].directory = null;
|
||||
media = Utils.clone(scannedDirectory.media[i]);
|
||||
scannedDirectory.media[i].directory = scannedDirectory;
|
||||
media.directory = currentDir;
|
||||
mediaToSave.push(media);
|
||||
} else if (!Utils.equalsFilter(media.metadata, scannedDirectory.media[i].metadata)) {
|
||||
media.metadata = scannedDirectory.media[i].metadata;
|
||||
mediaToSave.push(media);
|
||||
}
|
||||
}
|
||||
await this.saveMedia(connection, mediaToSave);
|
||||
await mediaRepository.remove(indexedMedia);
|
||||
|
||||
// save files
|
||||
const indexedMetaFiles = await fileRepository.createQueryBuilder('file')
|
||||
.where('file.directory = :dir', {
|
||||
dir: currentDir.id
|
||||
}).getMany();
|
||||
// Remove child Dirs that are not anymore in the parent dir
|
||||
await directoryRepository.remove(childDirectories);
|
||||
|
||||
// save media
|
||||
const indexedMedia = await mediaRepository.createQueryBuilder('media')
|
||||
.where('media.directory = :dir', {
|
||||
dir: currentDir.id
|
||||
}).getMany();
|
||||
|
||||
|
||||
const metaFilesToSave = [];
|
||||
for (let i = 0; i < scannedDirectory.metaFile.length; i++) {
|
||||
let metaFile: FileDTO = null;
|
||||
for (let j = 0; j < indexedMetaFiles.length; j++) {
|
||||
if (indexedMetaFiles[j].name === scannedDirectory.metaFile[i].name) {
|
||||
metaFile = indexedMetaFiles[j];
|
||||
indexedMetaFiles.splice(j, 1);
|
||||
break;
|
||||
const mediaToSave = [];
|
||||
for (let i = 0; i < scannedDirectory.media.length; i++) {
|
||||
let media: MediaDTO = null;
|
||||
for (let j = 0; j < indexedMedia.length; j++) {
|
||||
if (indexedMedia[j].name === scannedDirectory.media[i].name) {
|
||||
media = indexedMedia[j];
|
||||
indexedMedia.splice(j, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (media == null) { // not in DB yet
|
||||
scannedDirectory.media[i].directory = null;
|
||||
media = Utils.clone(scannedDirectory.media[i]);
|
||||
scannedDirectory.media[i].directory = scannedDirectory;
|
||||
media.directory = currentDir;
|
||||
mediaToSave.push(media);
|
||||
} else if (!Utils.equalsFilter(media.metadata, scannedDirectory.media[i].metadata)) {
|
||||
media.metadata = scannedDirectory.media[i].metadata;
|
||||
mediaToSave.push(media);
|
||||
}
|
||||
}
|
||||
if (metaFile == null) { //not in DB yet
|
||||
scannedDirectory.metaFile[i].directory = null;
|
||||
metaFile = Utils.clone(scannedDirectory.metaFile[i]);
|
||||
scannedDirectory.metaFile[i].directory = scannedDirectory;
|
||||
metaFile.directory = currentDir;
|
||||
metaFilesToSave.push(metaFile);
|
||||
await this.saveMedia(connection, mediaToSave);
|
||||
await mediaRepository.remove(indexedMedia);
|
||||
|
||||
// save files
|
||||
const indexedMetaFiles = await fileRepository.createQueryBuilder('file')
|
||||
.where('file.directory = :dir', {
|
||||
dir: currentDir.id
|
||||
}).getMany();
|
||||
|
||||
|
||||
const metaFilesToSave = [];
|
||||
for (let i = 0; i < scannedDirectory.metaFile.length; i++) {
|
||||
let metaFile: FileDTO = null;
|
||||
for (let j = 0; j < indexedMetaFiles.length; j++) {
|
||||
if (indexedMetaFiles[j].name === scannedDirectory.metaFile[i].name) {
|
||||
metaFile = indexedMetaFiles[j];
|
||||
indexedMetaFiles.splice(j, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (metaFile == null) { // not in DB yet
|
||||
scannedDirectory.metaFile[i].directory = null;
|
||||
metaFile = Utils.clone(scannedDirectory.metaFile[i]);
|
||||
scannedDirectory.metaFile[i].directory = scannedDirectory;
|
||||
metaFile.directory = currentDir;
|
||||
metaFilesToSave.push(metaFile);
|
||||
}
|
||||
}
|
||||
await fileRepository.save(metaFilesToSave);
|
||||
await fileRepository.remove(indexedMetaFiles);
|
||||
}catch (e){
|
||||
throw e;
|
||||
}finally {
|
||||
this.isSaving = false;
|
||||
}
|
||||
await fileRepository.save(metaFilesToSave);
|
||||
await fileRepository.remove(indexedMetaFiles);
|
||||
}
|
||||
|
||||
protected async saveMedia(connection: Connection, mediaList: MediaDTO[]): Promise<MediaEntity[]> {
|
||||
|
@ -26,11 +26,9 @@ export class SQLConnection {
|
||||
private static connection: Connection = null;
|
||||
|
||||
public static async getConnection(): Promise<Connection> {
|
||||
|
||||
if (this.connection == null) {
|
||||
|
||||
const options: any = this.getDriver(Config.Server.database);
|
||||
options.name = 'main';
|
||||
// options.name = 'main';
|
||||
options.entities = [
|
||||
UserEntity,
|
||||
FileEntity,
|
||||
@ -47,7 +45,6 @@ export class SQLConnection {
|
||||
await SQLConnection.schemeSync(this.connection);
|
||||
}
|
||||
return this.connection;
|
||||
|
||||
}
|
||||
|
||||
public static async tryConnection(config: DataBaseConfig) {
|
||||
|
@ -1,9 +1,10 @@
|
||||
import {Column, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn} from 'typeorm';
|
||||
import {Column, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn, Unique} from 'typeorm';
|
||||
import {DirectoryDTO} from '../../../../common/entities/DirectoryDTO';
|
||||
import {MediaEntity} from './MediaEntity';
|
||||
import {FileEntity} from './FileEntity';
|
||||
|
||||
@Entity()
|
||||
@Unique(['name', 'path'])
|
||||
export class DirectoryEntity implements DirectoryDTO {
|
||||
|
||||
@PrimaryGeneratedColumn()
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {Column, Entity, ManyToOne, PrimaryGeneratedColumn, TableInheritance} from 'typeorm';
|
||||
import {Column, Entity, ManyToOne, PrimaryGeneratedColumn, TableInheritance, Unique} from 'typeorm';
|
||||
import {DirectoryEntity} from './DirectoryEntity';
|
||||
import {MediaDimension, MediaDTO, MediaMetadata} from '../../../../common/entities/MediaDTO';
|
||||
import {OrientationTypes} from 'ts-exif-parser';
|
||||
@ -51,6 +51,7 @@ export class MediaMetadataEntity implements MediaMetadata {
|
||||
|
||||
// TODO: fix inheritance once its working in typeorm
|
||||
@Entity()
|
||||
@Unique(['name', 'directory'])
|
||||
@TableInheritance({column: {type: 'varchar', name: 'type'}})
|
||||
export abstract class MediaEntity implements MediaDTO {
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {Column, Entity, ChildEntity} from 'typeorm';
|
||||
import {Column, Entity, ChildEntity, Unique} from 'typeorm';
|
||||
import {CameraMetadata, GPSMetadata, PhotoDTO, PhotoMetadata, PositionMetaData} from '../../../../common/entities/PhotoDTO';
|
||||
import {OrientationTypes} from 'ts-exif-parser';
|
||||
import {MediaEntity, MediaMetadataEntity} from './MediaEntity';
|
||||
|
@ -162,10 +162,6 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit {
|
||||
public nextImage() {
|
||||
if (this.activePhotoId + 1 < this.gridPhotoQL.length) {
|
||||
this.navigateToPhoto(this.activePhotoId + 1);
|
||||
/*if (this.activePhotoId + 3 >= this.gridPhotoQL.length) {
|
||||
this.onLastElement.emit({}); // trigger to render more photos if there are
|
||||
}*/
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@ -173,14 +169,13 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit {
|
||||
this.pause();
|
||||
if (this.activePhotoId > 0) {
|
||||
this.navigateToPhoto(this.activePhotoId - 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private navigateToPhoto(photoIndex: number) {
|
||||
this.router.navigate([],
|
||||
{queryParams: this.queryService.getParams(this.gridPhotoQL.toArray()[photoIndex].gridPhoto.media)});
|
||||
{queryParams: this.queryService.getParams(this.gridPhotoQL.toArray()[photoIndex].gridPhoto.media)}).catch(console.error);
|
||||
}
|
||||
|
||||
private showPhoto(photoIndex: number, resize: boolean = true) {
|
||||
@ -235,6 +230,11 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit {
|
||||
this.prevImage();
|
||||
}
|
||||
break;
|
||||
case 'ArrowRight':
|
||||
if (this.activePhotoId < this.gridPhotoQL.length - 1) {
|
||||
this.nextImage();
|
||||
}
|
||||
break;
|
||||
case 'i':
|
||||
case 'I':
|
||||
if (this.isInfoPanelAnimating()) {
|
||||
@ -258,11 +258,6 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit {
|
||||
case 'C':
|
||||
this.controllersAlwaysOn = !this.controllersAlwaysOn;
|
||||
break;
|
||||
case 'ArrowRight':
|
||||
if (this.activePhotoId < this.gridPhotoQL.length - 1) {
|
||||
this.nextImage();
|
||||
}
|
||||
break;
|
||||
case 'Escape': // escape
|
||||
this.hide();
|
||||
break;
|
||||
@ -276,7 +271,7 @@ export class GalleryLightboxComponent implements OnDestroy, OnInit {
|
||||
|
||||
public hide() {
|
||||
this.router.navigate([],
|
||||
{queryParams: this.queryService.getParams()});
|
||||
{queryParams: this.queryService.getParams()}).catch(console.error);
|
||||
}
|
||||
|
||||
private hideLigthbox() {
|
||||
|
38
package.json
38
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "pigallery2",
|
||||
"version": "1.5.0",
|
||||
"version": "1.5.5",
|
||||
"description": "This is a photo gallery optimised for running low resource servers (especially on raspberry pi)",
|
||||
"author": "Patrik J. Braun",
|
||||
"homepage": "https://github.com/bpatrik/PiGallery2",
|
||||
@ -46,20 +46,20 @@
|
||||
"winston": "2.4.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "0.11.0",
|
||||
"@angular-devkit/build-optimizer": "0.11.0",
|
||||
"@angular/animations": "7.1.1",
|
||||
"@angular/cli": "7.1.0",
|
||||
"@angular/common": "7.1.1",
|
||||
"@angular/compiler": "7.1.1",
|
||||
"@angular/compiler-cli": "7.1.1",
|
||||
"@angular/core": "7.1.1",
|
||||
"@angular/forms": "7.1.1",
|
||||
"@angular/http": "7.1.1",
|
||||
"@angular/language-service": "7.1.1",
|
||||
"@angular/platform-browser": "7.1.1",
|
||||
"@angular/platform-browser-dynamic": "7.1.1",
|
||||
"@angular/router": "7.1.1",
|
||||
"@angular-devkit/build-angular": "0.11.2",
|
||||
"@angular-devkit/build-optimizer": "0.11.2",
|
||||
"@angular/animations": "7.1.2",
|
||||
"@angular/cli": "7.1.2",
|
||||
"@angular/common": "7.1.2",
|
||||
"@angular/compiler": "7.1.2",
|
||||
"@angular/compiler-cli": "7.1.2",
|
||||
"@angular/core": "7.1.2",
|
||||
"@angular/forms": "7.1.2",
|
||||
"@angular/http": "7.1.2",
|
||||
"@angular/language-service": "7.1.2",
|
||||
"@angular/platform-browser": "7.1.2",
|
||||
"@angular/platform-browser-dynamic": "7.1.2",
|
||||
"@angular/router": "7.1.2",
|
||||
"@ngx-translate/i18n-polyfill": "1.0.0",
|
||||
"@types/bcryptjs": "2.4.2",
|
||||
"@types/chai": "4.1.7",
|
||||
@ -70,18 +70,18 @@
|
||||
"@types/fluent-ffmpeg": "2.1.8",
|
||||
"@types/gm": "1.18.2",
|
||||
"@types/jasmine": "3.3.0",
|
||||
"@types/node": "10.12.11",
|
||||
"@types/node": "10.12.12",
|
||||
"@types/sharp": "0.21.0",
|
||||
"@types/winston": "2.3.9",
|
||||
"@yaga/leaflet-ng2": "^1.0.0",
|
||||
"bootstrap": "4.1.3",
|
||||
"chai": "4.2.0",
|
||||
"codelyzer": "4.5.0",
|
||||
"core-js": "2.5.7",
|
||||
"core-js": "2.6.0",
|
||||
"ejs-loader": "0.3.1",
|
||||
"gulp": "3.9.1",
|
||||
"gulp-json-modify": "1.0.2",
|
||||
"gulp-typescript": "5.0.0-alpha.3",
|
||||
"gulp-typescript": "5.0.0",
|
||||
"gulp-zip": "4.2.0",
|
||||
"hammerjs": "2.0.8",
|
||||
"intl": "1.2.5",
|
||||
@ -126,7 +126,7 @@
|
||||
"bcrypt": "3.0.2",
|
||||
"gm": "1.23.1",
|
||||
"mysql": "2.16.0",
|
||||
"sharp": "0.21.0"
|
||||
"sharp": "0.21.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6.9 <11.0"
|
||||
|
@ -12,6 +12,8 @@ import {DirectoryEntity} from '../../../../../backend/model/sql/enitites/Directo
|
||||
import {Utils} from '../../../../../common/Utils';
|
||||
import {MediaDTO} from '../../../../../common/entities/MediaDTO';
|
||||
import {FileDTO} from '../../../../../common/entities/FileDTO';
|
||||
import {PhotoEntity} from '../../../../../backend/model/sql/enitites/PhotoEntity';
|
||||
import {FileEntity} from '../../../../../backend/model/sql/enitites/FileEntity';
|
||||
|
||||
|
||||
class GalleryManagerTest extends GalleryManager {
|
||||
@ -28,6 +30,10 @@ class GalleryManagerTest extends GalleryManager {
|
||||
public async saveToDB(scannedDirectory: DirectoryDTO) {
|
||||
return super.saveToDB(scannedDirectory);
|
||||
}
|
||||
|
||||
public async queueForSave(scannedDirectory: DirectoryDTO): Promise<void> {
|
||||
return super.queueForSave(scannedDirectory);
|
||||
}
|
||||
}
|
||||
|
||||
describe('GalleryManager', () => {
|
||||
@ -171,7 +177,41 @@ describe('GalleryManager', () => {
|
||||
// selected.directories[0].parent = selected;
|
||||
expect(Utils.clone(Utils.removeNullOrEmptyObj(selected)))
|
||||
.to.deep.equal(Utils.clone(Utils.removeNullOrEmptyObj(subDir)));
|
||||
});
|
||||
|
||||
it('should avoid race condition', async () => {
|
||||
const conn = await SQLConnection.getConnection();
|
||||
const gm = new GalleryManagerTest();
|
||||
Config.Client.MetaFile.enabled = true;
|
||||
const parent = TestHelper.getRandomizedDirectoryEntry();
|
||||
const p1 = TestHelper.getRandomizedPhotoEntry(parent, 'Photo1');
|
||||
const p2 = TestHelper.getRandomizedPhotoEntry(parent, 'Photo2');
|
||||
const gpx = TestHelper.getRandomizedGPXEntry(parent, 'GPX1');
|
||||
const subDir = TestHelper.getRandomizedDirectoryEntry(parent, 'subDir');
|
||||
const sp1 = TestHelper.getRandomizedPhotoEntry(subDir, 'subPhoto1');
|
||||
const sp2 = TestHelper.getRandomizedPhotoEntry(subDir, 'subPhoto2');
|
||||
|
||||
|
||||
DirectoryDTO.removeReferences(parent);
|
||||
const s1 = gm.queueForSave(Utils.clone(parent));
|
||||
const s2 = gm.queueForSave(Utils.clone(parent));
|
||||
const s3 = gm.queueForSave(Utils.clone(parent));
|
||||
|
||||
await Promise.all([s1, s2, s3]);
|
||||
|
||||
const selected = await gm.selectParentDir(conn, parent.name, parent.path);
|
||||
await gm.fillParentDir(conn, selected);
|
||||
|
||||
const query = conn.getRepository(FileEntity).createQueryBuilder('photo');
|
||||
query.innerJoinAndSelect('photo.directory', 'directory');
|
||||
console.log((await query.getMany()));
|
||||
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)));
|
||||
});
|
||||
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user