mirror of
https://github.com/bpatrik/pigallery2.git
synced 2024-12-23 01:27:14 +02:00
remove threading #641
This commit is contained in:
parent
4dc431fe22
commit
2b0d1a96a6
@ -1,6 +1,6 @@
|
|||||||
import {Config} from '../src/common/config/private/Config';
|
import {Config} from '../src/common/config/private/Config';
|
||||||
import {ObjectManagers} from '../src/backend/model/ObjectManagers';
|
import {ObjectManagers} from '../src/backend/model/ObjectManagers';
|
||||||
import {DiskMangerWorker} from '../src/backend/model/threading/DiskMangerWorker';
|
import {DiskManager} from '../src/backend/model/fileaccess/DiskManager';
|
||||||
import {IndexingManager} from '../src/backend/model/database/IndexingManager';
|
import {IndexingManager} from '../src/backend/model/database/IndexingManager';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
@ -84,7 +84,7 @@ export class BenchmarkRunner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async bmSaveDirectory(): Promise<BenchmarkResult[]> {
|
async bmSaveDirectory(): Promise<BenchmarkResult[]> {
|
||||||
const dir = await DiskMangerWorker.scanDirectory(this.biggestDirPath);
|
const dir = await DiskManager.scanDirectory(this.biggestDirPath);
|
||||||
const bm = new Benchmark('Saving directory to DB', null,
|
const bm = new Benchmark('Saving directory to DB', null,
|
||||||
(): Promise<void> => this.resetDB(), null,
|
(): Promise<void> => this.resetDB(), null,
|
||||||
async (): Promise<void> => {
|
async (): Promise<void> => {
|
||||||
@ -110,7 +110,7 @@ export class BenchmarkRunner {
|
|||||||
});
|
});
|
||||||
bm.addAStep({
|
bm.addAStep({
|
||||||
name: 'Scanning directory',
|
name: 'Scanning directory',
|
||||||
fn: async (): Promise<ContentWrapper> => new ContentWrapper(await DiskMangerWorker.scanDirectory(this.biggestDirPath))
|
fn: async (): Promise<ContentWrapper> => new ContentWrapper(await DiskManager.scanDirectory(this.biggestDirPath))
|
||||||
});
|
});
|
||||||
return await bm.run(this.RUNS);
|
return await bm.run(this.RUNS);
|
||||||
}
|
}
|
||||||
@ -285,7 +285,6 @@ export class BenchmarkRunner {
|
|||||||
|
|
||||||
|
|
||||||
private resetDB = async (): Promise<void> => {
|
private resetDB = async (): Promise<void> => {
|
||||||
Config.Server.Threading.enabled = false;
|
|
||||||
await ObjectManagers.reset();
|
await ObjectManagers.reset();
|
||||||
await fs.promises.rm(ProjectPath.DBFolder, {recursive: true, force: true});
|
await fs.promises.rm(ProjectPath.DBFolder, {recursive: true, force: true});
|
||||||
Config.Database.type = DatabaseType.sqlite;
|
Config.Database.type = DatabaseType.sqlite;
|
||||||
@ -294,7 +293,6 @@ export class BenchmarkRunner {
|
|||||||
};
|
};
|
||||||
|
|
||||||
private async setupDB(): Promise<void> {
|
private async setupDB(): Promise<void> {
|
||||||
Config.Server.Threading.enabled = false;
|
|
||||||
await this.resetDB();
|
await this.resetDB();
|
||||||
await new Promise<void>((resolve, reject): void => {
|
await new Promise<void>((resolve, reject): void => {
|
||||||
try {
|
try {
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
import * as cluster from 'cluster';
|
|
||||||
import {Server} from './server';
|
import {Server} from './server';
|
||||||
import {Worker} from './model/threading/Worker';
|
|
||||||
import {ConfigDiagnostics} from './model/diagnostics/ConfigDiagnostics';
|
import {ConfigDiagnostics} from './model/diagnostics/ConfigDiagnostics';
|
||||||
|
|
||||||
|
|
||||||
@ -13,9 +11,5 @@ if ((process.argv || []).includes('--run-diagnostics')) {
|
|||||||
process.exit(0);
|
process.exit(0);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
if ((cluster as any).isMaster) {
|
new Server();
|
||||||
new Server();
|
|
||||||
} else {
|
|
||||||
Worker.process();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ import {Config} from '../../common/config/private/Config';
|
|||||||
import {UserDTOUtils} from '../../common/entities/UserDTO';
|
import {UserDTOUtils} from '../../common/entities/UserDTO';
|
||||||
import {MediaDTO, MediaDTOUtils} from '../../common/entities/MediaDTO';
|
import {MediaDTO, MediaDTOUtils} from '../../common/entities/MediaDTO';
|
||||||
import {QueryParams} from '../../common/QueryParams';
|
import {QueryParams} from '../../common/QueryParams';
|
||||||
import {VideoProcessing} from '../model/fileprocessing/VideoProcessing';
|
import {VideoProcessing} from '../model/fileaccess/fileprocessing/VideoProcessing';
|
||||||
import {SearchQueryDTO, SearchQueryTypes,} from '../../common/entities/SearchQueryDTO';
|
import {SearchQueryDTO, SearchQueryTypes,} from '../../common/entities/SearchQueryDTO';
|
||||||
import {LocationLookupException} from '../exceptions/LocationLookupException';
|
import {LocationLookupException} from '../exceptions/LocationLookupException';
|
||||||
import {SupportedFormats} from '../../common/SupportedFormats';
|
import {SupportedFormats} from '../../common/SupportedFormats';
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import {NextFunction, Request, Response} from 'express';
|
import {NextFunction, Request, Response} from 'express';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import {Config} from '../../common/config/private/Config';
|
import {Config} from '../../common/config/private/Config';
|
||||||
import {GPXProcessing} from '../model/fileprocessing/GPXProcessing';
|
import {GPXProcessing} from '../model/fileaccess/fileprocessing/GPXProcessing';
|
||||||
import {Logger} from '../Logger';
|
import {Logger} from '../Logger';
|
||||||
|
|
||||||
const LOG_TAG = 'MetaFileMWs';
|
const LOG_TAG = 'MetaFileMWs';
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import {NextFunction, Request, Response} from 'express';
|
import {NextFunction, Request, Response} from 'express';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import {PhotoProcessing} from '../../model/fileprocessing/PhotoProcessing';
|
import {PhotoProcessing} from '../../model/fileaccess/fileprocessing/PhotoProcessing';
|
||||||
import {Config} from '../../../common/config/private/Config';
|
import {Config} from '../../../common/config/private/Config';
|
||||||
import {ErrorCodes, ErrorDTO} from '../../../common/entities/Error';
|
import {ErrorCodes, ErrorDTO} from '../../../common/entities/Error';
|
||||||
|
|
||||||
|
@ -6,9 +6,9 @@ import {ContentWrapper} from '../../../common/entities/ConentWrapper';
|
|||||||
import {DirectoryPathDTO, ParentDirectoryDTO, SubDirectoryDTO,} from '../../../common/entities/DirectoryDTO';
|
import {DirectoryPathDTO, ParentDirectoryDTO, SubDirectoryDTO,} from '../../../common/entities/DirectoryDTO';
|
||||||
import {ProjectPath} from '../../ProjectPath';
|
import {ProjectPath} from '../../ProjectPath';
|
||||||
import {Config} from '../../../common/config/private/Config';
|
import {Config} from '../../../common/config/private/Config';
|
||||||
import {ThumbnailSourceType} from '../../model/threading/PhotoWorker';
|
import {ThumbnailSourceType} from '../../model/fileaccess/PhotoWorker';
|
||||||
import {MediaDTO} from '../../../common/entities/MediaDTO';
|
import {MediaDTO} from '../../../common/entities/MediaDTO';
|
||||||
import {PhotoProcessing} from '../../model/fileprocessing/PhotoProcessing';
|
import {PhotoProcessing} from '../../model/fileaccess/fileprocessing/PhotoProcessing';
|
||||||
import {ServerTime} from '../ServerTimingMWs';
|
import {ServerTime} from '../ServerTimingMWs';
|
||||||
import {PersonEntry} from '../../model/database/enitites/PersonEntry';
|
import {PersonEntry} from '../../model/database/enitites/PersonEntry';
|
||||||
|
|
||||||
|
@ -1,51 +0,0 @@
|
|||||||
import {ParentDirectoryDTO,} from '../../common/entities/DirectoryDTO';
|
|
||||||
import {Logger} from '../Logger';
|
|
||||||
import {Config} from '../../common/config/private/Config';
|
|
||||||
import {DiskManagerTH} from './threading/ThreadPool';
|
|
||||||
import {DirectoryScanSettings, DiskMangerWorker,} from './threading/DiskMangerWorker';
|
|
||||||
import {FileDTO} from '../../common/entities/FileDTO';
|
|
||||||
|
|
||||||
const LOG_TAG = '[DiskManager]';
|
|
||||||
|
|
||||||
export class DiskManager {
|
|
||||||
static threadPool: DiskManagerTH = null;
|
|
||||||
|
|
||||||
public static init(): void {
|
|
||||||
if (Config.Server.Threading.enabled === true) {
|
|
||||||
DiskManager.threadPool = new DiskManagerTH(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* List all files in a folder as fast as possible
|
|
||||||
*/
|
|
||||||
public static async scanDirectoryNoMetadata(
|
|
||||||
relativeDirectoryName: string,
|
|
||||||
settings: DirectoryScanSettings = {}
|
|
||||||
): Promise<ParentDirectoryDTO<FileDTO>> {
|
|
||||||
settings.noMetadata = true;
|
|
||||||
return this.scanDirectory(relativeDirectoryName, settings);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async scanDirectory(
|
|
||||||
relativeDirectoryName: string,
|
|
||||||
settings: DirectoryScanSettings = {}
|
|
||||||
): Promise<ParentDirectoryDTO> {
|
|
||||||
Logger.silly(LOG_TAG, 'scanning directory:', relativeDirectoryName);
|
|
||||||
|
|
||||||
let directory: ParentDirectoryDTO;
|
|
||||||
|
|
||||||
if (Config.Server.Threading.enabled === true) {
|
|
||||||
directory = await DiskManager.threadPool.execute(
|
|
||||||
relativeDirectoryName,
|
|
||||||
settings
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
directory = (await DiskMangerWorker.scanDirectory(
|
|
||||||
relativeDirectoryName,
|
|
||||||
settings
|
|
||||||
)) as ParentDirectoryDTO;
|
|
||||||
}
|
|
||||||
return directory;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +1,7 @@
|
|||||||
import {Config} from '../../../common/config/private/Config';
|
import {Config} from '../../../common/config/private/Config';
|
||||||
import {Brackets, SelectQueryBuilder, WhereExpression} from 'typeorm';
|
import {Brackets, SelectQueryBuilder, WhereExpression} from 'typeorm';
|
||||||
import {MediaEntity} from './enitites/MediaEntity';
|
import {MediaEntity} from './enitites/MediaEntity';
|
||||||
import {DiskMangerWorker} from '../threading/DiskMangerWorker';
|
import {DiskManager} from '../fileaccess/DiskManager';
|
||||||
import {ObjectManagers} from '../ObjectManagers';
|
import {ObjectManagers} from '../ObjectManagers';
|
||||||
import {DatabaseType} from '../../../common/config/private/PrivateConfig';
|
import {DatabaseType} from '../../../common/config/private/PrivateConfig';
|
||||||
import {SQLConnection} from './SQLConnection';
|
import {SQLConnection} from './SQLConnection';
|
||||||
@ -37,7 +37,7 @@ export class CoverManager implements IObjectManager {
|
|||||||
|
|
||||||
public async onNewDataVersion(changedDir: ParentDirectoryDTO): Promise<void> {
|
public async onNewDataVersion(changedDir: ParentDirectoryDTO): Promise<void> {
|
||||||
// Invalidating Album cover
|
// Invalidating Album cover
|
||||||
let fullPath = DiskMangerWorker.normalizeDirPath(
|
let fullPath = DiskManager.normalizeDirPath(
|
||||||
path.join(changedDir.path, changedDir.name)
|
path.join(changedDir.path, changedDir.name)
|
||||||
);
|
);
|
||||||
const query = (await SQLConnection.getConnection())
|
const query = (await SQLConnection.getConnection())
|
||||||
@ -46,10 +46,10 @@ export class CoverManager implements IObjectManager {
|
|||||||
.set({validCover: false});
|
.set({validCover: false});
|
||||||
|
|
||||||
let i = 0;
|
let i = 0;
|
||||||
const root = DiskMangerWorker.pathFromRelativeDirName('.');
|
const root = DiskManager.pathFromRelativeDirName('.');
|
||||||
while (fullPath !== root) {
|
while (fullPath !== root) {
|
||||||
const name = DiskMangerWorker.dirName(fullPath);
|
const name = DiskManager.dirName(fullPath);
|
||||||
const parentPath = DiskMangerWorker.pathFromRelativeDirName(fullPath);
|
const parentPath = DiskManager.pathFromRelativeDirName(fullPath);
|
||||||
fullPath = parentPath;
|
fullPath = parentPath;
|
||||||
++i;
|
++i;
|
||||||
query.orWhere(
|
query.orWhere(
|
||||||
@ -67,8 +67,8 @@ export class CoverManager implements IObjectManager {
|
|||||||
query.orWhere(
|
query.orWhere(
|
||||||
new Brackets((q: WhereExpression) => {
|
new Brackets((q: WhereExpression) => {
|
||||||
const param: { [key: string]: string } = {};
|
const param: { [key: string]: string } = {};
|
||||||
param['name' + i] = DiskMangerWorker.dirName('.');
|
param['name' + i] = DiskManager.dirName('.');
|
||||||
param['path' + i] = DiskMangerWorker.pathFromRelativeDirName('.');
|
param['path' + i] = DiskManager.pathFromRelativeDirName('.');
|
||||||
q.where(`path = :path${i}`, param);
|
q.where(`path = :path${i}`, param);
|
||||||
q.andWhere(`name = :name${i}`, param);
|
q.andWhere(`name = :name${i}`, param);
|
||||||
})
|
})
|
||||||
@ -157,11 +157,11 @@ export class CoverManager implements IObjectManager {
|
|||||||
});
|
});
|
||||||
if (Config.Database.type === DatabaseType.mysql) {
|
if (Config.Database.type === DatabaseType.mysql) {
|
||||||
q.orWhere('directory.path like :path || \'%\'', {
|
q.orWhere('directory.path like :path || \'%\'', {
|
||||||
path: DiskMangerWorker.pathFromParent(dir),
|
path: DiskManager.pathFromParent(dir),
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
q.orWhere('directory.path GLOB :path', {
|
q.orWhere('directory.path GLOB :path', {
|
||||||
path: DiskMangerWorker.pathFromParent(dir)
|
path: DiskManager.pathFromParent(dir)
|
||||||
// glob escaping. see https://github.com/bpatrik/pigallery2/issues/621
|
// glob escaping. see https://github.com/bpatrik/pigallery2/issues/621
|
||||||
.replaceAll('[', '[[]') + '*',
|
.replaceAll('[', '[[]') + '*',
|
||||||
});
|
});
|
||||||
|
@ -9,11 +9,11 @@ import {Config} from '../../../common/config/private/Config';
|
|||||||
import {Connection} from 'typeorm';
|
import {Connection} from 'typeorm';
|
||||||
import {MediaEntity} from './enitites/MediaEntity';
|
import {MediaEntity} from './enitites/MediaEntity';
|
||||||
import {VideoEntity} from './enitites/VideoEntity';
|
import {VideoEntity} from './enitites/VideoEntity';
|
||||||
import {DiskMangerWorker} from '../threading/DiskMangerWorker';
|
|
||||||
import {Logger} from '../../Logger';
|
import {Logger} from '../../Logger';
|
||||||
import {ObjectManagers} from '../ObjectManagers';
|
import {ObjectManagers} from '../ObjectManagers';
|
||||||
import {DuplicatesDTO} from '../../../common/entities/DuplicatesDTO';
|
import {DuplicatesDTO} from '../../../common/entities/DuplicatesDTO';
|
||||||
import {ReIndexingSensitivity} from '../../../common/config/private/PrivateConfig';
|
import {ReIndexingSensitivity} from '../../../common/config/private/PrivateConfig';
|
||||||
|
import { DiskManager } from '../fileaccess/DiskManager';
|
||||||
|
|
||||||
const LOG_TAG = '[GalleryManager]';
|
const LOG_TAG = '[GalleryManager]';
|
||||||
|
|
||||||
@ -22,7 +22,7 @@ export class GalleryManager {
|
|||||||
name: string;
|
name: string;
|
||||||
parent: string;
|
parent: string;
|
||||||
} {
|
} {
|
||||||
relativeDirectoryName = DiskMangerWorker.normalizeDirPath(
|
relativeDirectoryName = DiskManager.normalizeDirPath(
|
||||||
relativeDirectoryName
|
relativeDirectoryName
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
@ -57,7 +57,7 @@ export class GalleryManager {
|
|||||||
const stat = fs.statSync(
|
const stat = fs.statSync(
|
||||||
path.join(ProjectPath.ImageFolder, relativeDirectoryName)
|
path.join(ProjectPath.ImageFolder, relativeDirectoryName)
|
||||||
);
|
);
|
||||||
const lastModified = DiskMangerWorker.calcLastModified(stat);
|
const lastModified = DiskManager.calcLastModified(stat);
|
||||||
|
|
||||||
// If it seems that the content did not change, do not work on it
|
// If it seems that the content did not change, do not work on it
|
||||||
if (
|
if (
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import {DirectoryBaseDTO, DirectoryDTOUtils, DirectoryPathDTO, ParentDirectoryDTO} from '../../../common/entities/DirectoryDTO';
|
import {DirectoryBaseDTO, DirectoryDTOUtils, DirectoryPathDTO, ParentDirectoryDTO} from '../../../common/entities/DirectoryDTO';
|
||||||
import {DirectoryEntity} from './enitites/DirectoryEntity';
|
import {DirectoryEntity} from './enitites/DirectoryEntity';
|
||||||
import {SQLConnection} from './SQLConnection';
|
import {SQLConnection} from './SQLConnection';
|
||||||
import {DiskManager} from '../DiskManger';
|
|
||||||
import {PhotoEntity, PhotoMetadataEntity} from './enitites/PhotoEntity';
|
import {PhotoEntity, PhotoMetadataEntity} from './enitites/PhotoEntity';
|
||||||
import {Utils} from '../../../common/Utils';
|
import {Utils} from '../../../common/Utils';
|
||||||
import {PhotoMetadata,} from '../../../common/entities/PhotoDTO';
|
import {PhotoMetadata,} from '../../../common/entities/PhotoDTO';
|
||||||
@ -13,7 +12,6 @@ import {FileEntity} from './enitites/FileEntity';
|
|||||||
import {FileDTO} from '../../../common/entities/FileDTO';
|
import {FileDTO} from '../../../common/entities/FileDTO';
|
||||||
import {NotificationManager} from '../NotifocationManager';
|
import {NotificationManager} from '../NotifocationManager';
|
||||||
import {ObjectManagers} from '../ObjectManagers';
|
import {ObjectManagers} from '../ObjectManagers';
|
||||||
import {DiskMangerWorker} from '../threading/DiskMangerWorker';
|
|
||||||
import {Logger} from '../../Logger';
|
import {Logger} from '../../Logger';
|
||||||
import {ServerPG2ConfMap, ServerSidePG2ConfAction,} from '../../../common/PG2ConfMap';
|
import {ServerPG2ConfMap, ServerSidePG2ConfAction,} from '../../../common/PG2ConfMap';
|
||||||
import {ProjectPath} from '../../ProjectPath';
|
import {ProjectPath} from '../../ProjectPath';
|
||||||
@ -24,6 +22,7 @@ import {PersonEntry} from './enitites/PersonEntry';
|
|||||||
import {PersonJunctionTable} from './enitites/PersonJunctionTable';
|
import {PersonJunctionTable} from './enitites/PersonJunctionTable';
|
||||||
import {MDFileEntity} from './enitites/MDFileEntity';
|
import {MDFileEntity} from './enitites/MDFileEntity';
|
||||||
import {MDFileDTO} from '../../../common/entities/MDFileDTO';
|
import {MDFileDTO} from '../../../common/entities/MDFileDTO';
|
||||||
|
import {DiskManager} from '../fileaccess/DiskManager';
|
||||||
|
|
||||||
const LOG_TAG = '[IndexingManager]';
|
const LOG_TAG = '[IndexingManager]';
|
||||||
|
|
||||||
@ -242,9 +241,9 @@ export class IndexingManager {
|
|||||||
.update(DirectoryEntity)
|
.update(DirectoryEntity)
|
||||||
.set({parent: currentDirId as unknown})
|
.set({parent: currentDirId as unknown})
|
||||||
.where('path = :path', {
|
.where('path = :path', {
|
||||||
path: DiskMangerWorker.pathFromParent(scannedDirectory),
|
path: DiskManager.pathFromParent(scannedDirectory),
|
||||||
})
|
})
|
||||||
.andWhere('name NOT LIKE :root', {root: DiskMangerWorker.dirName('.')})
|
.andWhere('name NOT LIKE :root', {root: DiskManager.dirName('.')})
|
||||||
.andWhere('parent IS NULL')
|
.andWhere('parent IS NULL')
|
||||||
.execute();
|
.execute();
|
||||||
|
|
||||||
|
@ -6,15 +6,18 @@ import {ProjectPath} from '../../ProjectPath';
|
|||||||
import {Config} from '../../../common/config/private/Config';
|
import {Config} from '../../../common/config/private/Config';
|
||||||
import {VideoDTO} from '../../../common/entities/VideoDTO';
|
import {VideoDTO} from '../../../common/entities/VideoDTO';
|
||||||
import {FileDTO} from '../../../common/entities/FileDTO';
|
import {FileDTO} from '../../../common/entities/FileDTO';
|
||||||
import {MetadataLoader} from './MetadataLoader';
|
|
||||||
import {Logger} from '../../Logger';
|
import {Logger} from '../../Logger';
|
||||||
import {VideoProcessing} from '../fileprocessing/VideoProcessing';
|
import {VideoProcessing} from './fileprocessing/VideoProcessing';
|
||||||
import {PhotoProcessing} from '../fileprocessing/PhotoProcessing';
|
import {PhotoProcessing} from './fileprocessing/PhotoProcessing';
|
||||||
import {Utils} from '../../../common/Utils';
|
import {Utils} from '../../../common/Utils';
|
||||||
import {GPXProcessing} from '../fileprocessing/GPXProcessing';
|
import {GPXProcessing} from './fileprocessing/GPXProcessing';
|
||||||
import {MDFileDTO} from '../../../common/entities/MDFileDTO';
|
import {MDFileDTO} from '../../../common/entities/MDFileDTO';
|
||||||
|
import {MetadataLoader} from './MetadataLoader';
|
||||||
|
|
||||||
export class DiskMangerWorker {
|
|
||||||
|
const LOG_TAG = '[DiskManager]';
|
||||||
|
|
||||||
|
export class DiskManager {
|
||||||
public static calcLastModified(stat: Stats): number {
|
public static calcLastModified(stat: Stats): number {
|
||||||
return Math.max(stat.ctime.getTime(), stat.mtime.getTime());
|
return Math.max(stat.ctime.getTime(), stat.mtime.getTime());
|
||||||
}
|
}
|
||||||
@ -25,15 +28,15 @@ export class DiskMangerWorker {
|
|||||||
|
|
||||||
public static pathFromRelativeDirName(relativeDirectoryName: string): string {
|
public static pathFromRelativeDirName(relativeDirectoryName: string): string {
|
||||||
return path.join(
|
return path.join(
|
||||||
path.dirname(this.normalizeDirPath(relativeDirectoryName)),
|
path.dirname(this.normalizeDirPath(relativeDirectoryName)),
|
||||||
path.sep
|
path.sep
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static pathFromParent(parent: { path: string; name: string }): string {
|
public static pathFromParent(parent: { path: string; name: string }): string {
|
||||||
return path.join(
|
return path.join(
|
||||||
this.normalizeDirPath(path.join(parent.path, parent.name)),
|
this.normalizeDirPath(path.join(parent.path, parent.name)),
|
||||||
path.sep
|
path.sep
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,13 +48,13 @@ export class DiskMangerWorker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static async excludeDir(
|
public static async excludeDir(
|
||||||
name: string,
|
name: string,
|
||||||
relativeDirectoryName: string,
|
relativeDirectoryName: string,
|
||||||
absoluteDirectoryName: string
|
absoluteDirectoryName: string
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
if (
|
if (
|
||||||
Config.Indexing.excludeFolderList.length === 0 &&
|
Config.Indexing.excludeFolderList.length === 0 &&
|
||||||
Config.Indexing.excludeFileList.length === 0
|
Config.Indexing.excludeFileList.length === 0
|
||||||
) {
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -87,30 +90,31 @@ export class DiskMangerWorker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static async scanDirectoryNoMetadata(
|
public static async scanDirectoryNoMetadata(
|
||||||
relativeDirectoryName: string,
|
relativeDirectoryName: string,
|
||||||
settings: DirectoryScanSettings = {}
|
settings: DirectoryScanSettings = {}
|
||||||
): Promise<ParentDirectoryDTO<FileDTO>> {
|
): Promise<ParentDirectoryDTO<FileDTO>> {
|
||||||
settings.noMetadata = true;
|
settings.noMetadata = true;
|
||||||
return (await this.scanDirectory(
|
return (await this.scanDirectory(
|
||||||
relativeDirectoryName,
|
relativeDirectoryName,
|
||||||
settings
|
settings
|
||||||
)) as ParentDirectoryDTO<FileDTO>;
|
)) as ParentDirectoryDTO<FileDTO>;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async scanDirectory(
|
public static async scanDirectory(
|
||||||
relativeDirectoryName: string,
|
relativeDirectoryName: string,
|
||||||
settings: DirectoryScanSettings = {}
|
settings: DirectoryScanSettings = {}
|
||||||
): Promise<ParentDirectoryDTO> {
|
): Promise<ParentDirectoryDTO> {
|
||||||
|
Logger.silly(LOG_TAG, 'scanning directory:', relativeDirectoryName);
|
||||||
relativeDirectoryName = this.normalizeDirPath(relativeDirectoryName);
|
relativeDirectoryName = this.normalizeDirPath(relativeDirectoryName);
|
||||||
const directoryName = DiskMangerWorker.dirName(relativeDirectoryName);
|
const directoryName = DiskManager.dirName(relativeDirectoryName);
|
||||||
const directoryParent = this.pathFromRelativeDirName(relativeDirectoryName);
|
const directoryParent = this.pathFromRelativeDirName(relativeDirectoryName);
|
||||||
const absoluteDirectoryName = path.join(
|
const absoluteDirectoryName = path.join(
|
||||||
ProjectPath.ImageFolder,
|
ProjectPath.ImageFolder,
|
||||||
relativeDirectoryName
|
relativeDirectoryName
|
||||||
);
|
);
|
||||||
|
|
||||||
const stat = await fsp.stat(
|
const stat = await fsp.stat(
|
||||||
path.join(ProjectPath.ImageFolder, relativeDirectoryName)
|
path.join(ProjectPath.ImageFolder, relativeDirectoryName)
|
||||||
);
|
);
|
||||||
const directory: ParentDirectoryDTO = {
|
const directory: ParentDirectoryDTO = {
|
||||||
id: null,
|
id: null,
|
||||||
@ -132,36 +136,36 @@ export class DiskMangerWorker {
|
|||||||
|
|
||||||
// nothing to scan, we are here for the empty dir
|
// nothing to scan, we are here for the empty dir
|
||||||
if (
|
if (
|
||||||
settings.noPhoto === true &&
|
settings.noPhoto === true &&
|
||||||
settings.noMetaFile === true &&
|
settings.noMetaFile === true &&
|
||||||
settings.noVideo === true
|
settings.noVideo === true
|
||||||
) {
|
) {
|
||||||
return directory;
|
return directory;
|
||||||
}
|
}
|
||||||
const list = await fsp.readdir(absoluteDirectoryName);
|
const list = await fsp.readdir(absoluteDirectoryName);
|
||||||
for (const file of list) {
|
for (const file of list) {
|
||||||
const fullFilePath = path.normalize(
|
const fullFilePath = path.normalize(
|
||||||
path.join(absoluteDirectoryName, file)
|
path.join(absoluteDirectoryName, file)
|
||||||
);
|
);
|
||||||
if ((await fsp.stat(fullFilePath)).isDirectory()) {
|
if ((await fsp.stat(fullFilePath)).isDirectory()) {
|
||||||
if (
|
if (
|
||||||
settings.noDirectory === true ||
|
settings.noDirectory === true ||
|
||||||
settings.coverOnly === true ||
|
settings.coverOnly === true ||
|
||||||
(await DiskMangerWorker.excludeDir(
|
(await DiskManager.excludeDir(
|
||||||
file,
|
file,
|
||||||
relativeDirectoryName,
|
relativeDirectoryName,
|
||||||
absoluteDirectoryName
|
absoluteDirectoryName
|
||||||
))
|
))
|
||||||
) {
|
) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// create cover directory
|
// create cover directory
|
||||||
const d = (await DiskMangerWorker.scanDirectory(
|
const d = (await DiskManager.scanDirectory(
|
||||||
path.join(relativeDirectoryName, file),
|
path.join(relativeDirectoryName, file),
|
||||||
{
|
{
|
||||||
coverOnly: true,
|
coverOnly: true,
|
||||||
}
|
}
|
||||||
)) as SubDirectoryDTO;
|
)) as SubDirectoryDTO;
|
||||||
|
|
||||||
directory.directories.push(d);
|
directory.directories.push(d);
|
||||||
@ -174,9 +178,9 @@ export class DiskMangerWorker {
|
|||||||
name: file,
|
name: file,
|
||||||
directory: null,
|
directory: null,
|
||||||
metadata:
|
metadata:
|
||||||
settings.noMetadata === true
|
settings.noMetadata === true
|
||||||
? null
|
? null
|
||||||
: await MetadataLoader.loadPhotoMetadata(fullFilePath),
|
: await MetadataLoader.loadPhotoMetadata(fullFilePath),
|
||||||
} as PhotoDTO;
|
} as PhotoDTO;
|
||||||
|
|
||||||
if (!directory.cover) {
|
if (!directory.cover) {
|
||||||
@ -197,9 +201,9 @@ export class DiskMangerWorker {
|
|||||||
}
|
}
|
||||||
} else if (VideoProcessing.isVideo(fullFilePath)) {
|
} else if (VideoProcessing.isVideo(fullFilePath)) {
|
||||||
if (
|
if (
|
||||||
Config.Media.Video.enabled === false ||
|
Config.Media.Video.enabled === false ||
|
||||||
settings.noVideo === true ||
|
settings.noVideo === true ||
|
||||||
settings.coverOnly === true
|
settings.coverOnly === true
|
||||||
) {
|
) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -208,23 +212,23 @@ export class DiskMangerWorker {
|
|||||||
name: file,
|
name: file,
|
||||||
directory: null,
|
directory: null,
|
||||||
metadata:
|
metadata:
|
||||||
settings.noMetadata === true
|
settings.noMetadata === true
|
||||||
? null
|
? null
|
||||||
: await MetadataLoader.loadVideoMetadata(fullFilePath),
|
: await MetadataLoader.loadVideoMetadata(fullFilePath),
|
||||||
} as VideoDTO);
|
} as VideoDTO);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Logger.warn(
|
Logger.warn(
|
||||||
'Media loading error, skipping: ' +
|
'Media loading error, skipping: ' +
|
||||||
file +
|
file +
|
||||||
', reason: ' +
|
', reason: ' +
|
||||||
e.toString()
|
e.toString()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if (GPXProcessing.isMetaFile(fullFilePath)) {
|
} else if (GPXProcessing.isMetaFile(fullFilePath)) {
|
||||||
if (
|
if (
|
||||||
!DiskMangerWorker.isEnabledMetaFile(fullFilePath) ||
|
!DiskManager.isEnabledMetaFile(fullFilePath) ||
|
||||||
settings.noMetaFile === true ||
|
settings.noMetaFile === true ||
|
||||||
settings.coverOnly === true
|
settings.coverOnly === true
|
||||||
) {
|
) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -242,13 +246,13 @@ export class DiskMangerWorker {
|
|||||||
directory.oldestMedia = Number.MIN_SAFE_INTEGER;
|
directory.oldestMedia = Number.MIN_SAFE_INTEGER;
|
||||||
|
|
||||||
directory.media.forEach((m) => {
|
directory.media.forEach((m) => {
|
||||||
directory.youngestMedia = Math.min(m.metadata.creationDate, directory.youngestMedia);
|
directory.youngestMedia = Math.min(m.metadata.creationDate, directory.youngestMedia);
|
||||||
directory.oldestMedia = Math.max(m.metadata.creationDate, directory.oldestMedia);
|
directory.oldestMedia = Math.max(m.metadata.creationDate, directory.oldestMedia);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
directory.metaFile.forEach(mf => {
|
directory.metaFile.forEach(mf => {
|
||||||
if (DiskMangerWorker.isMarkdown(mf.name)) {
|
if (DiskManager.isMarkdown(mf.name)) {
|
||||||
(mf as MDFileDTO).date = directory.youngestMedia;
|
(mf as MDFileDTO).date = directory.youngestMedia;
|
||||||
}
|
}
|
||||||
});
|
});
|
@ -1,9 +1,9 @@
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import {constants as fsConstants, promises as fsp} from 'fs';
|
import {constants as fsConstants, promises as fsp} from 'fs';
|
||||||
import * as xml2js from 'xml2js';
|
import * as xml2js from 'xml2js';
|
||||||
import {ProjectPath} from '../../ProjectPath';
|
import {ProjectPath} from '../../../ProjectPath';
|
||||||
import {Config} from '../../../common/config/private/Config';
|
import {Config} from '../../../../common/config/private/Config';
|
||||||
import {SupportedFormats} from '../../../common/SupportedFormats';
|
import {SupportedFormats} from '../../../../common/SupportedFormats';
|
||||||
|
|
||||||
type gpxEntry = { '$': { lat: string, lon: string }, ele?: string[], time?: string[], extensions?: unknown };
|
type gpxEntry = { '$': { lat: string, lon: string }, ele?: string[], time?: string[], extensions?: unknown };
|
||||||
|
|
@ -2,14 +2,14 @@ import * as path from 'path';
|
|||||||
import {constants as fsConstants, promises as fsp} from 'fs';
|
import {constants as fsConstants, promises as fsp} from 'fs';
|
||||||
import * as os from 'os';
|
import * as os from 'os';
|
||||||
import * as crypto from 'crypto';
|
import * as crypto from 'crypto';
|
||||||
import {ProjectPath} from '../../ProjectPath';
|
import {ProjectPath} from '../../../ProjectPath';
|
||||||
import {Config} from '../../../common/config/private/Config';
|
import {Config} from '../../../../common/config/private/Config';
|
||||||
import {MediaRendererInput, PhotoWorker, SvgRendererInput, ThumbnailSourceType,} from '../threading/PhotoWorker';
|
import {MediaRendererInput, PhotoWorker, SvgRendererInput, ThumbnailSourceType,} from '../PhotoWorker';
|
||||||
import {ITaskExecuter, TaskExecuter} from '../threading/TaskExecuter';
|
import {ITaskExecuter, TaskExecuter} from '../TaskExecuter';
|
||||||
import {FaceRegion, PhotoDTO} from '../../../common/entities/PhotoDTO';
|
import {FaceRegion, PhotoDTO} from '../../../../common/entities/PhotoDTO';
|
||||||
import {SupportedFormats} from '../../../common/SupportedFormats';
|
import {SupportedFormats} from '../../../../common/SupportedFormats';
|
||||||
import {PersonEntry} from '../database/enitites/PersonEntry';
|
import {PersonEntry} from '../../database/enitites/PersonEntry';
|
||||||
import {SVGIconConfig} from '../../../common/config/public/ClientConfig';
|
import {SVGIconConfig} from '../../../../common/config/public/ClientConfig';
|
||||||
|
|
||||||
export class PhotoProcessing {
|
export class PhotoProcessing {
|
||||||
private static initDone = false;
|
private static initDone = false;
|
||||||
@ -21,19 +21,10 @@ export class PhotoProcessing {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Config.Server.Threading.enabled === true) {
|
Config.Media.Thumbnail.concurrentThumbnailGenerations = Math.max(
|
||||||
if (Config.Server.Threading.thumbnailThreads > 0) {
|
1,
|
||||||
Config.Media.Thumbnail.concurrentThumbnailGenerations =
|
os.cpus().length - 1
|
||||||
Config.Server.Threading.thumbnailThreads;
|
);
|
||||||
} else {
|
|
||||||
Config.Media.Thumbnail.concurrentThumbnailGenerations = Math.max(
|
|
||||||
1,
|
|
||||||
os.cpus().length - 1
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Config.Media.Thumbnail.concurrentThumbnailGenerations = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.taskQue = new TaskExecuter(
|
this.taskQue = new TaskExecuter(
|
||||||
Config.Media.Thumbnail.concurrentThumbnailGenerations,
|
Config.Media.Thumbnail.concurrentThumbnailGenerations,
|
@ -1,41 +1,41 @@
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import {constants as fsConstants, promises as fsp} from 'fs';
|
import {constants as fsConstants, promises as fsp} from 'fs';
|
||||||
import {ITaskExecuter, TaskExecuter} from '../threading/TaskExecuter';
|
import {ITaskExecuter, TaskExecuter} from '../TaskExecuter';
|
||||||
import {VideoConverterInput, VideoConverterWorker,} from '../threading/VideoConverterWorker';
|
import {VideoConverterInput, VideoConverterWorker,} from '../VideoConverterWorker';
|
||||||
import {MetadataLoader} from '../threading/MetadataLoader';
|
import {MetadataLoader} from '../MetadataLoader';
|
||||||
import {Config} from '../../../common/config/private/Config';
|
import {Config} from '../../../../common/config/private/Config';
|
||||||
import {ProjectPath} from '../../ProjectPath';
|
import {ProjectPath} from '../../../ProjectPath';
|
||||||
import {SupportedFormats} from '../../../common/SupportedFormats';
|
import {SupportedFormats} from '../../../../common/SupportedFormats';
|
||||||
|
|
||||||
export class VideoProcessing {
|
export class VideoProcessing {
|
||||||
private static taskQue: ITaskExecuter<VideoConverterInput, void> =
|
private static taskQue: ITaskExecuter<VideoConverterInput, void> =
|
||||||
new TaskExecuter(
|
new TaskExecuter(
|
||||||
1,
|
1,
|
||||||
(input): Promise<void> => VideoConverterWorker.convert(input)
|
(input): Promise<void> => VideoConverterWorker.convert(input)
|
||||||
);
|
);
|
||||||
|
|
||||||
public static generateConvertedFilePath(videoPath: string): string {
|
public static generateConvertedFilePath(videoPath: string): string {
|
||||||
return path.join(
|
return path.join(
|
||||||
ProjectPath.TranscodedFolder,
|
ProjectPath.TranscodedFolder,
|
||||||
ProjectPath.getRelativePathToImages(path.dirname(videoPath)),
|
ProjectPath.getRelativePathToImages(path.dirname(videoPath)),
|
||||||
path.basename(videoPath) + '_' + this.getConvertedFilePostFix()
|
path.basename(videoPath) + '_' + this.getConvertedFilePostFix()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async isValidConvertedPath(
|
public static async isValidConvertedPath(
|
||||||
convertedPath: string
|
convertedPath: string
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
const origFilePath = path.join(
|
const origFilePath = path.join(
|
||||||
ProjectPath.ImageFolder,
|
ProjectPath.ImageFolder,
|
||||||
path.relative(
|
path.relative(
|
||||||
ProjectPath.TranscodedFolder,
|
ProjectPath.TranscodedFolder,
|
||||||
convertedPath.substring(0, convertedPath.lastIndexOf('_'))
|
convertedPath.substring(0, convertedPath.lastIndexOf('_'))
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
const postfix = convertedPath.substring(
|
const postfix = convertedPath.substring(
|
||||||
convertedPath.lastIndexOf('_') + 1,
|
convertedPath.lastIndexOf('_') + 1,
|
||||||
convertedPath.length
|
convertedPath.length
|
||||||
);
|
);
|
||||||
|
|
||||||
if (postfix !== this.getConvertedFilePostFix()) {
|
if (postfix !== this.getConvertedFilePostFix()) {
|
||||||
@ -82,8 +82,8 @@ export class VideoProcessing {
|
|||||||
output: {
|
output: {
|
||||||
path: outPath,
|
path: outPath,
|
||||||
codec: Config.Media.Video.transcoding.format === 'mp4' ?
|
codec: Config.Media.Video.transcoding.format === 'mp4' ?
|
||||||
Config.Media.Video.transcoding.mp4Codec :
|
Config.Media.Video.transcoding.mp4Codec :
|
||||||
Config.Media.Video.transcoding.webmCodec,
|
Config.Media.Video.transcoding.webmCodec,
|
||||||
format: Config.Media.Video.transcoding.format,
|
format: Config.Media.Video.transcoding.format,
|
||||||
crf: Config.Media.Video.transcoding.crf,
|
crf: Config.Media.Video.transcoding.crf,
|
||||||
preset: Config.Media.Video.transcoding.preset,
|
preset: Config.Media.Video.transcoding.preset,
|
||||||
@ -93,17 +93,17 @@ export class VideoProcessing {
|
|||||||
|
|
||||||
if (metaData.bitRate > Config.Media.Video.transcoding.bitRate) {
|
if (metaData.bitRate > Config.Media.Video.transcoding.bitRate) {
|
||||||
renderInput.output.bitRate =
|
renderInput.output.bitRate =
|
||||||
Config.Media.Video.transcoding.bitRate;
|
Config.Media.Video.transcoding.bitRate;
|
||||||
}
|
}
|
||||||
if (metaData.fps > Config.Media.Video.transcoding.fps) {
|
if (metaData.fps > Config.Media.Video.transcoding.fps) {
|
||||||
renderInput.output.fps = Config.Media.Video.transcoding.fps;
|
renderInput.output.fps = Config.Media.Video.transcoding.fps;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
Config.Media.Video.transcoding.resolution < metaData.size.height
|
Config.Media.Video.transcoding.resolution < metaData.size.height
|
||||||
) {
|
) {
|
||||||
renderInput.output.resolution =
|
renderInput.output.resolution =
|
||||||
Config.Media.Video.transcoding.resolution;
|
Config.Media.Video.transcoding.resolution;
|
||||||
}
|
}
|
||||||
|
|
||||||
const outDir = path.dirname(renderInput.output.path);
|
const outDir = path.dirname(renderInput.output.path);
|
||||||
@ -119,14 +119,14 @@ export class VideoProcessing {
|
|||||||
|
|
||||||
protected static getConvertedFilePostFix(): string {
|
protected static getConvertedFilePostFix(): string {
|
||||||
return (
|
return (
|
||||||
Math.round(Config.Media.Video.transcoding.bitRate / 1024) +
|
Math.round(Config.Media.Video.transcoding.bitRate / 1024) +
|
||||||
'k' +
|
'k' +
|
||||||
(Config.Media.Video.transcoding.format === 'mp4' ?
|
(Config.Media.Video.transcoding.format === 'mp4' ?
|
||||||
Config.Media.Video.transcoding.mp4Codec :
|
Config.Media.Video.transcoding.mp4Codec :
|
||||||
Config.Media.Video.transcoding.webmCodec).toString().toLowerCase() +
|
Config.Media.Video.transcoding.webmCodec).toString().toLowerCase() +
|
||||||
Config.Media.Video.transcoding.resolution +
|
Config.Media.Video.transcoding.resolution +
|
||||||
'.' +
|
'.' +
|
||||||
Config.Media.Video.transcoding.format.toLowerCase()
|
Config.Media.Video.transcoding.format.toLowerCase()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,8 +1,6 @@
|
|||||||
import {ConfigTemplateEntry} from '../../../../common/entities/job/JobDTO';
|
import {ConfigTemplateEntry} from '../../../../common/entities/job/JobDTO';
|
||||||
import {Job} from './Job';
|
import {Job} from './Job';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import {DiskManager} from '../../DiskManger';
|
|
||||||
import {DirectoryScanSettings} from '../../threading/DiskMangerWorker';
|
|
||||||
import {Logger} from '../../../Logger';
|
import {Logger} from '../../../Logger';
|
||||||
import {Config} from '../../../../common/config/private/Config';
|
import {Config} from '../../../../common/config/private/Config';
|
||||||
import {FileDTO} from '../../../../common/entities/FileDTO';
|
import {FileDTO} from '../../../../common/entities/FileDTO';
|
||||||
@ -14,6 +12,7 @@ import {backendTexts} from '../../../../common/BackendTexts';
|
|||||||
import {ProjectPath} from '../../../ProjectPath';
|
import {ProjectPath} from '../../../ProjectPath';
|
||||||
import {FileEntity} from '../../database/enitites/FileEntity';
|
import {FileEntity} from '../../database/enitites/FileEntity';
|
||||||
import {DirectoryBaseDTO, DirectoryDTOUtils} from '../../../../common/entities/DirectoryDTO';
|
import {DirectoryBaseDTO, DirectoryDTOUtils} from '../../../../common/entities/DirectoryDTO';
|
||||||
|
import {DirectoryScanSettings, DiskManager} from '../../fileaccess/DiskManager';
|
||||||
|
|
||||||
const LOG_TAG = '[FileJob]';
|
const LOG_TAG = '[FileJob]';
|
||||||
|
|
||||||
@ -69,10 +68,10 @@ export abstract class FileJob<S extends { indexedOnly?: boolean } = { indexedOnl
|
|||||||
|
|
||||||
protected async step(): Promise<boolean> {
|
protected async step(): Promise<boolean> {
|
||||||
if (
|
if (
|
||||||
this.fileQueue.length === 0 &&
|
this.fileQueue.length === 0 &&
|
||||||
((this.directoryQueue.length === 0 && !this.config.indexedOnly) ||
|
((this.directoryQueue.length === 0 && !this.config.indexedOnly) ||
|
||||||
(this.config.indexedOnly &&
|
(this.config.indexedOnly &&
|
||||||
this.DBProcessing.hasMoreMedia === false))) {
|
this.DBProcessing.hasMoreMedia === false))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,11 +112,11 @@ export abstract class FileJob<S extends { indexedOnly?: boolean } = { indexedOnl
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
Logger.error(
|
Logger.error(
|
||||||
LOG_TAG,
|
LOG_TAG,
|
||||||
'Error during processing file:' + filePath + ', ' + e.toString()
|
'Error during processing file:' + filePath + ', ' + e.toString()
|
||||||
);
|
);
|
||||||
this.Progress.log(
|
this.Progress.log(
|
||||||
'Error during processing file:' + filePath + ', ' + e.toString()
|
'Error during processing file:' + filePath + ', ' + e.toString()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,8 +127,8 @@ export abstract class FileJob<S extends { indexedOnly?: boolean } = { indexedOnl
|
|||||||
const directory = this.directoryQueue.shift();
|
const directory = this.directoryQueue.shift();
|
||||||
this.Progress.log('scanning directory: ' + directory);
|
this.Progress.log('scanning directory: ' + directory);
|
||||||
const scanned = await DiskManager.scanDirectoryNoMetadata(
|
const scanned = await DiskManager.scanDirectoryNoMetadata(
|
||||||
directory,
|
directory,
|
||||||
this.scanFilter
|
this.scanFilter
|
||||||
);
|
);
|
||||||
for (const item of scanned.directories) {
|
for (const item of scanned.directories) {
|
||||||
this.directoryQueue.push(path.join(item.path, item.name));
|
this.directoryQueue.push(path.join(item.path, item.name));
|
||||||
@ -144,12 +143,12 @@ export abstract class FileJob<S extends { indexedOnly?: boolean } = { indexedOnl
|
|||||||
}
|
}
|
||||||
for (const item of scannedAndFiltered) {
|
for (const item of scannedAndFiltered) {
|
||||||
this.fileQueue.push(
|
this.fileQueue.push(
|
||||||
path.join(
|
path.join(
|
||||||
ProjectPath.ImageFolder,
|
ProjectPath.ImageFolder,
|
||||||
item.directory.path,
|
item.directory.path,
|
||||||
item.directory.name,
|
item.directory.name,
|
||||||
item.name
|
item.name
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -162,12 +161,12 @@ export abstract class FileJob<S extends { indexedOnly?: boolean } = { indexedOnl
|
|||||||
}
|
}
|
||||||
for (const item of scannedAndFiltered) {
|
for (const item of scannedAndFiltered) {
|
||||||
this.fileQueue.push(
|
this.fileQueue.push(
|
||||||
path.join(
|
path.join(
|
||||||
ProjectPath.ImageFolder,
|
ProjectPath.ImageFolder,
|
||||||
item.directory.path,
|
item.directory.path,
|
||||||
item.directory.name,
|
item.directory.name,
|
||||||
item.name
|
item.name
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -175,8 +174,8 @@ export abstract class FileJob<S extends { indexedOnly?: boolean } = { indexedOnl
|
|||||||
|
|
||||||
private async loadMediaFilesFromDB(): Promise<void> {
|
private async loadMediaFilesFromDB(): Promise<void> {
|
||||||
if (this.scanFilter.noVideo === true &&
|
if (this.scanFilter.noVideo === true &&
|
||||||
this.scanFilter.noPhoto === true &&
|
this.scanFilter.noPhoto === true &&
|
||||||
this.scanFilter.noMetaFile === true) {
|
this.scanFilter.noMetaFile === true) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,7 +189,7 @@ export abstract class FileJob<S extends { indexedOnly?: boolean } = { indexedOnl
|
|||||||
};
|
};
|
||||||
const connection = await SQLConnection.getConnection();
|
const connection = await SQLConnection.getConnection();
|
||||||
if (!this.scanFilter.noVideo ||
|
if (!this.scanFilter.noVideo ||
|
||||||
!this.scanFilter.noPhoto) {
|
!this.scanFilter.noPhoto) {
|
||||||
|
|
||||||
let usedEntity = MediaEntity;
|
let usedEntity = MediaEntity;
|
||||||
|
|
||||||
@ -201,13 +200,13 @@ export abstract class FileJob<S extends { indexedOnly?: boolean } = { indexedOnl
|
|||||||
}
|
}
|
||||||
|
|
||||||
const result = await connection
|
const result = await connection
|
||||||
.getRepository(usedEntity)
|
.getRepository(usedEntity)
|
||||||
.createQueryBuilder('media')
|
.createQueryBuilder('media')
|
||||||
.select(['media.name', 'directory.name', 'directory.path'])
|
.select(['media.name', 'directory.name', 'directory.path'])
|
||||||
.leftJoin('media.directory', 'directory')
|
.leftJoin('media.directory', 'directory')
|
||||||
.offset(this.DBProcessing.mediaLoaded)
|
.offset(this.DBProcessing.mediaLoaded)
|
||||||
.limit(Config.Jobs.mediaProcessingBatchSize)
|
.limit(Config.Jobs.mediaProcessingBatchSize)
|
||||||
.getMany();
|
.getMany();
|
||||||
|
|
||||||
hasMoreFile.media = result.length > 0;
|
hasMoreFile.media = result.length > 0;
|
||||||
this.DBProcessing.mediaLoaded += result.length;
|
this.DBProcessing.mediaLoaded += result.length;
|
||||||
@ -219,25 +218,25 @@ export abstract class FileJob<S extends { indexedOnly?: boolean } = { indexedOnl
|
|||||||
}
|
}
|
||||||
for (const item of scannedAndFiltered) {
|
for (const item of scannedAndFiltered) {
|
||||||
this.fileQueue.push(
|
this.fileQueue.push(
|
||||||
path.join(
|
path.join(
|
||||||
ProjectPath.ImageFolder,
|
ProjectPath.ImageFolder,
|
||||||
item.directory.path,
|
item.directory.path,
|
||||||
item.directory.name,
|
item.directory.name,
|
||||||
item.name
|
item.name
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!this.scanFilter.noMetaFile) {
|
if (!this.scanFilter.noMetaFile) {
|
||||||
|
|
||||||
const result = await connection
|
const result = await connection
|
||||||
.getRepository(FileEntity)
|
.getRepository(FileEntity)
|
||||||
.createQueryBuilder('file')
|
.createQueryBuilder('file')
|
||||||
.select(['file.name', 'directory.name', 'directory.path'])
|
.select(['file.name', 'directory.name', 'directory.path'])
|
||||||
.leftJoin('file.directory', 'directory')
|
.leftJoin('file.directory', 'directory')
|
||||||
.offset(this.DBProcessing.mediaLoaded)
|
.offset(this.DBProcessing.mediaLoaded)
|
||||||
.limit(Config.Jobs.mediaProcessingBatchSize)
|
.limit(Config.Jobs.mediaProcessingBatchSize)
|
||||||
.getMany();
|
.getMany();
|
||||||
|
|
||||||
|
|
||||||
hasMoreFile.metafile = result.length > 0;
|
hasMoreFile.metafile = result.length > 0;
|
||||||
@ -250,12 +249,12 @@ export abstract class FileJob<S extends { indexedOnly?: boolean } = { indexedOnl
|
|||||||
}
|
}
|
||||||
for (const item of scannedAndFiltered) {
|
for (const item of scannedAndFiltered) {
|
||||||
this.fileQueue.push(
|
this.fileQueue.push(
|
||||||
path.join(
|
path.join(
|
||||||
ProjectPath.ImageFolder,
|
ProjectPath.ImageFolder,
|
||||||
item.directory.path,
|
item.directory.path,
|
||||||
item.directory.name,
|
item.directory.name,
|
||||||
item.name
|
item.name
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -264,14 +263,14 @@ export abstract class FileJob<S extends { indexedOnly?: boolean } = { indexedOnl
|
|||||||
|
|
||||||
private async countMediaFromDB(): Promise<number> {
|
private async countMediaFromDB(): Promise<number> {
|
||||||
if (this.scanFilter.noVideo === true &&
|
if (this.scanFilter.noVideo === true &&
|
||||||
this.scanFilter.noPhoto === true &&
|
this.scanFilter.noPhoto === true &&
|
||||||
this.scanFilter.noMetaFile === true) {
|
this.scanFilter.noMetaFile === true) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let count = 0;
|
let count = 0;
|
||||||
const connection = await SQLConnection.getConnection();
|
const connection = await SQLConnection.getConnection();
|
||||||
if (!this.scanFilter.noVideo ||
|
if (!this.scanFilter.noVideo ||
|
||||||
!this.scanFilter.noPhoto) {
|
!this.scanFilter.noPhoto) {
|
||||||
|
|
||||||
let usedEntity = MediaEntity;
|
let usedEntity = MediaEntity;
|
||||||
|
|
||||||
@ -282,16 +281,16 @@ export abstract class FileJob<S extends { indexedOnly?: boolean } = { indexedOnl
|
|||||||
}
|
}
|
||||||
|
|
||||||
count += await connection
|
count += await connection
|
||||||
.getRepository(usedEntity)
|
.getRepository(usedEntity)
|
||||||
.createQueryBuilder('media')
|
.createQueryBuilder('media')
|
||||||
.getCount();
|
.getCount();
|
||||||
}
|
}
|
||||||
if (!this.scanFilter.noMetaFile) {
|
if (!this.scanFilter.noMetaFile) {
|
||||||
|
|
||||||
count += await connection
|
count += await connection
|
||||||
.getRepository(FileEntity)
|
.getRepository(FileEntity)
|
||||||
.createQueryBuilder('file')
|
.createQueryBuilder('file')
|
||||||
.getCount();
|
.getCount();
|
||||||
|
|
||||||
}
|
}
|
||||||
return count;
|
return count;
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import {Config} from '../../../../common/config/private/Config';
|
import {Config} from '../../../../common/config/private/Config';
|
||||||
import {DefaultsJobs} from '../../../../common/entities/job/JobDTO';
|
import {DefaultsJobs} from '../../../../common/entities/job/JobDTO';
|
||||||
import {FileJob} from './FileJob';
|
import {FileJob} from './FileJob';
|
||||||
import {GPXProcessing} from '../../fileprocessing/GPXProcessing';
|
|
||||||
import {FileDTO} from '../../../../common/entities/FileDTO';
|
import {FileDTO} from '../../../../common/entities/FileDTO';
|
||||||
import {Logger} from '../../../Logger';
|
import {Logger} from '../../../Logger';
|
||||||
|
import {GPXProcessing} from '../../fileaccess/fileprocessing/GPXProcessing';
|
||||||
|
|
||||||
export class GPXCompressionJob extends FileJob {
|
export class GPXCompressionJob extends FileJob {
|
||||||
public readonly Name = DefaultsJobs[DefaultsJobs['GPX Compression']];
|
public readonly Name = DefaultsJobs[DefaultsJobs['GPX Compression']];
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import {DefaultsJobs} from '../../../../common/entities/job/JobDTO';
|
import {DefaultsJobs} from '../../../../common/entities/job/JobDTO';
|
||||||
import {TempFolderCleaningJob} from './TempFolderCleaningJob';
|
import {TempFolderCleaningJob} from './TempFolderCleaningJob';
|
||||||
import {GPXProcessing} from '../../fileprocessing/GPXProcessing';
|
import {GPXProcessing} from '../../fileaccess/fileprocessing/GPXProcessing';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes all gpx file from the tmp folder
|
* Deletes all gpx file from the tmp folder
|
||||||
|
@ -4,12 +4,12 @@ import * as fs from 'fs';
|
|||||||
import {Job} from './Job';
|
import {Job} from './Job';
|
||||||
import {ConfigTemplateEntry, DefaultsJobs,} from '../../../../common/entities/job/JobDTO';
|
import {ConfigTemplateEntry, DefaultsJobs,} from '../../../../common/entities/job/JobDTO';
|
||||||
import {JobProgressStates} from '../../../../common/entities/job/JobProgressDTO';
|
import {JobProgressStates} from '../../../../common/entities/job/JobProgressDTO';
|
||||||
import {DiskMangerWorker} from '../../threading/DiskMangerWorker';
|
|
||||||
import {ProjectPath} from '../../../ProjectPath';
|
import {ProjectPath} from '../../../ProjectPath';
|
||||||
import {backendTexts} from '../../../../common/BackendTexts';
|
import {backendTexts} from '../../../../common/BackendTexts';
|
||||||
import {ParentDirectoryDTO} from '../../../../common/entities/DirectoryDTO';
|
import {ParentDirectoryDTO} from '../../../../common/entities/DirectoryDTO';
|
||||||
import {Logger} from '../../../Logger';
|
import {Logger} from '../../../Logger';
|
||||||
import {FileDTO} from '../../../../common/entities/FileDTO';
|
import {FileDTO} from '../../../../common/entities/FileDTO';
|
||||||
|
import {DiskManager} from '../../fileaccess/DiskManager';
|
||||||
|
|
||||||
const LOG_TAG = '[IndexingJob]';
|
const LOG_TAG = '[IndexingJob]';
|
||||||
|
|
||||||
@ -62,7 +62,7 @@ export class IndexingJob<
|
|||||||
if (this.config.indexChangesOnly) {
|
if (this.config.indexChangesOnly) {
|
||||||
|
|
||||||
const stat = fs.statSync(absDirPath);
|
const stat = fs.statSync(absDirPath);
|
||||||
const lastModified = DiskMangerWorker.calcLastModified(stat);
|
const lastModified = DiskManager.calcLastModified(stat);
|
||||||
scanned = await ObjectManagers.getInstance().GalleryManager.selectDirStructure(directory);
|
scanned = await ObjectManagers.getInstance().GalleryManager.selectDirStructure(directory);
|
||||||
// If not modified and it was scanned before, dir is up-to-date
|
// If not modified and it was scanned before, dir is up-to-date
|
||||||
if (
|
if (
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import {Config} from '../../../../common/config/private/Config';
|
import {Config} from '../../../../common/config/private/Config';
|
||||||
import {DefaultsJobs} from '../../../../common/entities/job/JobDTO';
|
import {DefaultsJobs} from '../../../../common/entities/job/JobDTO';
|
||||||
import {FileJob} from './FileJob';
|
import {FileJob} from './FileJob';
|
||||||
import {PhotoProcessing} from '../../fileprocessing/PhotoProcessing';
|
import {PhotoProcessing} from '../../fileaccess/fileprocessing/PhotoProcessing';
|
||||||
|
|
||||||
export class PhotoConvertingJob extends FileJob {
|
export class PhotoConvertingJob extends FileJob {
|
||||||
public readonly Name = DefaultsJobs[DefaultsJobs['Photo Converting']];
|
public readonly Name = DefaultsJobs[DefaultsJobs['Photo Converting']];
|
||||||
@ -16,8 +16,8 @@ export class PhotoConvertingJob extends FileJob {
|
|||||||
|
|
||||||
protected async shouldProcess(mPath: string): Promise<boolean> {
|
protected async shouldProcess(mPath: string): Promise<boolean> {
|
||||||
return !(await PhotoProcessing.convertedPhotoExist(
|
return !(await PhotoProcessing.convertedPhotoExist(
|
||||||
mPath,
|
mPath,
|
||||||
Config.Media.Photo.Converting.resolution
|
Config.Media.Photo.Converting.resolution
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,9 +3,9 @@ import * as path from 'path';
|
|||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import {Job} from './Job';
|
import {Job} from './Job';
|
||||||
import {ProjectPath} from '../../../ProjectPath';
|
import {ProjectPath} from '../../../ProjectPath';
|
||||||
import {PhotoProcessing} from '../../fileprocessing/PhotoProcessing';
|
import {GPXProcessing} from '../../fileaccess/fileprocessing/GPXProcessing';
|
||||||
import {VideoProcessing} from '../../fileprocessing/VideoProcessing';
|
import {PhotoProcessing} from '../../fileaccess/fileprocessing/PhotoProcessing';
|
||||||
import {GPXProcessing} from '../../fileprocessing/GPXProcessing';
|
import {VideoProcessing} from '../../fileaccess/fileprocessing/VideoProcessing';
|
||||||
|
|
||||||
export class TempFolderCleaningJob extends Job {
|
export class TempFolderCleaningJob extends Job {
|
||||||
public readonly Name = DefaultsJobs[DefaultsJobs['Temp Folder Cleaning']];
|
public readonly Name = DefaultsJobs[DefaultsJobs['Temp Folder Cleaning']];
|
||||||
@ -38,8 +38,8 @@ export class TempFolderCleaningJob extends Job {
|
|||||||
|
|
||||||
protected async isValidDirectory(filePath: string): Promise<boolean> {
|
protected async isValidDirectory(filePath: string): Promise<boolean> {
|
||||||
const originalPath = path.join(
|
const originalPath = path.join(
|
||||||
ProjectPath.ImageFolder,
|
ProjectPath.ImageFolder,
|
||||||
path.relative(ProjectPath.TranscodedFolder, filePath)
|
path.relative(ProjectPath.TranscodedFolder, filePath)
|
||||||
);
|
);
|
||||||
try {
|
try {
|
||||||
await fs.promises.access(originalPath);
|
await fs.promises.access(originalPath);
|
||||||
@ -52,7 +52,7 @@ export class TempFolderCleaningJob extends Job {
|
|||||||
|
|
||||||
protected async readDir(dirPath: string): Promise<string[]> {
|
protected async readDir(dirPath: string): Promise<string[]> {
|
||||||
return (await fs.promises.readdir(dirPath)).map((f) =>
|
return (await fs.promises.readdir(dirPath)).map((f) =>
|
||||||
path.normalize(path.join(dirPath, f))
|
path.normalize(path.join(dirPath, f))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,7 +91,7 @@ export class TempFolderCleaningJob extends Job {
|
|||||||
this.Progress.log('skipping: ' + filePath);
|
this.Progress.log('skipping: ' + filePath);
|
||||||
this.Progress.Skipped++;
|
this.Progress.Skipped++;
|
||||||
this.directoryQueue = this.directoryQueue.concat(
|
this.directoryQueue = this.directoryQueue.concat(
|
||||||
await this.readDir(filePath)
|
await this.readDir(filePath)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import {Config} from '../../../../common/config/private/Config';
|
import {Config} from '../../../../common/config/private/Config';
|
||||||
import {DefaultsJobs} from '../../../../common/entities/job/JobDTO';
|
import {DefaultsJobs} from '../../../../common/entities/job/JobDTO';
|
||||||
import {FileJob} from './FileJob';
|
import {FileJob} from './FileJob';
|
||||||
import {PhotoProcessing} from '../../fileprocessing/PhotoProcessing';
|
import {PhotoProcessing} from '../../fileaccess/fileprocessing/PhotoProcessing';
|
||||||
import {ThumbnailSourceType} from '../../threading/PhotoWorker';
|
import {ThumbnailSourceType} from '../../fileaccess/PhotoWorker';
|
||||||
import {MediaDTOUtils} from '../../../../common/entities/MediaDTO';
|
import {MediaDTOUtils} from '../../../../common/entities/MediaDTO';
|
||||||
import {FileDTO} from '../../../../common/entities/FileDTO';
|
import {FileDTO} from '../../../../common/entities/FileDTO';
|
||||||
import {backendTexts} from '../../../../common/BackendTexts';
|
import {backendTexts} from '../../../../common/BackendTexts';
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import {Config} from '../../../../common/config/private/Config';
|
import {Config} from '../../../../common/config/private/Config';
|
||||||
import {DefaultsJobs} from '../../../../common/entities/job/JobDTO';
|
import {DefaultsJobs} from '../../../../common/entities/job/JobDTO';
|
||||||
import {FileJob} from './FileJob';
|
import {FileJob} from './FileJob';
|
||||||
import {VideoProcessing} from '../../fileprocessing/VideoProcessing';
|
import {VideoProcessing} from '../../fileaccess/fileprocessing/VideoProcessing';
|
||||||
|
|
||||||
declare const global: any;
|
declare const global: any;
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import {createTransport, Transporter} from 'nodemailer';
|
import {createTransport, Transporter} from 'nodemailer';
|
||||||
import {MediaDTO, MediaDTOUtils} from '../../../common/entities/MediaDTO';
|
import {MediaDTO, MediaDTOUtils} from '../../../common/entities/MediaDTO';
|
||||||
import {Config} from '../../../common/config/private/Config';
|
import {Config} from '../../../common/config/private/Config';
|
||||||
import {PhotoProcessing} from '../fileprocessing/PhotoProcessing';
|
import {PhotoProcessing} from '../fileaccess/fileprocessing/PhotoProcessing';
|
||||||
import {ThumbnailSourceType} from '../threading/PhotoWorker';
|
import {ThumbnailSourceType} from '../fileaccess/PhotoWorker';
|
||||||
import {ProjectPath} from '../../ProjectPath';
|
import {ProjectPath} from '../../ProjectPath';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import {PhotoMetadata} from '../../../common/entities/PhotoDTO';
|
import {PhotoMetadata} from '../../../common/entities/PhotoDTO';
|
||||||
@ -27,10 +27,10 @@ export class EmailMediaMessenger {
|
|||||||
|
|
||||||
private async getThumbnail(m: MediaDTO) {
|
private async getThumbnail(m: MediaDTO) {
|
||||||
return await PhotoProcessing.generateThumbnail(
|
return await PhotoProcessing.generateThumbnail(
|
||||||
path.join(ProjectPath.ImageFolder, m.directory.path, m.directory.name, m.name),
|
path.join(ProjectPath.ImageFolder, m.directory.path, m.directory.name, m.name),
|
||||||
Config.Media.Thumbnail.thumbnailSizes[0],
|
Config.Media.Thumbnail.thumbnailSizes[0],
|
||||||
MediaDTOUtils.isPhoto(m) ? ThumbnailSourceType.Photo : ThumbnailSourceType.Video,
|
MediaDTOUtils.isPhoto(m) ? ThumbnailSourceType.Photo : ThumbnailSourceType.Video,
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,22 +42,22 @@ export class EmailMediaMessenger {
|
|||||||
|
|
||||||
const attachments = [];
|
const attachments = [];
|
||||||
const htmlStart = '<h1 style="text-align: center; margin-bottom: 2em">' + Config.Server.applicationTitle + '</h1>\n' +
|
const htmlStart = '<h1 style="text-align: center; margin-bottom: 2em">' + Config.Server.applicationTitle + '</h1>\n' +
|
||||||
'<h3>' + mailSettings.text + '</h3>\n' +
|
'<h3>' + mailSettings.text + '</h3>\n' +
|
||||||
'<table style="margin-left: auto; margin-right: auto;">\n' +
|
'<table style="margin-left: auto; margin-right: auto;">\n' +
|
||||||
' <tbody>\n';
|
' <tbody>\n';
|
||||||
const htmlEnd = ' </tr>\n' +
|
const htmlEnd = ' </tr>\n' +
|
||||||
' </tbody>\n' +
|
' </tbody>\n' +
|
||||||
'</table>';
|
'</table>';
|
||||||
let htmlMiddle = '';
|
let htmlMiddle = '';
|
||||||
const numberOfColumns = media.length >= 6 ? 3 : 2;
|
const numberOfColumns = media.length >= 6 ? 3 : 2;
|
||||||
for (let i = 0; i < media.length; ++i) {
|
for (let i = 0; i < media.length; ++i) {
|
||||||
const thPath = await this.getThumbnail(media[i]);
|
const thPath = await this.getThumbnail(media[i]);
|
||||||
const linkUrl = Utils.concatUrls(Config.Server.publicUrl, '/gallery/', encodeURIComponent(path.join(media[i].directory.path, media[i].directory.name))) +
|
const linkUrl = Utils.concatUrls(Config.Server.publicUrl, '/gallery/', encodeURIComponent(path.join(media[i].directory.path, media[i].directory.name))) +
|
||||||
'?' + QueryParams.gallery.photo + '=' + encodeURIComponent(media[i].name);
|
'?' + QueryParams.gallery.photo + '=' + encodeURIComponent(media[i].name);
|
||||||
const location = (media[i].metadata as PhotoMetadata).positionData?.country ?
|
const location = (media[i].metadata as PhotoMetadata).positionData?.country ?
|
||||||
(media[i].metadata as PhotoMetadata).positionData?.country :
|
(media[i].metadata as PhotoMetadata).positionData?.country :
|
||||||
((media[i].metadata as PhotoMetadata).positionData?.city ?
|
((media[i].metadata as PhotoMetadata).positionData?.city ?
|
||||||
(media[i].metadata as PhotoMetadata).positionData?.city : '');
|
(media[i].metadata as PhotoMetadata).positionData?.city : '');
|
||||||
const caption = (new Date(media[i].metadata.creationDate)).getFullYear() + (location ? ', ' + location : '');
|
const caption = (new Date(media[i].metadata.creationDate)).getFullYear() + (location ? ', ' + location : '');
|
||||||
attachments.push({
|
attachments.push({
|
||||||
filename: media[i].name,
|
filename: media[i].name,
|
||||||
@ -68,9 +68,9 @@ export class EmailMediaMessenger {
|
|||||||
htmlMiddle += '<tr>';
|
htmlMiddle += '<tr>';
|
||||||
}
|
}
|
||||||
htmlMiddle += '<td>\n' +
|
htmlMiddle += '<td>\n' +
|
||||||
' <a style="display: block;text-align: center;" href="' + linkUrl + '"><img alt="' + media[i].name + '" style="max-width: 200px; max-height: 150px; height:auto; width:auto;" src="cid:img' + i + '"/></a>\n' +
|
' <a style="display: block;text-align: center;" href="' + linkUrl + '"><img alt="' + media[i].name + '" style="max-width: 200px; max-height: 150px; height:auto; width:auto;" src="cid:img' + i + '"/></a>\n' +
|
||||||
caption +
|
caption +
|
||||||
' </td>\n';
|
' </td>\n';
|
||||||
|
|
||||||
if (i % numberOfColumns == (numberOfColumns - 1) || i === media.length - 1) {
|
if (i % numberOfColumns == (numberOfColumns - 1) || i === media.length - 1) {
|
||||||
htmlMiddle += '</tr>';
|
htmlMiddle += '</tr>';
|
||||||
|
@ -1,119 +0,0 @@
|
|||||||
import * as cluster from 'cluster';
|
|
||||||
import {Worker} from 'cluster';
|
|
||||||
import {Logger} from '../../Logger';
|
|
||||||
import {DiskManagerTask, WorkerMessage, WorkerTask, WorkerTaskTypes,} from './Worker';
|
|
||||||
import {ParentDirectoryDTO} from '../../../common/entities/DirectoryDTO';
|
|
||||||
import {TaskQue, TaskQueEntry} from './TaskQue';
|
|
||||||
import {ITaskExecuter} from './TaskExecuter';
|
|
||||||
import {DirectoryScanSettings} from './DiskMangerWorker';
|
|
||||||
|
|
||||||
interface WorkerWrapper<O> {
|
|
||||||
worker: Worker;
|
|
||||||
poolTask: TaskQueEntry<WorkerTask, O>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const LOG_TAG = '[ThreadPool]';
|
|
||||||
|
|
||||||
export class ThreadPool<O> {
|
|
||||||
public static WorkerCount = 0;
|
|
||||||
private workers: WorkerWrapper<O>[] = [];
|
|
||||||
private taskQue = new TaskQue<WorkerTask, O>();
|
|
||||||
|
|
||||||
constructor(private size: number) {
|
|
||||||
Logger.silly(LOG_TAG, 'Creating thread pool with', size, 'workers');
|
|
||||||
for (let i = 0; i < size; i++) {
|
|
||||||
this.startWorker();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected executeTask(task: WorkerTask): Promise<O> {
|
|
||||||
const promise = this.taskQue.add(task).promise.obj;
|
|
||||||
this.run();
|
|
||||||
return promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
private run = (): void => {
|
|
||||||
if (this.taskQue.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const worker = this.getFreeWorker();
|
|
||||||
if (worker == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const poolTask = this.taskQue.get();
|
|
||||||
worker.poolTask = poolTask;
|
|
||||||
worker.worker.send(poolTask.data);
|
|
||||||
};
|
|
||||||
|
|
||||||
private getFreeWorker(): null | WorkerWrapper<O> {
|
|
||||||
for (const worker of this.workers) {
|
|
||||||
if (worker.poolTask == null) {
|
|
||||||
return worker;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private startWorker(): void {
|
|
||||||
const worker = {
|
|
||||||
poolTask: null,
|
|
||||||
worker: (cluster as any).fork(),
|
|
||||||
} as WorkerWrapper<O>;
|
|
||||||
this.workers.push(worker);
|
|
||||||
worker.worker.on('online', (): void => {
|
|
||||||
ThreadPool.WorkerCount++;
|
|
||||||
Logger.debug(
|
|
||||||
LOG_TAG,
|
|
||||||
'Worker ' + worker.worker.process.pid + ' is online, worker count:',
|
|
||||||
ThreadPool.WorkerCount
|
|
||||||
);
|
|
||||||
});
|
|
||||||
worker.worker.on('exit', (code, signal): void => {
|
|
||||||
ThreadPool.WorkerCount--;
|
|
||||||
Logger.warn(
|
|
||||||
LOG_TAG,
|
|
||||||
'Worker ' +
|
|
||||||
worker.worker.process.pid +
|
|
||||||
' died with code: ' +
|
|
||||||
code +
|
|
||||||
', and signal: ' +
|
|
||||||
signal +
|
|
||||||
', worker count:',
|
|
||||||
ThreadPool.WorkerCount
|
|
||||||
);
|
|
||||||
Logger.debug(LOG_TAG, 'Starting a new worker');
|
|
||||||
this.startWorker();
|
|
||||||
});
|
|
||||||
|
|
||||||
worker.worker.on('message', (msg: WorkerMessage<O>): void => {
|
|
||||||
if (worker.poolTask == null) {
|
|
||||||
throw new Error('No worker task after worker task is completed');
|
|
||||||
}
|
|
||||||
if (msg.error) {
|
|
||||||
worker.poolTask.promise.reject(msg.error);
|
|
||||||
} else {
|
|
||||||
worker.poolTask.promise.resolve(msg.result);
|
|
||||||
}
|
|
||||||
this.taskQue.ready(worker.poolTask);
|
|
||||||
worker.poolTask = null;
|
|
||||||
this.run();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class DiskManagerTH
|
|
||||||
extends ThreadPool<ParentDirectoryDTO>
|
|
||||||
implements ITaskExecuter<string, ParentDirectoryDTO> {
|
|
||||||
execute(
|
|
||||||
relativeDirectoryName: string,
|
|
||||||
settings: DirectoryScanSettings = {}
|
|
||||||
): Promise<ParentDirectoryDTO> {
|
|
||||||
return super.executeTask({
|
|
||||||
type: WorkerTaskTypes.diskManager,
|
|
||||||
relativeDirectoryName,
|
|
||||||
settings,
|
|
||||||
} as DiskManagerTask);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,71 +0,0 @@
|
|||||||
import {DirectoryScanSettings, DiskMangerWorker} from './DiskMangerWorker';
|
|
||||||
import {Logger} from '../../Logger';
|
|
||||||
import {MediaRendererInput, PhotoWorker} from './PhotoWorker';
|
|
||||||
import {Utils} from '../../../common/Utils';
|
|
||||||
import {MediaDTO} from '../../../common/entities/MediaDTO';
|
|
||||||
import {ParentDirectoryDTO} from '../../../common/entities/DirectoryDTO';
|
|
||||||
|
|
||||||
declare const process: NodeJS.Process;
|
|
||||||
const LOG_TAG = '[Worker]';
|
|
||||||
|
|
||||||
export class Worker {
|
|
||||||
public static process<O extends void | ParentDirectoryDTO<MediaDTO>>(): void {
|
|
||||||
Logger.debug(LOG_TAG, 'Worker is waiting for tasks');
|
|
||||||
process.on('message', async (task: WorkerTask) => {
|
|
||||||
try {
|
|
||||||
let result = null;
|
|
||||||
switch (task.type) {
|
|
||||||
case WorkerTaskTypes.diskManager:
|
|
||||||
result = await DiskMangerWorker.scanDirectory(
|
|
||||||
(task as DiskManagerTask).relativeDirectoryName,
|
|
||||||
(task as DiskManagerTask).settings
|
|
||||||
);
|
|
||||||
if (global.gc) {
|
|
||||||
global.gc();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case WorkerTaskTypes.thumbnail:
|
|
||||||
result = await PhotoWorker.render((task as ThumbnailTask).input);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error('Unknown worker task type');
|
|
||||||
}
|
|
||||||
process.send({
|
|
||||||
error: null,
|
|
||||||
result,
|
|
||||||
} as WorkerMessage<O>);
|
|
||||||
} catch (err) {
|
|
||||||
process.send({error: err, result: null});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum WorkerTaskTypes {
|
|
||||||
thumbnail = 1,
|
|
||||||
diskManager = 2,
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface WorkerTask {
|
|
||||||
type: WorkerTaskTypes;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DiskManagerTask extends WorkerTask {
|
|
||||||
relativeDirectoryName: string;
|
|
||||||
settings: DirectoryScanSettings;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ThumbnailTask extends WorkerTask {
|
|
||||||
input: MediaRendererInput;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const WorkerTask = {
|
|
||||||
equals: (t1: WorkerTask, t2: WorkerTask): boolean => {
|
|
||||||
return Utils.equalsFilter(t1, t2);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface WorkerMessage<O> {
|
|
||||||
error: Error;
|
|
||||||
result: O;
|
|
||||||
}
|
|
@ -4,7 +4,7 @@ import {GalleryMWs} from '../middlewares/GalleryMWs';
|
|||||||
import {RenderingMWs} from '../middlewares/RenderingMWs';
|
import {RenderingMWs} from '../middlewares/RenderingMWs';
|
||||||
import {ThumbnailGeneratorMWs} from '../middlewares/thumbnail/ThumbnailGeneratorMWs';
|
import {ThumbnailGeneratorMWs} from '../middlewares/thumbnail/ThumbnailGeneratorMWs';
|
||||||
import {UserRoles} from '../../common/entities/UserDTO';
|
import {UserRoles} from '../../common/entities/UserDTO';
|
||||||
import {ThumbnailSourceType} from '../model/threading/PhotoWorker';
|
import {ThumbnailSourceType} from '../model/fileaccess/PhotoWorker';
|
||||||
import {VersionMWs} from '../middlewares/VersionMWs';
|
import {VersionMWs} from '../middlewares/VersionMWs';
|
||||||
import {SupportedFormats} from '../../common/SupportedFormats';
|
import {SupportedFormats} from '../../common/SupportedFormats';
|
||||||
import {PhotoConverterMWs} from '../middlewares/thumbnail/PhotoConverterMWs';
|
import {PhotoConverterMWs} from '../middlewares/thumbnail/PhotoConverterMWs';
|
||||||
|
@ -11,7 +11,7 @@ import {UserDTO} from '../../common/entities/UserDTO';
|
|||||||
import {ServerTimeEntry} from '../middlewares/ServerTimingMWs';
|
import {ServerTimeEntry} from '../middlewares/ServerTimingMWs';
|
||||||
import {ClientConfig, TAGS} from '../../common/config/public/ClientConfig';
|
import {ClientConfig, TAGS} from '../../common/config/public/ClientConfig';
|
||||||
import {QueryParams} from '../../common/QueryParams';
|
import {QueryParams} from '../../common/QueryParams';
|
||||||
import {PhotoProcessing} from '../model/fileprocessing/PhotoProcessing';
|
import {PhotoProcessing} from '../model/fileaccess/fileprocessing/PhotoProcessing';
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
@ -10,12 +10,11 @@ import * as locale from 'locale';
|
|||||||
import {ObjectManagers} from './model/ObjectManagers';
|
import {ObjectManagers} from './model/ObjectManagers';
|
||||||
import {Logger} from './Logger';
|
import {Logger} from './Logger';
|
||||||
import {LoggerRouter} from './routes/LoggerRouter';
|
import {LoggerRouter} from './routes/LoggerRouter';
|
||||||
import {DiskManager} from './model/DiskManger';
|
|
||||||
import {ConfigDiagnostics} from './model/diagnostics/ConfigDiagnostics';
|
import {ConfigDiagnostics} from './model/diagnostics/ConfigDiagnostics';
|
||||||
import {Localizations} from './model/Localizations';
|
import {Localizations} from './model/Localizations';
|
||||||
import {CookieNames} from '../common/CookieNames';
|
import {CookieNames} from '../common/CookieNames';
|
||||||
import {Router} from './routes/Router';
|
import {Router} from './routes/Router';
|
||||||
import {PhotoProcessing} from './model/fileprocessing/PhotoProcessing';
|
import {PhotoProcessing} from './model/fileaccess/fileprocessing/PhotoProcessing';
|
||||||
import * as _csrf from 'csurf';
|
import * as _csrf from 'csurf';
|
||||||
import {Event} from '../common/event/Event';
|
import {Event} from '../common/event/Event';
|
||||||
import {QueryParams} from '../common/QueryParams';
|
import {QueryParams} from '../common/QueryParams';
|
||||||
@ -39,8 +38,8 @@ export class Server {
|
|||||||
constructor() {
|
constructor() {
|
||||||
if (!(process.env.NODE_ENV === 'production')) {
|
if (!(process.env.NODE_ENV === 'production')) {
|
||||||
Logger.info(
|
Logger.info(
|
||||||
LOG_TAG,
|
LOG_TAG,
|
||||||
'Running in DEBUG mode, set env variable NODE_ENV=production to disable '
|
'Running in DEBUG mode, set env variable NODE_ENV=production to disable '
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
this.init().catch(console.error);
|
this.init().catch(console.error);
|
||||||
@ -54,13 +53,13 @@ export class Server {
|
|||||||
Logger.info(LOG_TAG, 'running diagnostics...');
|
Logger.info(LOG_TAG, 'running diagnostics...');
|
||||||
await ConfigDiagnostics.runDiagnostics();
|
await ConfigDiagnostics.runDiagnostics();
|
||||||
Logger.verbose(
|
Logger.verbose(
|
||||||
LOG_TAG,
|
LOG_TAG,
|
||||||
'using config from ' +
|
'using config from ' +
|
||||||
(
|
(
|
||||||
ConfigClassBuilder.attachPrivateInterface(Config)
|
ConfigClassBuilder.attachPrivateInterface(Config)
|
||||||
.__options as ConfigClassOptions<ServerConfig>
|
.__options as ConfigClassOptions<ServerConfig>
|
||||||
).configPath +
|
).configPath +
|
||||||
':'
|
':'
|
||||||
);
|
);
|
||||||
Logger.verbose(LOG_TAG, JSON.stringify(Config.toJSON({attachDescription: false}), null, '\t'));
|
Logger.verbose(LOG_TAG, JSON.stringify(Config.toJSON({attachDescription: false}), null, '\t'));
|
||||||
|
|
||||||
@ -75,10 +74,10 @@ export class Server {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
this.app.use(
|
this.app.use(
|
||||||
session({
|
session({
|
||||||
name: CookieNames.session,
|
name: CookieNames.session,
|
||||||
keys: Config.Server.sessionSecret,
|
keys: Config.Server.sessionSecret,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -90,29 +89,28 @@ export class Server {
|
|||||||
const csuf: any = _csrf();
|
const csuf: any = _csrf();
|
||||||
csuf.unless = unless;
|
csuf.unless = unless;
|
||||||
this.app.use(
|
this.app.use(
|
||||||
csuf.unless((req: Request) => {
|
csuf.unless((req: Request) => {
|
||||||
return (
|
return (
|
||||||
Config.Users.authenticationRequired === false ||
|
Config.Users.authenticationRequired === false ||
|
||||||
[Config.Server.apiPath + '/user/login', Config.Server.apiPath + '/user/logout', Config.Server.apiPath + '/share/login'].indexOf(
|
[Config.Server.apiPath + '/user/login', Config.Server.apiPath + '/user/logout', Config.Server.apiPath + '/share/login'].indexOf(
|
||||||
req.originalUrl
|
req.originalUrl
|
||||||
) !== -1 ||
|
) !== -1 ||
|
||||||
(Config.Sharing.enabled === true &&
|
(Config.Sharing.enabled === true &&
|
||||||
!!req.query[QueryParams.gallery.sharingKey_query])
|
!!req.query[QueryParams.gallery.sharingKey_query])
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
// enable token generation but do not check it
|
// enable token generation but do not check it
|
||||||
this.app.post(
|
this.app.post(
|
||||||
[Config.Server.apiPath + '/user/login', Config.Server.apiPath + '/share/login'],
|
[Config.Server.apiPath + '/user/login', Config.Server.apiPath + '/share/login'],
|
||||||
_csrf({ignoreMethods: ['POST']})
|
_csrf({ignoreMethods: ['POST']})
|
||||||
);
|
);
|
||||||
this.app.get(
|
this.app.get(
|
||||||
[Config.Server.apiPath + '/user/me', Config.Server.apiPath + '/share/:' + QueryParams.gallery.sharingKey_params],
|
[Config.Server.apiPath + '/user/me', Config.Server.apiPath + '/share/:' + QueryParams.gallery.sharingKey_params],
|
||||||
_csrf({ignoreMethods: ['GET']})
|
_csrf({ignoreMethods: ['GET']})
|
||||||
);
|
);
|
||||||
|
|
||||||
DiskManager.init();
|
|
||||||
PhotoProcessing.init();
|
PhotoProcessing.init();
|
||||||
Localizations.init();
|
Localizations.init();
|
||||||
|
|
||||||
@ -176,7 +174,7 @@ export class Server {
|
|||||||
private onListening = () => {
|
private onListening = () => {
|
||||||
const addr = this.server.address();
|
const addr = this.server.address();
|
||||||
const bind =
|
const bind =
|
||||||
typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port;
|
typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port;
|
||||||
Logger.info(LOG_TAG, 'Listening on ' + bind);
|
Logger.info(LOG_TAG, 'Listening on ' + bind);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -477,30 +477,6 @@ export class ServerIndexingConfig {
|
|||||||
excludeFileList: string[] = [];
|
excludeFileList: string[] = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
@SubConfigClass({softReadonly: true})
|
|
||||||
export class ServerThreadingConfig {
|
|
||||||
@ConfigProperty({
|
|
||||||
tags:
|
|
||||||
{
|
|
||||||
name: $localize`Threading`,
|
|
||||||
uiResetNeeded: {server: true},
|
|
||||||
priority: ConfigPriority.underTheHood,
|
|
||||||
} as TAGS,
|
|
||||||
description: $localize`[Deprecated, will be removed in the next release] Runs directory scanning and thumbnail generation in a different thread.`
|
|
||||||
})
|
|
||||||
enabled: boolean = false;
|
|
||||||
@ConfigProperty({
|
|
||||||
tags:
|
|
||||||
{
|
|
||||||
name: $localize`Thumbnail threads`,
|
|
||||||
uiResetNeeded: {server: true},
|
|
||||||
priority: ConfigPriority.underTheHood
|
|
||||||
},
|
|
||||||
description: $localize`Number of threads that are used to generate thumbnails. If 0, number of 'CPU cores -1' threads will be used.`,
|
|
||||||
})
|
|
||||||
thumbnailThreads: number = 0; // if zero-> CPU count -1
|
|
||||||
}
|
|
||||||
|
|
||||||
@SubConfigClass({softReadonly: true})
|
@SubConfigClass({softReadonly: true})
|
||||||
export class ServerDuplicatesConfig {
|
export class ServerDuplicatesConfig {
|
||||||
@ConfigProperty({
|
@ConfigProperty({
|
||||||
@ -1028,14 +1004,6 @@ export class ServerServiceConfig extends ClientServiceConfig {
|
|||||||
})
|
})
|
||||||
host: string = '0.0.0.0';
|
host: string = '0.0.0.0';
|
||||||
|
|
||||||
@ConfigProperty({
|
|
||||||
tags: {
|
|
||||||
name: $localize`Threading`,
|
|
||||||
priority: ConfigPriority.underTheHood,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
Threading: ServerThreadingConfig = new ServerThreadingConfig();
|
|
||||||
|
|
||||||
@ConfigProperty({
|
@ConfigProperty({
|
||||||
tags: {
|
tags: {
|
||||||
name: $localize`Logs`,
|
name: $localize`Logs`,
|
||||||
|
@ -19,7 +19,7 @@ import {
|
|||||||
} from '../src/common/entities/PhotoDTO';
|
} from '../src/common/entities/PhotoDTO';
|
||||||
import {DirectoryBaseDTO, DirectoryPathDTO} from '../src/common/entities/DirectoryDTO';
|
import {DirectoryBaseDTO, DirectoryPathDTO} from '../src/common/entities/DirectoryDTO';
|
||||||
import {FileDTO} from '../src/common/entities/FileDTO';
|
import {FileDTO} from '../src/common/entities/FileDTO';
|
||||||
import {DiskMangerWorker} from '../src/backend/model/threading/DiskMangerWorker';
|
import {DiskManager} from '../src/backend/model/fileaccess/DiskManager';
|
||||||
|
|
||||||
export class TestHelper {
|
export class TestHelper {
|
||||||
|
|
||||||
@ -29,7 +29,7 @@ export class TestHelper {
|
|||||||
|
|
||||||
const dir = new DirectoryEntity();
|
const dir = new DirectoryEntity();
|
||||||
dir.name = name;
|
dir.name = name;
|
||||||
dir.path = DiskMangerWorker.pathFromParent({path: '', name: '.'});
|
dir.path = DiskManager.pathFromParent({path: '', name: '.'});
|
||||||
dir.mediaCount = 0;
|
dir.mediaCount = 0;
|
||||||
dir.youngestMedia = 10;
|
dir.youngestMedia = 10;
|
||||||
dir.oldestMedia = 1000;
|
dir.oldestMedia = 1000;
|
||||||
@ -40,7 +40,7 @@ export class TestHelper {
|
|||||||
dir.lastScanned = 1656069687773;
|
dir.lastScanned = 1656069687773;
|
||||||
// dir.parent = null;
|
// dir.parent = null;
|
||||||
if (parent !== null) {
|
if (parent !== null) {
|
||||||
dir.path = DiskMangerWorker.pathFromParent(parent);
|
dir.path = DiskManager.pathFromParent(parent);
|
||||||
parent.directories.push(dir);
|
parent.directories.push(dir);
|
||||||
}
|
}
|
||||||
return dir;
|
return dir;
|
||||||
@ -299,8 +299,8 @@ export class TestHelper {
|
|||||||
|
|
||||||
const dir: DirectoryBaseDTO = {
|
const dir: DirectoryBaseDTO = {
|
||||||
id: null,
|
id: null,
|
||||||
name: DiskMangerWorker.dirName(forceStr || Math.random().toString(36).substring(7)),
|
name: DiskManager.dirName(forceStr || Math.random().toString(36).substring(7)),
|
||||||
path: DiskMangerWorker.pathFromParent({path: '', name: '.'}),
|
path: DiskManager.pathFromParent({path: '', name: '.'}),
|
||||||
mediaCount: 0,
|
mediaCount: 0,
|
||||||
youngestMedia: 10,
|
youngestMedia: 10,
|
||||||
oldestMedia: 1000,
|
oldestMedia: 1000,
|
||||||
@ -314,7 +314,7 @@ export class TestHelper {
|
|||||||
parent
|
parent
|
||||||
};
|
};
|
||||||
if (parent !== null) {
|
if (parent !== null) {
|
||||||
dir.path = DiskMangerWorker.pathFromParent(parent);
|
dir.path = DiskManager.pathFromParent(parent);
|
||||||
parent.directories.push(dir);
|
parent.directories.push(dir);
|
||||||
}
|
}
|
||||||
return dir;
|
return dir;
|
||||||
|
@ -6,7 +6,7 @@ import {DatabaseType, LogLevel} from '../../src/common/config/private/PrivateCon
|
|||||||
import {ProjectPath} from '../../src/backend/ProjectPath';
|
import {ProjectPath} from '../../src/backend/ProjectPath';
|
||||||
import {DirectoryBaseDTO, ParentDirectoryDTO, SubDirectoryDTO} from '../../src/common/entities/DirectoryDTO';
|
import {DirectoryBaseDTO, ParentDirectoryDTO, SubDirectoryDTO} from '../../src/common/entities/DirectoryDTO';
|
||||||
import {ObjectManagers} from '../../src/backend/model/ObjectManagers';
|
import {ObjectManagers} from '../../src/backend/model/ObjectManagers';
|
||||||
import {DiskMangerWorker} from '../../src/backend/model/threading/DiskMangerWorker';
|
import {DiskManager} from '../../src/backend/model/fileaccess/DiskManager';
|
||||||
import {IndexingManager} from '../../src/backend/model/database/IndexingManager';
|
import {IndexingManager} from '../../src/backend/model/database/IndexingManager';
|
||||||
import {GalleryManager} from '../../src/backend/model/database/GalleryManager';
|
import {GalleryManager} from '../../src/backend/model/database/GalleryManager';
|
||||||
import {Connection} from 'typeorm';
|
import {Connection} from 'typeorm';
|
||||||
@ -135,7 +135,7 @@ export class DBTestHelper {
|
|||||||
for (let i = 0; i < d.directories.length; i++) {
|
for (let i = 0; i < d.directories.length; i++) {
|
||||||
d.directories[i] = await gm.getParentDirFromId(connection,
|
d.directories[i] = await gm.getParentDirFromId(connection,
|
||||||
(await gm.getDirIdAndTime(connection, d.directories[i].name,
|
(await gm.getDirIdAndTime(connection, d.directories[i].name,
|
||||||
path.join(DiskMangerWorker.pathFromParent(d), path.sep))).id);
|
path.join(DiskManager.pathFromParent(d), path.sep))).id);
|
||||||
await populateDir(d.directories[i]);
|
await populateDir(d.directories[i]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -30,7 +30,6 @@ describe('GalleryRouter', (sqlHelper: DBTestHelper) => {
|
|||||||
const setUp = async () => {
|
const setUp = async () => {
|
||||||
await sqlHelper.initDB();
|
await sqlHelper.initDB();
|
||||||
Config.Users.authenticationRequired = false;
|
Config.Users.authenticationRequired = false;
|
||||||
Config.Server.Threading.enabled = false;
|
|
||||||
Config.Media.Video.enabled = true;
|
Config.Media.Video.enabled = true;
|
||||||
Config.Media.folder = path.join(__dirname, '../../assets');
|
Config.Media.folder = path.join(__dirname, '../../assets');
|
||||||
Config.Media.tempFolder = tempDir;
|
Config.Media.tempFolder = tempDir;
|
||||||
|
@ -34,7 +34,6 @@ describe('PublicRouter', () => {
|
|||||||
const setUp = async () => {
|
const setUp = async () => {
|
||||||
await fs.promises.rm(tempDir, {recursive: true, force: true});
|
await fs.promises.rm(tempDir, {recursive: true, force: true});
|
||||||
Config.Users.authenticationRequired = true;
|
Config.Users.authenticationRequired = true;
|
||||||
Config.Server.Threading.enabled = false;
|
|
||||||
Config.Sharing.enabled = true;
|
Config.Sharing.enabled = true;
|
||||||
Config.Database.type = DatabaseType.sqlite;
|
Config.Database.type = DatabaseType.sqlite;
|
||||||
Config.Database.dbFolder = tempDir;
|
Config.Database.dbFolder = tempDir;
|
||||||
|
@ -35,7 +35,6 @@ describe('SharingRouter', () => {
|
|||||||
const setUp = async () => {
|
const setUp = async () => {
|
||||||
await fs.promises.rm(tempDir, {recursive: true, force: true});
|
await fs.promises.rm(tempDir, {recursive: true, force: true});
|
||||||
Config.Users.authenticationRequired = true;
|
Config.Users.authenticationRequired = true;
|
||||||
Config.Server.Threading.enabled = false;
|
|
||||||
Config.Sharing.enabled = true;
|
Config.Sharing.enabled = true;
|
||||||
Config.Database.type = DatabaseType.sqlite;
|
Config.Database.type = DatabaseType.sqlite;
|
||||||
Config.Database.dbFolder = tempDir;
|
Config.Database.dbFolder = tempDir;
|
||||||
|
@ -35,7 +35,6 @@ describe('UserRouter', () => {
|
|||||||
let server: Server;
|
let server: Server;
|
||||||
const setUp = async () => {
|
const setUp = async () => {
|
||||||
await fs.promises.rm(tempDir, {recursive: true, force: true});
|
await fs.promises.rm(tempDir, {recursive: true, force: true});
|
||||||
Config.Server.Threading.enabled = false;
|
|
||||||
Config.Database.type = DatabaseType.sqlite;
|
Config.Database.type = DatabaseType.sqlite;
|
||||||
Config.Database.dbFolder = tempDir;
|
Config.Database.dbFolder = tempDir;
|
||||||
ProjectPath.reset();
|
ProjectPath.reset();
|
||||||
|
@ -19,7 +19,6 @@ describe('SettingsRouter', () => {
|
|||||||
const tempDir = path.join(__dirname, '../../tmp');
|
const tempDir = path.join(__dirname, '../../tmp');
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await fs.promises.rm(tempDir, {recursive: true, force: true});
|
await fs.promises.rm(tempDir, {recursive: true, force: true});
|
||||||
Config.Server.Threading.enabled = false;
|
|
||||||
Config.Database.type = DatabaseType.sqlite;
|
Config.Database.type = DatabaseType.sqlite;
|
||||||
Config.Database.dbFolder = tempDir;
|
Config.Database.dbFolder = tempDir;
|
||||||
ProjectPath.reset();
|
ProjectPath.reset();
|
||||||
|
@ -2,7 +2,7 @@ import {expect} from 'chai';
|
|||||||
import {Config} from '../../../../../src/common/config/private/Config';
|
import {Config} from '../../../../../src/common/config/private/Config';
|
||||||
import {ProjectPath} from '../../../../../src/backend/ProjectPath';
|
import {ProjectPath} from '../../../../../src/backend/ProjectPath';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import {PhotoProcessing} from '../../../../../src/backend/model/fileprocessing/PhotoProcessing';
|
import {PhotoProcessing} from '../../../../../src/backend/model/fileaccess/fileprocessing/PhotoProcessing';
|
||||||
|
|
||||||
|
|
||||||
describe('PhotoProcessing', () => {
|
describe('PhotoProcessing', () => {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import {expect} from 'chai';
|
import {expect} from 'chai';
|
||||||
import {VideoProcessing} from '../../../../../src/backend/model/fileprocessing/VideoProcessing';
|
import {VideoProcessing} from '../../../../../src/backend/model/fileaccess/fileprocessing/VideoProcessing';
|
||||||
import {Config} from '../../../../../src/common/config/private/Config';
|
import {Config} from '../../../../../src/common/config/private/Config';
|
||||||
import {ProjectPath} from '../../../../../src/backend/ProjectPath';
|
import {ProjectPath} from '../../../../../src/backend/ProjectPath';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
@ -11,15 +11,14 @@ import {FileDTO} from '../../../../../src/common/entities/FileDTO';
|
|||||||
import {IndexingManager} from '../../../../../src/backend/model/database/IndexingManager';
|
import {IndexingManager} from '../../../../../src/backend/model/database/IndexingManager';
|
||||||
import {ObjectManagers} from '../../../../../src/backend/model/ObjectManagers';
|
import {ObjectManagers} from '../../../../../src/backend/model/ObjectManagers';
|
||||||
import {DBTestHelper} from '../../../DBTestHelper';
|
import {DBTestHelper} from '../../../DBTestHelper';
|
||||||
import {DiskMangerWorker} from '../../../../../src/backend/model/threading/DiskMangerWorker';
|
|
||||||
import {ReIndexingSensitivity} from '../../../../../src/common/config/private/PrivateConfig';
|
import {ReIndexingSensitivity} from '../../../../../src/common/config/private/PrivateConfig';
|
||||||
import {SearchQueryTypes, TextSearch, TextSearchQueryMatchTypes} from '../../../../../src/common/entities/SearchQueryDTO';
|
import {SearchQueryTypes, TextSearch, TextSearchQueryMatchTypes} from '../../../../../src/common/entities/SearchQueryDTO';
|
||||||
import {ProjectPath} from '../../../../../src/backend/ProjectPath';
|
import {ProjectPath} from '../../../../../src/backend/ProjectPath';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import {DiskManager} from '../../../../../src/backend/model/DiskManger';
|
|
||||||
import {AlbumManager} from '../../../../../src/backend/model/database/AlbumManager';
|
import {AlbumManager} from '../../../../../src/backend/model/database/AlbumManager';
|
||||||
import {SortByTypes} from '../../../../../src/common/entities/SortingMethods';
|
import {SortByTypes} from '../../../../../src/common/entities/SortingMethods';
|
||||||
import {ClientSortingConfig} from '../../../../../src/common/config/public/ClientConfig';
|
import {ClientSortingConfig} from '../../../../../src/common/config/public/ClientConfig';
|
||||||
|
import { DiskManager } from '../../../../../src/backend/model/fileaccess/DiskManager';
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
const deepEqualInAnyOrder = require('deep-equal-in-any-order');
|
const deepEqualInAnyOrder = require('deep-equal-in-any-order');
|
||||||
@ -162,7 +161,6 @@ describe('IndexingManager', (sqlHelper: DBTestHelper) => {
|
|||||||
|
|
||||||
ProjectPath.reset();
|
ProjectPath.reset();
|
||||||
ProjectPath.ImageFolder = path.join(__dirname, '/../../../assets');
|
ProjectPath.ImageFolder = path.join(__dirname, '/../../../assets');
|
||||||
Config.Server.Threading.enabled = false;
|
|
||||||
|
|
||||||
await ObjectManagers.getInstance().IndexingManager.indexDirectory('.');
|
await ObjectManagers.getInstance().IndexingManager.indexDirectory('.');
|
||||||
if (ObjectManagers.getInstance().IndexingManager.IsSavingInProgress) {
|
if (ObjectManagers.getInstance().IndexingManager.IsSavingInProgress) {
|
||||||
@ -353,7 +351,7 @@ describe('IndexingManager', (sqlHelper: DBTestHelper) => {
|
|||||||
const p1 = TestHelper.getRandomizedPhotoEntry(parent, 'Photo1');
|
const p1 = TestHelper.getRandomizedPhotoEntry(parent, 'Photo1');
|
||||||
|
|
||||||
const subDir = TestHelper.getRandomizedDirectoryEntry(null, 'subDir');
|
const subDir = TestHelper.getRandomizedDirectoryEntry(null, 'subDir');
|
||||||
subDir.path = DiskMangerWorker.pathFromParent(parent);
|
subDir.path = DiskManager.pathFromParent(parent);
|
||||||
const sp1 = TestHelper.getRandomizedPhotoEntry(subDir, 'subPhoto1', 0);
|
const sp1 = TestHelper.getRandomizedPhotoEntry(subDir, 'subPhoto1', 0);
|
||||||
sp1.metadata.rating = 5;
|
sp1.metadata.rating = 5;
|
||||||
const sp2 = TestHelper.getRandomizedPhotoEntry(subDir, 'subPhoto2', 0);
|
const sp2 = TestHelper.getRandomizedPhotoEntry(subDir, 'subPhoto2', 0);
|
||||||
@ -391,7 +389,7 @@ describe('IndexingManager', (sqlHelper: DBTestHelper) => {
|
|||||||
const p1 = TestHelper.getRandomizedPhotoEntry(parent, 'Photo1');
|
const p1 = TestHelper.getRandomizedPhotoEntry(parent, 'Photo1');
|
||||||
|
|
||||||
const subDir = TestHelper.getRandomizedDirectoryEntry(null, 'subDir');
|
const subDir = TestHelper.getRandomizedDirectoryEntry(null, 'subDir');
|
||||||
subDir.path = DiskMangerWorker.pathFromParent(parent);
|
subDir.path = DiskManager.pathFromParent(parent);
|
||||||
const sp1 = TestHelper.getRandomizedPhotoEntry(subDir, 'subPhoto1', 0);
|
const sp1 = TestHelper.getRandomizedPhotoEntry(subDir, 'subPhoto1', 0);
|
||||||
sp1.metadata.rating = 5;
|
sp1.metadata.rating = 5;
|
||||||
const sp2 = TestHelper.getRandomizedPhotoEntry(subDir, 'subPhoto2', 0);
|
const sp2 = TestHelper.getRandomizedPhotoEntry(subDir, 'subPhoto2', 0);
|
||||||
@ -636,7 +634,6 @@ describe('IndexingManager', (sqlHelper: DBTestHelper) => {
|
|||||||
}) as any).timeout(40000);
|
}) as any).timeout(40000);
|
||||||
|
|
||||||
it('should save .md with date', async () => {
|
it('should save .md with date', async () => {
|
||||||
Config.Server.Threading.enabled = false;
|
|
||||||
Config.Album.enabled = true;
|
Config.Album.enabled = true;
|
||||||
Config.Faces.enabled = true;
|
Config.Faces.enabled = true;
|
||||||
|
|
||||||
@ -721,7 +718,6 @@ describe('IndexingManager', (sqlHelper: DBTestHelper) => {
|
|||||||
|
|
||||||
|
|
||||||
it('.saved_searches.pg2conf', async () => {
|
it('.saved_searches.pg2conf', async () => {
|
||||||
Config.Server.Threading.enabled = false;
|
|
||||||
Config.Album.enabled = true;
|
Config.Album.enabled = true;
|
||||||
Config.Faces.enabled = true;
|
Config.Faces.enabled = true;
|
||||||
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import {expect} from 'chai';
|
import {expect} from 'chai';
|
||||||
import {DiskMangerWorker} from '../../../../../src/backend/model/threading/DiskMangerWorker';
|
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import {Config} from '../../../../../src/common/config/private/Config';
|
import {Config} from '../../../../../src/common/config/private/Config';
|
||||||
import {ProjectPath} from '../../../../../src/backend/ProjectPath';
|
import {ProjectPath} from '../../../../../src/backend/ProjectPath';
|
||||||
import {Utils} from '../../../../../src/common/Utils';
|
import {Utils} from '../../../../../src/common/Utils';
|
||||||
import {DatabaseType} from '../../../../../src/common/config/private/PrivateConfig';
|
import {DatabaseType} from '../../../../../src/common/config/private/PrivateConfig';
|
||||||
|
import {DiskManager} from '../../../../../src/backend/model/fileaccess/DiskManager';
|
||||||
|
|
||||||
declare const before: any;
|
declare const before: any;
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ describe('DiskMangerWorker', () => {
|
|||||||
it('should parse metadata', async () => {
|
it('should parse metadata', async () => {
|
||||||
Config.Media.folder = path.join(__dirname, '/../../../assets');
|
Config.Media.folder = path.join(__dirname, '/../../../assets');
|
||||||
ProjectPath.ImageFolder = path.join(__dirname, '/../../../assets');
|
ProjectPath.ImageFolder = path.join(__dirname, '/../../../assets');
|
||||||
const dir = await DiskMangerWorker.scanDirectory('/');
|
const dir = await DiskManager.scanDirectory('/');
|
||||||
// should match the number of media (photo/video) files in the assets folder
|
// should match the number of media (photo/video) files in the assets folder
|
||||||
expect(dir.media.length).to.be.equals(10);
|
expect(dir.media.length).to.be.equals(10);
|
||||||
const expected = require(path.join(__dirname, '/../../../assets/test image öüóőúéáű-.,.json'));
|
const expected = require(path.join(__dirname, '/../../../assets/test image öüóőúéáű-.,.json'));
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||||
import {expect} from 'chai';
|
import {expect} from 'chai';
|
||||||
import {MetadataLoader} from '../../../../../src/backend/model/threading/MetadataLoader';
|
import {MetadataLoader} from '../../../../../src/backend/model/fileaccess/MetadataLoader';
|
||||||
import {Utils} from '../../../../../src/common/Utils';
|
import {Utils} from '../../../../../src/common/Utils';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import {PhotoProcessing} from '../../../../../src/backend/model/fileprocessing/PhotoProcessing';
|
import {PhotoProcessing} from '../../../../../src/backend/model/fileaccess/fileprocessing/PhotoProcessing';
|
||||||
import {Config} from '../../../../../src/common/config/private/Config';
|
import {Config} from '../../../../../src/common/config/private/Config';
|
||||||
import {DatabaseType} from '../../../../../src/common/config/private/PrivateConfig';
|
import {DatabaseType} from '../../../../../src/common/config/private/PrivateConfig';
|
||||||
|
|
||||||
@ -143,7 +143,7 @@ describe('MetadataLoader', () => {
|
|||||||
delete expected.duration;
|
delete expected.duration;
|
||||||
expect(Utils.clone(data)).to.be.deep.equal(expected);
|
expect(Utils.clone(data)).to.be.deep.equal(expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should load mkv', async () => {
|
it('should load mkv', async () => {
|
||||||
const data = await MetadataLoader.loadVideoMetadata(path.join(__dirname, '/../../../assets/video_mkv.mkv'));
|
const data = await MetadataLoader.loadVideoMetadata(path.join(__dirname, '/../../../assets/video_mkv.mkv'));
|
||||||
const expected = require(path.join(__dirname, '/../../../assets/video_mkv.json'));
|
const expected = require(path.join(__dirname, '/../../../assets/video_mkv.json'));
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import {expect} from 'chai';
|
import {expect} from 'chai';
|
||||||
import {TaskExecuter} from '../../../../../src/backend/model/threading/TaskExecuter';
|
import {TaskExecuter} from '../../../../../src/backend/model/fileaccess/TaskExecuter';
|
||||||
|
|
||||||
describe('TaskExecuter', () => {
|
describe('TaskExecuter', () => {
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import {expect} from 'chai';
|
import {expect} from 'chai';
|
||||||
import {TaskQue} from '../../../../../src/backend/model/threading/TaskQue';
|
import {TaskQue} from '../../../../../src/backend/model/fileaccess/TaskQue';
|
||||||
|
|
||||||
describe('TaskQue', () => {
|
describe('TaskQue', () => {
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user