1
0
mirror of https://github.com/xuthus83/pigallery2.git synced 2025-01-14 14:43:17 +08:00

adding indexedOnly option to fileJobs

This commit is contained in:
Patrik J. Braun 2019-12-27 19:45:11 +01:00
parent b3024986e6
commit 08e3937292
8 changed files with 116 additions and 77 deletions

View File

@ -63,10 +63,8 @@ export class JobManager implements IJobManager {
} }
async onJobFinished(job: IJob<any>): Promise<void> { async onJobFinished(job: IJob<any>): Promise<void> {
console.log('onFinished' + job.Name);
const sch = Config.Server.Jobs.scheduled.find(s => s.jobName === job.Name); const sch = Config.Server.Jobs.scheduled.find(s => s.jobName === job.Name);
if (sch) { if (sch) {
console.log('parent found' + sch.jobName);
const children = Config.Server.Jobs.scheduled.filter(s => s.trigger.type === JobTriggerType.after && const children = Config.Server.Jobs.scheduled.filter(s => s.trigger.type === JobTriggerType.after &&
(<AfterJobTrigger>s.trigger).afterScheduleName === sch.name); (<AfterJobTrigger>s.trigger).afterScheduleName === sch.name);
for (let i = 0; i < children.length; ++i) { for (let i = 0; i < children.length; ++i) {

View File

@ -4,8 +4,15 @@ import {Job} from './Job';
import * as path from 'path'; import * as path from 'path';
import {DiskManager} from '../../DiskManger'; import {DiskManager} from '../../DiskManger';
import {DiskMangerWorker} from '../../threading/DiskMangerWorker'; import {DiskMangerWorker} from '../../threading/DiskMangerWorker';
import {DirectoryDTO} from '../../../../common/entities/DirectoryDTO';
import {Logger} from '../../../Logger'; import {Logger} from '../../../Logger';
import {Config} from '../../../../common/config/private/Config';
import {ServerConfig} from '../../../../common/config/private/IPrivateConfig';
import {FileDTO} from '../../../../common/entities/FileDTO';
import {SQLConnection} from '../../database/sql/SQLConnection';
import {MediaEntity} from '../../database/sql/enitites/MediaEntity';
import {PhotoEntity} from '../../database/sql/enitites/PhotoEntity';
import {VideoEntity} from '../../database/sql/enitites/VideoEntity';
import DatabaseType = ServerConfig.DatabaseType;
declare var global: NodeJS.Global; declare var global: NodeJS.Global;
@ -13,14 +20,22 @@ declare var global: NodeJS.Global;
const LOG_TAG = '[FileJob]'; const LOG_TAG = '[FileJob]';
export abstract class FileJob<T, S = void> extends Job<S> { export abstract class FileJob<S extends { indexedOnly: boolean } = { indexedOnly: boolean }> extends Job<S> {
public readonly ConfigTemplate: ConfigTemplateEntry[] = null; public readonly ConfigTemplate: ConfigTemplateEntry[] = [];
directoryQueue: string[] = []; directoryQueue: string[] = [];
fileQueue: T[] = []; fileQueue: FileDTO[] = [];
protected constructor(private scanFilter: DiskMangerWorker.DirectoryScanSettings) { protected constructor(private scanFilter: DiskMangerWorker.DirectoryScanSettings) {
super(); super();
if (Config.Server.Database.type !== DatabaseType.memory) {
this.ConfigTemplate.push({
id: 'indexedOnly',
type: 'boolean',
name: 'Only indexed files',
defaultValue: true
});
}
} }
protected async init() { protected async init() {
@ -29,9 +44,16 @@ export abstract class FileJob<T, S = void> extends Job<S> {
this.directoryQueue.push('/'); this.directoryQueue.push('/');
} }
protected abstract async processDirectory(directory: DirectoryDTO): Promise<T[]>; protected async filterMediaFiles(files: FileDTO[]): Promise<FileDTO[]> {
return files;
}
protected abstract async processFile(file: T): Promise<void>;
protected async filterMetaFiles(files: FileDTO[]): Promise<FileDTO[]> {
return files;
}
protected abstract async processFile(file: FileDTO): Promise<void>;
protected async step(): Promise<JobProgressDTO> { protected async step(): Promise<JobProgressDTO> {
if (this.directoryQueue.length === 0 && this.fileQueue.length === 0) { if (this.directoryQueue.length === 0 && this.fileQueue.length === 0) {
@ -40,18 +62,19 @@ export abstract class FileJob<T, S = void> extends Job<S> {
this.progress.time.current = Date.now(); this.progress.time.current = Date.now();
if (this.directoryQueue.length > 0) { if (this.directoryQueue.length > 0) {
const directory = this.directoryQueue.shift();
this.progress.comment = 'scanning directory: ' + directory; if (this.config.indexedOnly === true &&
const scanned = await DiskManager.scanDirectory(directory, this.scanFilter); Config.Server.Database.type !== DatabaseType.memory) {
for (let i = 0; i < scanned.directories.length; i++) { await this.loadAllMediaFilesFromDB();
this.directoryQueue.push(path.join(scanned.directories[i].path, scanned.directories[i].name)); this.directoryQueue = [];
} else {
await this.loadADirectoryFromDisk();
} }
this.fileQueue.push(...await this.processDirectory(scanned));
} else if (this.fileQueue.length > 0) { } else if (this.fileQueue.length > 0) {
const file = this.fileQueue.shift(); const file = this.fileQueue.shift();
this.progress.left = this.fileQueue.length; this.progress.left = this.fileQueue.length;
this.progress.progress++; this.progress.progress++;
this.progress.comment = 'processing: ' + file; this.progress.comment = 'processing: ' + path.join(file.directory.path, file.directory.name, file.name);
try { try {
await this.processFile(file); await this.processFile(file);
} catch (e) { } catch (e) {
@ -62,4 +85,43 @@ export abstract class FileJob<T, S = void> extends Job<S> {
return this.progress; return this.progress;
} }
private async loadADirectoryFromDisk() {
const directory = this.directoryQueue.shift();
this.progress.comment = 'scanning directory: ' + directory;
const scanned = await DiskManager.scanDirectory(directory, this.scanFilter);
for (let i = 0; i < scanned.directories.length; i++) {
this.directoryQueue.push(path.join(scanned.directories[i].path, scanned.directories[i].name));
}
if (this.scanFilter.noVideo !== true || this.scanFilter.noVideo !== true) {
this.fileQueue.push(...await this.filterMediaFiles(scanned.media));
}
if (this.scanFilter.noMetaFile !== true) {
this.fileQueue.push(...await this.filterMetaFiles(scanned.metaFile));
}
}
private async loadAllMediaFilesFromDB() {
if (this.scanFilter.noVideo === true && this.scanFilter.noPhoto === true) {
return;
}
Logger.silly(LOG_TAG, 'Loading files from db');
const connection = await SQLConnection.getConnection();
let usedEntity = MediaEntity;
if (this.scanFilter.noVideo === true) {
usedEntity = PhotoEntity;
} else if (this.scanFilter.noPhoto === true) {
usedEntity = VideoEntity;
}
const result = await connection.getRepository(usedEntity).createQueryBuilder('media')
.select(['media.name', 'media.id'])
.leftJoinAndSelect('media.directory', 'directory')
.getMany();
this.fileQueue.push(...await this.filterMediaFiles(result));
}
} }

View File

@ -2,17 +2,14 @@ import {Config} from '../../../../common/config/private/Config';
import {DefaultsJobs} from '../../../../common/entities/job/JobDTO'; import {DefaultsJobs} from '../../../../common/entities/job/JobDTO';
import {ProjectPath} from '../../../ProjectPath'; import {ProjectPath} from '../../../ProjectPath';
import * as path from 'path'; import * as path from 'path';
import * as fs from 'fs';
import * as util from 'util';
import {FileJob} from './FileJob'; import {FileJob} from './FileJob';
import {DirectoryDTO} from '../../../../common/entities/DirectoryDTO';
import {PhotoProcessing} from '../../fileprocessing/PhotoProcessing'; import {PhotoProcessing} from '../../fileprocessing/PhotoProcessing';
import {FileDTO} from '../../../../common/entities/FileDTO';
const LOG_TAG = '[PhotoConvertingJob]'; const LOG_TAG = '[PhotoConvertingJob]';
const existsPr = util.promisify(fs.exists);
export class PhotoConvertingJob extends FileJob<string> { export class PhotoConvertingJob extends FileJob {
public readonly Name = DefaultsJobs[DefaultsJobs['Photo Converting']]; public readonly Name = DefaultsJobs[DefaultsJobs['Photo Converting']];
constructor() { constructor() {
@ -23,23 +20,12 @@ export class PhotoConvertingJob extends FileJob<string> {
return Config.Client.Media.Photo.Converting.enabled === true; return Config.Client.Media.Photo.Converting.enabled === true;
} }
protected async processDirectory(directory: DirectoryDTO): Promise<string[]> {
const ret = [];
for (let i = 0; i < directory.media.length; ++i) {
const photoPath = path.join(ProjectPath.ImageFolder,
directory.media[i].directory.path,
directory.media[i].directory.name,
directory.media[i].name);
if (await existsPr(PhotoProcessing.generateConvertedFilePath(photoPath)) === false) { protected async processFile(file: FileDTO): Promise<void> {
ret.push(photoPath); await PhotoProcessing.convertPhoto(path.join(ProjectPath.ImageFolder,
} file.directory.path,
} file.directory.name,
return ret; file.name), Config.Server.Media.Photo.Converting.resolution);
}
protected async processFile(file: string): Promise<void> {
await PhotoProcessing.convertPhoto(file, Config.Server.Media.Photo.Converting.resolution);
} }

View File

@ -1,34 +1,34 @@
import {Config} from '../../../../common/config/private/Config'; import {Config} from '../../../../common/config/private/Config';
import {ConfigTemplateEntry, DefaultsJobs} from '../../../../common/entities/job/JobDTO'; import {DefaultsJobs} from '../../../../common/entities/job/JobDTO';
import {ProjectPath} from '../../../ProjectPath'; import {ProjectPath} from '../../../ProjectPath';
import * as path from 'path'; import * as path from 'path';
import {FileJob} from './FileJob'; import {FileJob} from './FileJob';
import {DirectoryDTO} from '../../../../common/entities/DirectoryDTO';
import {PhotoProcessing} from '../../fileprocessing/PhotoProcessing'; import {PhotoProcessing} from '../../fileprocessing/PhotoProcessing';
import {ThumbnailSourceType} from '../../threading/PhotoWorker'; import {ThumbnailSourceType} from '../../threading/PhotoWorker';
import {MediaDTO} from '../../../../common/entities/MediaDTO'; import {MediaDTO} from '../../../../common/entities/MediaDTO';
import {FileDTO} from '../../../../common/entities/FileDTO';
const LOG_TAG = '[ThumbnailGenerationJob]'; const LOG_TAG = '[ThumbnailGenerationJob]';
export class ThumbnailGenerationJob extends FileJob<MediaDTO, { sizes: number[] }> { export class ThumbnailGenerationJob extends FileJob<{ sizes: number[], indexedOnly: boolean }> {
public readonly Name = DefaultsJobs[DefaultsJobs['Thumbnail Generation']]; public readonly Name = DefaultsJobs[DefaultsJobs['Thumbnail Generation']];
public readonly ConfigTemplate: ConfigTemplateEntry[] = [{
id: 'sizes',
type: 'number-array',
name: 'Sizes to generate',
defaultValue: [Config.Client.Media.Thumbnail.thumbnailSizes[0]]
}];
constructor() { constructor() {
super({noMetaFile: true}); super({noMetaFile: true});
this.ConfigTemplate.push({
id: 'sizes',
type: 'number-array',
name: 'Sizes to generate',
defaultValue: [Config.Client.Media.Thumbnail.thumbnailSizes[0]]
});
} }
public get Supported(): boolean { public get Supported(): boolean {
return true; return true;
} }
start(config: { sizes: number[] }, OnFinishCB: () => void): Promise<void> { start(config: { sizes: number[], indexedOnly: boolean }, OnFinishCB: () => void): Promise<void> {
for (let i = 0; i < config.sizes.length; ++i) { for (let i = 0; i < config.sizes.length; ++i) {
if (Config.Client.Media.Thumbnail.thumbnailSizes.indexOf(config.sizes[i]) === -1) { if (Config.Client.Media.Thumbnail.thumbnailSizes.indexOf(config.sizes[i]) === -1) {
throw new Error('unknown thumbnails size: ' + config.sizes[i] + '. Add it to the possible thumbnail sizes.'); throw new Error('unknown thumbnails size: ' + config.sizes[i] + '. Add it to the possible thumbnail sizes.');
@ -38,11 +38,15 @@ export class ThumbnailGenerationJob extends FileJob<MediaDTO, { sizes: number[]
return super.start(config, OnFinishCB); return super.start(config, OnFinishCB);
} }
protected async processDirectory(directory: DirectoryDTO): Promise<MediaDTO[]> { protected async filterMediaFiles(files: FileDTO[]): Promise<FileDTO[]> {
return directory.media; return files;
} }
protected async processFile(media: MediaDTO): Promise<void> { protected async filterMetaFiles(files: FileDTO[]): Promise<FileDTO[]> {
return undefined;
}
protected async processFile(media: FileDTO): Promise<void> {
const mPath = path.join(ProjectPath.ImageFolder, media.directory.path, media.directory.name, media.name); const mPath = path.join(ProjectPath.ImageFolder, media.directory.path, media.directory.name, media.name);
for (let i = 0; i < this.config.sizes.length; ++i) { for (let i = 0; i < this.config.sizes.length; ++i) {

View File

@ -2,17 +2,14 @@ import {Config} from '../../../../common/config/private/Config';
import {DefaultsJobs} from '../../../../common/entities/job/JobDTO'; import {DefaultsJobs} from '../../../../common/entities/job/JobDTO';
import {ProjectPath} from '../../../ProjectPath'; import {ProjectPath} from '../../../ProjectPath';
import * as path from 'path'; import * as path from 'path';
import * as fs from 'fs';
import * as util from 'util';
import {FileJob} from './FileJob'; import {FileJob} from './FileJob';
import {DirectoryDTO} from '../../../../common/entities/DirectoryDTO';
import {VideoProcessing} from '../../fileprocessing/VideoProcessing'; import {VideoProcessing} from '../../fileprocessing/VideoProcessing';
import {FileDTO} from '../../../../common/entities/FileDTO';
const LOG_TAG = '[VideoConvertingJob]'; const LOG_TAG = '[VideoConvertingJob]';
const existsPr = util.promisify(fs.exists);
export class VideoConvertingJob extends FileJob<string> { export class VideoConvertingJob extends FileJob {
public readonly Name = DefaultsJobs[DefaultsJobs['Video Converting']]; public readonly Name = DefaultsJobs[DefaultsJobs['Video Converting']];
constructor() { constructor() {
@ -23,23 +20,12 @@ export class VideoConvertingJob extends FileJob<string> {
return Config.Client.Media.Video.enabled === true; return Config.Client.Media.Video.enabled === true;
} }
protected async processDirectory(directory: DirectoryDTO): Promise<string[]> {
const ret = [];
for (let i = 0; i < directory.media.length; ++i) {
const videoPath = path.join(ProjectPath.ImageFolder,
directory.media[i].directory.path,
directory.media[i].directory.name,
directory.media[i].name);
if (await existsPr(VideoProcessing.generateConvertedFilePath(videoPath)) === false) { protected async processFile(file: FileDTO): Promise<void> {
ret.push(videoPath); await VideoProcessing.convertVideo(path.join(ProjectPath.ImageFolder,
} file.directory.path,
} file.directory.name,
return ret; file.name));
}
protected async processFile(file: string): Promise<void> {
await VideoProcessing.convertVideo(file);
} }

View File

@ -126,7 +126,7 @@ export class DiskMangerWorker {
d.isPartial = true; d.isPartial = true;
directory.directories.push(d); directory.directories.push(d);
} else if (PhotoProcessing.isPhoto(fullFilePath)) { } else if (PhotoProcessing.isPhoto(fullFilePath)) {
if (settings.noPhoto) { if (settings.noPhoto === true) {
continue; continue;
} }
directory.media.push(<PhotoDTO>{ directory.media.push(<PhotoDTO>{
@ -139,7 +139,7 @@ export class DiskMangerWorker {
break; break;
} }
} else if (VideoProcessing.isVideo(fullFilePath)) { } else if (VideoProcessing.isVideo(fullFilePath)) {
if (Config.Client.Media.Video.enabled === false || settings.noVideo) { if (Config.Client.Media.Video.enabled === false || settings.noVideo === true) {
continue; continue;
} }
try { try {
@ -153,7 +153,7 @@ export class DiskMangerWorker {
} }
} else if (DiskMangerWorker.isMetaFile(fullFilePath)) { } else if (DiskMangerWorker.isMetaFile(fullFilePath)) {
if (Config.Client.MetaFile.enabled === false || settings.noMetaFile) { if (Config.Client.MetaFile.enabled === false || settings.noMetaFile === true) {
continue; continue;
} }

View File

@ -15,7 +15,10 @@ export class PhotoWorker {
if (input.type === ThumbnailSourceType.Photo) { if (input.type === ThumbnailSourceType.Photo) {
return this.renderFromImage(input, renderer); return this.renderFromImage(input, renderer);
} }
return this.renderFromVideo(input); if (input.type === ThumbnailSourceType.Video) {
return this.renderFromVideo(input);
}
throw new Error('Unsupported media type to render thumbnail:' + input.type);
} }
public static renderFromImage(input: RendererInput, renderer: ServerConfig.PhotoProcessingLib): Promise<void> { public static renderFromImage(input: RendererInput, renderer: ServerConfig.PhotoProcessingLib): Promise<void> {

View File

@ -51,11 +51,11 @@ export module MediaDTO {
}; };
export const isPhoto = (media: MediaDTO): boolean => { export const isPhoto = (media: FileDTO): boolean => {
return !MediaDTO.isVideo(media); return !MediaDTO.isVideo(media);
}; };
export const isVideo = (media: MediaDTO): boolean => { export const isVideo = (media: FileDTO): boolean => {
const lower = media.name.toLowerCase(); const lower = media.name.toLowerCase();
for (const ext of SupportedFormats.WithDots.Videos) { for (const ext of SupportedFormats.WithDots.Videos) {
if (lower.endsWith(ext)) { if (lower.endsWith(ext)) {
@ -65,7 +65,7 @@ export module MediaDTO {
return false; return false;
}; };
export const isVideoTranscodingNeeded = (media: MediaDTO): boolean => { export const isVideoTranscodingNeeded = (media: FileDTO): boolean => {
const lower = media.name.toLowerCase(); const lower = media.name.toLowerCase();
for (const ext of SupportedFormats.WithDots.TranscodeNeed.Videos) { for (const ext of SupportedFormats.WithDots.TranscodeNeed.Videos) {
if (lower.endsWith(ext)) { if (lower.endsWith(ext)) {