1
0
mirror of https://github.com/bpatrik/pigallery2.git synced 2025-04-07 07:09:59 +02:00

improving task management

This commit is contained in:
Patrik J. Braun 2019-12-11 17:02:02 +01:00
parent f2faa4cd25
commit 2ac0eaa57a
15 changed files with 143 additions and 94 deletions

View File

@ -1,4 +1,4 @@
import {TaskProgressDTO} from '../../../../common/entities/settings/TaskProgressDTO';
import {TaskProgressDTO, TaskState} from '../../../../common/entities/settings/TaskProgressDTO';
import {ObjectManagers} from '../../ObjectManagers';
import * as path from 'path';
import * as fs from 'fs';
@ -45,7 +45,7 @@ export class IndexingTask extends Task<{ createThumbnails: boolean }> {
this.progress.comment = directory;
this.progress.left = this.directoriesToIndex.length;
const scanned = await ObjectManagers.getInstance().IndexingManager.indexDirectory(directory);
if (this.running === false) {
if (this.state !== TaskState.running) {
return null;
}
this.progress.progress++;

View File

@ -1,4 +1,4 @@
import {TaskProgressDTO} from '../../../../common/entities/settings/TaskProgressDTO';
import {TaskProgressDTO, TaskState} from '../../../../common/entities/settings/TaskProgressDTO';
import {Logger} from '../../../Logger';
import {ITask} from './ITask';
import {ConfigTemplateEntry, TaskDTO} from '../../../../common/entities/task/TaskDTO';
@ -8,7 +8,7 @@ declare const process: any;
export abstract class Task<T = void> implements ITask<T> {
protected progress: TaskProgressDTO = null;
protected running = false;
protected state = TaskState.idle;
protected config: T;
protected prResolve: () => void;
@ -25,24 +25,26 @@ export abstract class Task<T = void> implements ITask<T> {
}
public start(config: T): Promise<void> {
if (this.running === false && this.Supported) {
if (this.state === TaskState.idle && this.Supported) {
Logger.info('[Task]', 'Running task: ' + this.Name);
this.config = config;
this.progress = {
progress: 0,
left: 0,
comment: '',
state: TaskState.running,
time: {
start: Date.now(),
current: Date.now()
}
};
this.running = true;
this.init().catch(console.error);
this.run();
return new Promise<void>((resolve) => {
const pr = new Promise<void>((resolve) => {
this.prResolve = resolve;
});
this.init().catch(console.error);
this.state = TaskState.running;
this.run();
return pr;
} else {
Logger.info('[Task]', 'Task already running: ' + this.Name);
return Promise.reject();
@ -51,8 +53,8 @@ export abstract class Task<T = void> implements ITask<T> {
public stop(): void {
Logger.info('[Task]', 'Stopping task: ' + this.Name);
this.running = false;
this.onFinish();
this.state = TaskState.stopping;
this.progress.state = TaskState.stopping;
}
public toJSON(): TaskDTO {
@ -75,13 +77,13 @@ export abstract class Task<T = void> implements ITask<T> {
private run() {
process.nextTick(async () => {
try {
if (!this.running) {
if (this.state === TaskState.idle) {
this.progress = null;
return;
}
this.progress = await this.step();
if (this.progress == null) { // finished
this.running = false;
this.state = TaskState.idle;
this.onFinish();
return;
}

View File

@ -1,4 +1,4 @@
import {TaskProgressDTO} from '../../../../common/entities/settings/TaskProgressDTO';
import {TaskProgressDTO, TaskState} from '../../../../common/entities/settings/TaskProgressDTO';
import {Config} from '../../../../common/config/private/Config';
import {ConfigTemplateEntry, DefaultsTasks} from '../../../../common/entities/task/TaskDTO';
import {Task} from './Task';
@ -33,7 +33,7 @@ export class VideoConvertingTask extends Task {
protected async step(): Promise<TaskProgressDTO> {
if ((this.directoryQueue.length === 0 && this.videoQueue.length === 0)
|| this.running === false) {
|| this.state !== TaskState.running) {
if (global.gc) {
global.gc();
}

View File

@ -1,6 +1,12 @@
export enum TaskState {
idle = 1, running = 2, stopping = 3
}
export interface TaskProgressDTO {
progress: number;
left: number;
state: TaskState;
comment: string;
time: {
start: number,

View File

@ -86,6 +86,7 @@ import {ScheduledTasksService} from './ui/settings/scheduled-tasks.service';
import {TimepickerModule} from 'ngx-bootstrap/timepicker';
import {TimeStampDatePickerComponent} from './ui/utils/timestamp-datepicker/datepicker.component';
import {TimeStampTimePickerComponent} from './ui/utils/timestamp-timepicker/timepicker.component';
import {TasksProgressComponent} from './ui/settings/tasks/progress/progress.tasks.settings.component';
@ -195,6 +196,7 @@ export function translationsFactory(locale: string) {
FacesSettingsComponent,
OtherSettingsComponent,
IndexingSettingsComponent,
TasksProgressComponent,
TasksSettingsComponent,
// Pipes
StringifyRole,

View File

@ -1,7 +1,8 @@
<form #settingsForm="ngForm" class="form-horizontal">
<div class="card mb-4">
<h5 class="card-header">
<ng-container i18n>Folder indexing</ng-container><ng-container *ngIf="changed">*</ng-container>
<ng-container i18n>Folder indexing</ng-container>
<ng-container *ngIf="changed">*</ng-container>
</h5>
<div class="card-body">
<div [hidden]="!error" class="alert alert-danger" role="alert"><strong>Error: </strong>{{error}}</div>
@ -57,9 +58,13 @@
[(ngModel)]="excludeFolderList"
name="excludeFolderList" required>
<small class="form-text text-muted">
<ng-container i18n>Folders to exclude from indexing</ng-container><br/>
<ng-container i18n>Folders to exclude from indexing</ng-container>
<br/>
<ng-container
i18n>';' separated strings. If an entry starts with '/' it is treated as an absolute path. If it doesn't start with '/' but contains a '/', the path is relative to the image directory. If it doesn't contain a '/', any folder with this name will be excluded.</ng-container>
i18n>';' separated strings. If an entry starts with '/' it is treated as an absolute path. If it doesn't
start with '/' but contains a '/', the path is relative to the image directory. If it doesn't contain a
'/', any folder with this name will be excluded.
</ng-container>
</small>
</div>
</div>
@ -72,9 +77,12 @@
[(ngModel)]="excludeFileList"
name="excludeFileList" required>
<small class="form-text text-muted">
<ng-container i18n>Files that mark a folder to be excluded from indexing</ng-container><br/>
<ng-container i18n>Files that mark a folder to be excluded from indexing</ng-container>
<br/>
<ng-container
i18n>';' separated strings. Any folder that contains a file with this name will be excluded from indexing.</ng-container>
i18n>';' separated strings. Any folder that contains a file with this name will be excluded from
indexing.
</ng-container>
</small>
</div>
</div>
@ -101,21 +109,7 @@
<div *ngIf="Progress != null">
<span class="progress-details" i18n>indexing</span>: {{Progress.comment}} <br/>
<span class="progress-details" i18n>elapsed</span>: {{tasksService.calcTimeElapsed(Progress) | duration}}<br/>
<span class="progress-details" i18n>left</span>: {{tasksService.calcTimeLeft(Progress) | duration}}
<div class="progress">
<div class="progress-bar progress-bar-success"
role="progressbar"
aria-valuenow="2"
aria-valuemin="0"
aria-valuemax="100"
style="min-width: 2em;"
[style.width.%]="(Progress.progress/(Progress.left+Progress.progress))*100">
{{Progress.progress}}
/{{Progress.progress + Progress.left}}
</div>
</div>
<app-settings-tasks-progress [progress]="Progress"></app-settings-tasks-progress>
</div>
<div class="row justify-content-center buttons-row">
@ -135,7 +129,7 @@
</button>
<button class="btn btn-secondary"
*ngIf="Progress != null"
[disabled]="inProgress"
[disabled]="inProgress || Progress.state !== TaskState.running"
(click)="cancelIndexing()" i18n>Cancel
</button>
<button class="btn btn-danger"

View File

@ -10,6 +10,7 @@ import {I18n} from '@ngx-translate/i18n-polyfill';
import {ScheduledTasksService} from '../scheduled-tasks.service';
import {DefaultsTasks} from '../../../../../common/entities/task/TaskDTO';
import {ServerConfig} from '../../../../../common/config/private/IPrivateConfig';
import {TaskState} from '../../../../../common/entities/settings/TaskProgressDTO';
@Component({
selector: 'app-settings-indexing',
@ -23,6 +24,7 @@ export class IndexingSettingsComponent extends SettingsComponent<ServerConfig.In
types: { key: number; value: string }[] = [];
TaskState = TaskState;
constructor(_authService: AuthenticationService,
_navigation: NavigationService,

View File

@ -0,0 +1,8 @@
.progress {
padding: 0;
}
.progress-row {
font-size: .75rem;
}

View File

@ -0,0 +1,30 @@
<div *ngIf="progress">
<div class="form-group row">
<div class="col-md-12">
<input *ngIf="progress.state === TaskState.running" type="text" class="form-control" disabled
[ngModel]="progress.comment" name="details">
<input *ngIf="progress.state === TaskState.stopping" type="text" class="form-control" disabled value="Stopping"
i18n-value name="details">
</div>
</div>
<div class="form-group row progress-row ">
<div class="col-md-1 text-right" title="time elapsed" i18n-title>{{TimeElapsed| duration}}</div>
<div class="progress col-md-10 ">
<div
class="progress-bar d-inline-block progress-bar-success {{progress.state === TaskState.stopping ? 'bg-secondary' : ''}}"
role="progressbar"
aria-valuenow="2"
aria-valuemin="0"
aria-valuemax="100"
style="min-width: 2em;"
[style.width.%]="(progress.progress/(progress.left+progress.progress))*100">
{{progress.progress}}
/{{progress.progress + progress.left}}
</div>
</div>
<div class="col-md-1" title="time left" i18n-title>{{TimeAll| duration}}</div>
</div>
</div>

View File

@ -0,0 +1,44 @@
import {Component, Input} from '@angular/core';
import {TaskProgressDTO, TaskState} from '../../../../../../common/entities/settings/TaskProgressDTO';
@Component({
selector: 'app-settings-tasks-progress',
templateUrl: './progress.tasks.settings.component.html',
styleUrls: ['./progress.tasks.settings.component.css']
})
export class TasksProgressComponent {
@Input() progress: TaskProgressDTO;
TaskState = TaskState;
constructor() {
}
get TimeAll(): number {
if (!this.progress) {
return 0;
}
return (this.progress.time.current - this.progress.time.start) /
this.progress.progress * (this.progress.left + this.progress.progress);
}
get TimeLeft(): number {
if (!this.progress) {
return 0;
}
return (this.progress.time.current - this.progress.time.start) / this.progress.progress * this.progress.left;
}
get TimeElapsed() {
if (!this.progress) {
return 0;
}
return (this.progress.time.current - this.progress.time.start);
}
}

View File

@ -1,8 +1,4 @@
.progress-details {
font-weight: bold;
}
.card {
margin-bottom: 1rem;
}

View File

@ -103,7 +103,7 @@
</button>
<button class="btn btn-secondary float-right"
*ngIf="tasksService.progress.value[schedule.taskName]"
[disabled]="disableButtons"
[disabled]="disableButtons || tasksService.progress.value[schedule.taskName].state !== TaskState.running"
(click)="stop(schedule)" i18n>Stop
</button>
</div>
@ -115,7 +115,8 @@
<ng-container [ngSwitch]="configEntry.type">
<ng-container *ngSwitchCase="'boolean'">
<div class="form-group row">
<label class="col-md-2 control-label" [for]="configEntry.id+'_boolean_'+i">{{configEntry.name}}</label>
<label class="col-md-2 control-label"
[for]="configEntry.id+'_boolean_'+i">{{configEntry.name}}</label>
<div class="col-md-10">
<bSwitch
id="enableThreading"
@ -135,7 +136,8 @@
</ng-container>
<ng-container *ngSwitchCase="'string'">
<div class="form-group row" [hidden]="simplifiedMode">
<label class="col-md-2 control-label" [for]="configEntry.id+'_string_'+i">{{configEntry.name}}</label>
<label class="col-md-2 control-label"
[for]="configEntry.id+'_string_'+i">{{configEntry.name}}</label>
<div class="col-md-10">
<input type="text" class="form-control" [name]="configEntry.id+'_string_'+i"
[id]="configEntry.id+'_string_'+i"
@ -145,7 +147,8 @@
</ng-container>
<ng-container *ngSwitchCase="'number'">
<div class="form-group row" [hidden]="simplifiedMode">
<label class="col-md-2 control-label" [for]="configEntry.id+'_number_'+i">{{configEntry.name}}</label>
<label class="col-md-2 control-label"
[for]="configEntry.id+'_number_'+i">{{configEntry.name}}</label>
<div class="col-md-10">
<input type="number" class="form-control" [name]="configEntry.id+'_number_'+i"
[id]="configEntry.id+'_number_'+i"
@ -157,24 +160,11 @@
</div>
</ng-container>
</div>
<div class="card-footer bg-transparent" *ngIf="tasksService.progress.value[schedule.taskName]">
<span class="progress-details"
i18n>status</span>: {{tasksService.progress.value[schedule.taskName].comment}} <br/>
<span class="progress-details" i18n>elapsed</span>: {{getTimeElapsed(schedule.taskName) | duration}}<br/>
<span class="progress-details" i18n>left</span>: {{getTimeLeft(schedule.taskName) | duration}}
<div class="progress">
<div class="progress-bar progress-bar-success"
role="progressbar"
aria-valuenow="2"
aria-valuemin="0"
aria-valuemax="100"
style="min-width: 2em;"
[style.width.%]="(tasksService.progress.value[schedule.taskName].progress/(tasksService.progress.value[schedule.taskName].left+tasksService.progress.value[schedule.taskName].progress))*100">
{{tasksService.progress.value[schedule.taskName].progress}}
/{{tasksService.progress.value[schedule.taskName].progress + tasksService.progress.value[schedule.taskName].left}}
</div>
</div>
</div>
<app-settings-tasks-progress
class="card-footer bg-transparent"
[progress]="tasksService.progress.value[schedule.taskName]">
</app-settings-tasks-progress>
</div>
</div>
<button class="btn btn-success float-right"

View File

@ -17,6 +17,7 @@ import {
import {Utils} from '../../../../../common/Utils';
import {ServerConfig} from '../../../../../common/config/private/IPrivateConfig';
import {ConfigTemplateEntry} from '../../../../../common/entities/task/TaskDTO';
import {TaskState} from '../../../../../common/entities/settings/TaskProgressDTO';
@Component({
selector: 'app-settings-tasks',
@ -33,6 +34,7 @@ export class TasksSettingsComponent extends SettingsComponent<ServerConfig.TaskC
TaskTriggerType = TaskTriggerType;
periods: string[] = [];
showDetails: boolean[] = [];
TaskState = TaskState;
constructor(_authService: AuthenticationService,
_navigation: NavigationService,
@ -85,22 +87,6 @@ export class TasksSettingsComponent extends SettingsComponent<ServerConfig.TaskC
this.tasksService.unsubscribeFromProgress();
}
getTimeLeft(id: string): number {
const prg = this.tasksService.progress.value[id];
if (!prg) {
return null;
}
return (prg.time.current - prg.time.start) / prg.progress * prg.left;
}
getTimeElapsed(id: string) {
const prg = this.tasksService.progress.value[id];
if (!prg) {
return null;
}
return (prg.time.current - prg.time.start);
}
public async start(schedule: TaskScheduleDTO) {
this.error = '';

View File

@ -133,7 +133,7 @@
</button>
<button class="btn btn-secondary float-right"
*ngIf="Progress != null"
[disabled]="inProgress"
[disabled]="inProgress || Progress.state !== TaskState.running"
(click)="cancelTranscoding()" i18n>Cancel transcoding
</button>
@ -141,21 +141,7 @@
<ng-container *ngIf="Progress != null">
<br/>
<hr/>
<span class="progress-details" i18n>status</span>: {{Progress.comment}} <br/>
<span class="progress-details" i18n>elapsed</span>: {{tasksService.calcTimeElapsed(Progress) | duration}}<br/>
<span class="progress-details" i18n>left</span>: {{tasksService.calcTimeLeft(Progress) | duration}}
<div class="progress">
<div class="progress-bar progress-bar-success"
role="progressbar"
aria-valuenow="2"
aria-valuemin="0"
aria-valuemax="100"
style="min-width: 2em;"
[style.width.%]="(Progress.progress/(Progress.left+Progress.progress))*100">
{{Progress.progress}}
/{{Progress.progress + Progress.left}}
</div>
</div>
<app-settings-tasks-progress [progress]="Progress"></app-settings-tasks-progress>
</ng-container>

View File

@ -10,6 +10,7 @@ import {ScheduledTasksService} from '../scheduled-tasks.service';
import {DefaultsTasks} from '../../../../../common/entities/task/TaskDTO';
import {ErrorDTO} from '../../../../../common/entities/Error';
import {ServerConfig} from '../../../../../common/config/private/IPrivateConfig';
import { TaskState } from '../../../../../common/entities/settings/TaskProgressDTO';
@Component({
@ -26,6 +27,8 @@ export class VideoSettingsComponent extends SettingsComponent<{ server: ServerCo
formats: ServerConfig.formatType[] = ['mp4', 'webm'];
fps = [24, 25, 30, 48, 50, 60];
TaskState = TaskState;
constructor(_authService: AuthenticationService,
_navigation: NavigationService,
_settingsService: VideoSettingsService,