mirror of
https://github.com/xuthus83/pigallery2.git
synced 2025-01-14 14:43:17 +08:00
improving job ui and job handling
This commit is contained in:
parent
c4ca9e5857
commit
4c507f286d
@ -50,11 +50,13 @@ export class AdminMWs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async startJob(req: Request, res: Response, next: NextFunction) {
|
|
||||||
|
public static startJob(soloRun: boolean) {
|
||||||
|
return async (req: Request, res: Response, next: NextFunction) => {
|
||||||
try {
|
try {
|
||||||
const id = req.params.id;
|
const id = req.params.id;
|
||||||
const JobConfig: any = req.body.config;
|
const JobConfig: any = req.body.config;
|
||||||
await ObjectManagers.getInstance().JobManager.run(id, JobConfig);
|
await ObjectManagers.getInstance().JobManager.run(id, JobConfig, soloRun);
|
||||||
req.resultPipe = 'ok';
|
req.resultPipe = 'ok';
|
||||||
return next();
|
return next();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@ -63,6 +65,7 @@ export class AdminMWs {
|
|||||||
}
|
}
|
||||||
return next(new ErrorDTO(ErrorCodes.JOB_ERROR, 'Job error: ' + JSON.stringify(err, null, ' '), err));
|
return next(new ErrorDTO(ErrorCodes.JOB_ERROR, 'Job error: ' + JSON.stringify(err, null, ' '), err));
|
||||||
}
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static stopJob(req: Request, res: Response, next: NextFunction) {
|
public static stopJob(req: Request, res: Response, next: NextFunction) {
|
||||||
@ -102,15 +105,4 @@ export class AdminMWs {
|
|||||||
return next(new ErrorDTO(ErrorCodes.JOB_ERROR, 'Job error: ' + JSON.stringify(err, null, ' '), err));
|
return next(new ErrorDTO(ErrorCodes.JOB_ERROR, 'Job error: ' + JSON.stringify(err, null, ' '), err));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public static getJobLastRuns(req: Request, res: Response, next: NextFunction) {
|
|
||||||
try {
|
|
||||||
req.resultPipe = ObjectManagers.getInstance().JobManager.getJobLastRuns();
|
|
||||||
return next();
|
|
||||||
} catch (err) {
|
|
||||||
if (err instanceof Error) {
|
|
||||||
return next(new ErrorDTO(ErrorCodes.JOB_ERROR, 'Job error: ' + err.toString(), err));
|
|
||||||
}
|
|
||||||
return next(new ErrorDTO(ErrorCodes.JOB_ERROR, 'Job error: ' + JSON.stringify(err, null, ' '), err));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -3,18 +3,17 @@ import {JobDTO} from '../../../../common/entities/job/JobDTO';
|
|||||||
|
|
||||||
export interface IJobManager {
|
export interface IJobManager {
|
||||||
|
|
||||||
run(jobId: string, config: any): Promise<void>;
|
|
||||||
|
run(jobId: string, config: any, soloRun: boolean): Promise<void>;
|
||||||
|
|
||||||
stop(jobId: string): void;
|
stop(jobId: string): void;
|
||||||
|
|
||||||
getProgresses(): { [key: string]: JobProgressDTO };
|
getProgresses(): { [key: string]: JobProgressDTO };
|
||||||
|
|
||||||
|
|
||||||
getAvailableJobs(): JobDTO[];
|
getAvailableJobs(): JobDTO[];
|
||||||
|
|
||||||
stopSchedules(): void;
|
stopSchedules(): void;
|
||||||
|
|
||||||
runSchedules(): void;
|
runSchedules(): void;
|
||||||
|
|
||||||
getJobLastRuns(): { [key: string]: JobProgressDTO };
|
|
||||||
}
|
}
|
||||||
|
@ -23,18 +23,14 @@ export class JobManager implements IJobManager, IJobListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getProgresses(): { [id: string]: JobProgressDTO } {
|
getProgresses(): { [id: string]: JobProgressDTO } {
|
||||||
return this.progressManager.Running;
|
return this.progressManager.Progresses;
|
||||||
}
|
}
|
||||||
|
|
||||||
getJobLastRuns(): { [key: string]: JobProgressDTO } {
|
async run<T>(jobName: string, config: T, soloRun: boolean): Promise<void> {
|
||||||
return this.progressManager.Finished;
|
|
||||||
}
|
|
||||||
|
|
||||||
async run<T>(jobName: string, config: T): Promise<void> {
|
|
||||||
const t = this.findJob(jobName);
|
const t = this.findJob(jobName);
|
||||||
if (t) {
|
if (t) {
|
||||||
t.JobListener = this;
|
t.JobListener = this;
|
||||||
await t.start(config);
|
await t.start(config, soloRun);
|
||||||
} else {
|
} else {
|
||||||
Logger.warn(LOG_TAG, 'cannot find job to start:' + jobName);
|
Logger.warn(LOG_TAG, 'cannot find job to start:' + jobName);
|
||||||
}
|
}
|
||||||
@ -54,8 +50,9 @@ export class JobManager implements IJobManager, IJobListener {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
onJobFinished = async (job: IJob<any>, state: JobProgressStates): Promise<void> => {
|
onJobFinished = async (job: IJob<any>, state: JobProgressStates, soloRun: boolean): Promise<void> => {
|
||||||
if (state !== JobProgressStates.finished) { // if it was not finished peacefully, do not start the next one
|
// if it was not finished peacefully or was a soloRun, do not start the next one
|
||||||
|
if (state !== JobProgressStates.finished || soloRun === true) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const sch = Config.Server.Jobs.scheduled.find(s => s.jobName === job.Name);
|
const sch = Config.Server.Jobs.scheduled.find(s => s.jobName === job.Name);
|
||||||
@ -64,7 +61,7 @@ export class JobManager implements IJobManager, IJobListener {
|
|||||||
(<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) {
|
||||||
try {
|
try {
|
||||||
await this.run(children[i].jobName, children[i].config);
|
await this.run(children[i].jobName, children[i].config, false);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
NotificationManager.warning('Job running error:' + children[i].name, e.toString());
|
NotificationManager.warning('Job running error:' + children[i].name, e.toString());
|
||||||
}
|
}
|
||||||
@ -99,7 +96,7 @@ export class JobManager implements IJobManager, IJobListener {
|
|||||||
|
|
||||||
const timer: NodeJS.Timeout = setTimeout(async () => {
|
const timer: NodeJS.Timeout = setTimeout(async () => {
|
||||||
this.timers = this.timers.filter(t => t.timer !== timer);
|
this.timers = this.timers.filter(t => t.timer !== timer);
|
||||||
await this.run(schedule.jobName, schedule.config);
|
await this.run(schedule.jobName, schedule.config, false);
|
||||||
this.runSchedule(schedule);
|
this.runSchedule(schedule);
|
||||||
}, nextDate.getTime() - Date.now());
|
}, nextDate.getTime() - Date.now());
|
||||||
this.timers.push({schedule: schedule, timer: timer});
|
this.timers.push({schedule: schedule, timer: timer});
|
||||||
|
@ -5,13 +5,13 @@ import {Config} from '../../../common/config/private/Config';
|
|||||||
import {JobProgressDTO, JobProgressStates} from '../../../common/entities/job/JobProgressDTO';
|
import {JobProgressDTO, JobProgressStates} from '../../../common/entities/job/JobProgressDTO';
|
||||||
|
|
||||||
export class JobProgressManager {
|
export class JobProgressManager {
|
||||||
private static readonly VERSION = 1;
|
private static readonly VERSION = 2;
|
||||||
db: {
|
private db: {
|
||||||
version: number,
|
version: number,
|
||||||
db: { [key: string]: { progress: JobProgressDTO, timestamp: number } }
|
progresses: { [key: string]: { progress: JobProgressDTO, timestamp: number } }
|
||||||
} = {
|
} = {
|
||||||
version: JobProgressManager.VERSION,
|
version: JobProgressManager.VERSION,
|
||||||
db: {}
|
progresses: {}
|
||||||
};
|
};
|
||||||
private readonly dbPath: string;
|
private readonly dbPath: string;
|
||||||
private timer: NodeJS.Timeout = null;
|
private timer: NodeJS.Timeout = null;
|
||||||
@ -21,29 +21,20 @@ export class JobProgressManager {
|
|||||||
this.loadDB().catch(console.error);
|
this.loadDB().catch(console.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
get Running(): { [key: string]: JobProgressDTO } {
|
get Progresses(): { [key: string]: JobProgressDTO } {
|
||||||
const m: { [key: string]: JobProgressDTO } = {};
|
const m: { [key: string]: JobProgressDTO } = {};
|
||||||
for (const key of Object.keys(this.db.db)) {
|
for (const key of Object.keys(this.db.progresses)) {
|
||||||
if (this.db.db[key].progress.state === JobProgressStates.running) {
|
m[key] = this.db.progresses[key].progress;
|
||||||
m[key] = this.db.db[key].progress;
|
if (this.db.progresses[key].progress.state === JobProgressStates.running) {
|
||||||
m[key].time.end = Date.now();
|
m[key].time.end = Date.now();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
get Finished(): { [key: string]: JobProgressDTO } {
|
|
||||||
const m: { [key: string]: JobProgressDTO } = {};
|
|
||||||
for (const key of Object.keys(this.db.db)) {
|
|
||||||
if (this.db.db[key].progress.state !== JobProgressStates.running) {
|
|
||||||
m[key] = this.db.db[key].progress;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
|
|
||||||
onJobProgressUpdate(progress: JobProgressDTO) {
|
onJobProgressUpdate(progress: JobProgressDTO) {
|
||||||
this.db.db[progress.HashName] = {progress: progress, timestamp: Date.now()};
|
this.db.progresses[progress.HashName] = {progress: progress, timestamp: Date.now()};
|
||||||
this.delayedSave();
|
this.delayedSave();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,19 +51,20 @@ export class JobProgressManager {
|
|||||||
}
|
}
|
||||||
this.db = db;
|
this.db = db;
|
||||||
|
|
||||||
while (Object.keys(this.db.db).length > Config.Server.Jobs.maxSavedProgress) {
|
while (Object.keys(this.db.progresses).length > Config.Server.Jobs.maxSavedProgress) {
|
||||||
let min: string = null;
|
let min: string = null;
|
||||||
for (const key of Object.keys(this.db.db)) {
|
for (const key of Object.keys(this.db.progresses)) {
|
||||||
if (min === null || this.db.db[min].timestamp > this.db.db[key].timestamp) {
|
if (min === null || this.db.progresses[min].timestamp > this.db.progresses[key].timestamp) {
|
||||||
min = key;
|
min = key;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
delete this.db.db[min];
|
delete this.db.progresses[min];
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const key of Object.keys(this.db.db)) {
|
for (const key of Object.keys(this.db.progresses)) {
|
||||||
if (this.db.db[key].progress.state === JobProgressStates.running) {
|
if (this.db.progresses[key].progress.state === JobProgressStates.running ||
|
||||||
this.db.db[key].progress.state = JobProgressStates.interrupted;
|
this.db.progresses[key].progress.state === JobProgressStates.cancelling) {
|
||||||
|
this.db.progresses[key].progress.state = JobProgressStates.interrupted;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -88,7 +80,7 @@ export class JobProgressManager {
|
|||||||
this.timer = setTimeout(async () => {
|
this.timer = setTimeout(async () => {
|
||||||
this.saveDB().catch(console.error);
|
this.saveDB().catch(console.error);
|
||||||
this.timer = null;
|
this.timer = null;
|
||||||
}, 1000);
|
}, 5000);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ export interface IJob<T> extends JobDTO {
|
|||||||
Progress: JobProgress;
|
Progress: JobProgress;
|
||||||
JobListener: IJobListener;
|
JobListener: IJobListener;
|
||||||
|
|
||||||
start(config: T): Promise<void>;
|
start(config: T, soloRun?: boolean): Promise<void>;
|
||||||
|
|
||||||
cancel(): void;
|
cancel(): void;
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ import {IJob} from './IJob';
|
|||||||
import {JobProgressStates} from '../../../../common/entities/job/JobProgressDTO';
|
import {JobProgressStates} from '../../../../common/entities/job/JobProgressDTO';
|
||||||
|
|
||||||
export interface IJobListener {
|
export interface IJobListener {
|
||||||
onJobFinished(job: IJob<any>, state: JobProgressStates): void;
|
onJobFinished(job: IJob<any>, state: JobProgressStates, soloRun: boolean): void;
|
||||||
|
|
||||||
onProgressUpdate(progress: JobProgress): void;
|
onProgressUpdate(progress: JobProgress): void;
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ export abstract class Job<T = void> implements IJob<T> {
|
|||||||
protected prResolve: () => void;
|
protected prResolve: () => void;
|
||||||
protected IsInstant = false;
|
protected IsInstant = false;
|
||||||
private jobListener: IJobListener;
|
private jobListener: IJobListener;
|
||||||
|
private soloRun: boolean;
|
||||||
|
|
||||||
public set JobListener(value: IJobListener) {
|
public set JobListener(value: IJobListener) {
|
||||||
this.jobListener = value;
|
this.jobListener = value;
|
||||||
@ -32,13 +33,15 @@ export abstract class Job<T = void> implements IJob<T> {
|
|||||||
return this.progress;
|
return this.progress;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get CanRun() {
|
protected get InProgress(): boolean {
|
||||||
return this.Progress == null && this.Supported;
|
return this.Progress !== null && (this.Progress.State === JobProgressStates.running ||
|
||||||
|
this.Progress.State === JobProgressStates.cancelling);
|
||||||
}
|
}
|
||||||
|
|
||||||
public start(config: T): Promise<void> {
|
public start(config: T, soloRun = false): Promise<void> {
|
||||||
if (this.CanRun) {
|
if (this.InProgress === false && this.Supported === true) {
|
||||||
Logger.info(LOG_TAG, 'Running job: ' + this.Name);
|
Logger.info(LOG_TAG, 'Running job ' + (soloRun === true ? 'solo' : '') + ': ' + this.Name);
|
||||||
|
this.soloRun = soloRun;
|
||||||
this.config = config;
|
this.config = config;
|
||||||
this.progress = new JobProgress(JobDTO.getHashName(this.Name, this.config));
|
this.progress = new JobProgress(JobDTO.getHashName(this.Name, this.config));
|
||||||
this.progress.OnChange = this.jobListener.onProgressUpdate;
|
this.progress.OnChange = this.jobListener.onProgressUpdate;
|
||||||
@ -52,12 +55,15 @@ export abstract class Job<T = void> implements IJob<T> {
|
|||||||
}
|
}
|
||||||
return pr;
|
return pr;
|
||||||
} else {
|
} else {
|
||||||
Logger.info(LOG_TAG, 'Job already running: ' + this.Name);
|
Logger.info(LOG_TAG, 'Job already running or not supported: ' + this.Name);
|
||||||
return Promise.reject();
|
return Promise.reject('Job already running or not supported: ' + this.Name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public cancel(): void {
|
public cancel(): void {
|
||||||
|
if (this.InProgress === false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
Logger.info(LOG_TAG, 'Stopping job: ' + this.Name);
|
Logger.info(LOG_TAG, 'Stopping job: ' + this.Name);
|
||||||
this.Progress.State = JobProgressStates.cancelling;
|
this.Progress.State = JobProgressStates.cancelling;
|
||||||
}
|
}
|
||||||
@ -69,18 +75,20 @@ export abstract class Job<T = void> implements IJob<T> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected abstract async step(): Promise<boolean>;
|
protected abstract async step(): Promise<boolean>;
|
||||||
|
|
||||||
protected abstract async init(): Promise<void>;
|
protected abstract async init(): Promise<void>;
|
||||||
|
|
||||||
private onFinish(): void {
|
private onFinish(): void {
|
||||||
|
if (this.InProgress === false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (this.Progress.State === JobProgressStates.running) {
|
if (this.Progress.State === JobProgressStates.running) {
|
||||||
this.Progress.State = JobProgressStates.finished;
|
this.Progress.State = JobProgressStates.finished;
|
||||||
}
|
} else if (this.Progress.State === JobProgressStates.cancelling) {
|
||||||
if (this.Progress.State === JobProgressStates.cancelling) {
|
|
||||||
this.Progress.State = JobProgressStates.canceled;
|
this.Progress.State = JobProgressStates.canceled;
|
||||||
}
|
}
|
||||||
|
|
||||||
const finishState = this.Progress.State;
|
const finishState = this.Progress.State;
|
||||||
this.progress = null;
|
this.progress = null;
|
||||||
if (global.gc) {
|
if (global.gc) {
|
||||||
@ -90,13 +98,14 @@ export abstract class Job<T = void> implements IJob<T> {
|
|||||||
if (this.IsInstant) {
|
if (this.IsInstant) {
|
||||||
this.prResolve();
|
this.prResolve();
|
||||||
}
|
}
|
||||||
this.jobListener.onJobFinished(this, finishState);
|
this.jobListener.onJobFinished(this, finishState, this.soloRun);
|
||||||
}
|
}
|
||||||
|
|
||||||
private run() {
|
private run() {
|
||||||
process.nextTick(async () => {
|
process.nextTick(async () => {
|
||||||
try {
|
try {
|
||||||
if (this.Progress == null || this.Progress.State !== JobProgressStates.running) {
|
if (this.Progress == null || this.Progress.State !== JobProgressStates.running) {
|
||||||
|
this.onFinish();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (await this.step() === false) { // finished
|
if (await this.step() === false) { // finished
|
||||||
|
@ -31,14 +31,14 @@ export class ThumbnailGenerationJob extends FileJob<{ sizes: number[], indexedOn
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
start(config: { sizes: number[], indexedOnly: boolean }): Promise<void> {
|
start(config: { sizes: number[], indexedOnly: boolean }, soloRun = false): 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.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.start(config);
|
return super.start(config, soloRun);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async filterMediaFiles(files: FileDTO[]): Promise<FileDTO[]> {
|
protected async filterMediaFiles(files: FileDTO[]): Promise<FileDTO[]> {
|
||||||
|
@ -43,16 +43,16 @@ export class AdminRouter {
|
|||||||
AdminMWs.getJobProgresses,
|
AdminMWs.getJobProgresses,
|
||||||
RenderingMWs.renderResult
|
RenderingMWs.renderResult
|
||||||
);
|
);
|
||||||
app.get('/api/admin/jobs/scheduled/lastRun',
|
|
||||||
AuthenticationMWs.authenticate,
|
|
||||||
AuthenticationMWs.authorise(UserRoles.Admin),
|
|
||||||
AdminMWs.getJobLastRuns,
|
|
||||||
RenderingMWs.renderResult
|
|
||||||
);
|
|
||||||
app.post('/api/admin/jobs/scheduled/:id/start',
|
app.post('/api/admin/jobs/scheduled/:id/start',
|
||||||
AuthenticationMWs.authenticate,
|
AuthenticationMWs.authenticate,
|
||||||
AuthenticationMWs.authorise(UserRoles.Admin),
|
AuthenticationMWs.authorise(UserRoles.Admin),
|
||||||
AdminMWs.startJob,
|
AdminMWs.startJob(false),
|
||||||
|
RenderingMWs.renderResult
|
||||||
|
);
|
||||||
|
app.post('/api/admin/jobs/scheduled/:id/soloStart',
|
||||||
|
AuthenticationMWs.authenticate,
|
||||||
|
AuthenticationMWs.authorise(UserRoles.Admin),
|
||||||
|
AdminMWs.startJob(true),
|
||||||
RenderingMWs.renderResult
|
RenderingMWs.renderResult
|
||||||
);
|
);
|
||||||
app.post('/api/admin/jobs/scheduled/:id/stop',
|
app.post('/api/admin/jobs/scheduled/:id/stop',
|
||||||
|
@ -89,6 +89,7 @@ import {JobProgressComponent} from './ui/settings/jobs/progress/job-progress.set
|
|||||||
import {JobsSettingsComponent} from './ui/settings/jobs/jobs.settings.component';
|
import {JobsSettingsComponent} from './ui/settings/jobs/jobs.settings.component';
|
||||||
import {ScheduledJobsService} from './ui/settings/scheduled-jobs.service';
|
import {ScheduledJobsService} from './ui/settings/scheduled-jobs.service';
|
||||||
import {BackendtextService} from './model/backendtext.service';
|
import {BackendtextService} from './model/backendtext.service';
|
||||||
|
import {JobButtonComponent} from './ui/settings/jobs/button/job-button.settings.component';
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@ -200,6 +201,8 @@ export function translationsFactory(locale: string) {
|
|||||||
IndexingSettingsComponent,
|
IndexingSettingsComponent,
|
||||||
JobProgressComponent,
|
JobProgressComponent,
|
||||||
JobsSettingsComponent,
|
JobsSettingsComponent,
|
||||||
|
JobButtonComponent,
|
||||||
|
|
||||||
// Pipes
|
// Pipes
|
||||||
StringifyRole,
|
StringifyRole,
|
||||||
IconizeSortingMethod,
|
IconizeSortingMethod,
|
||||||
|
@ -103,33 +103,26 @@
|
|||||||
<ng-container i18n>If you add a new folder to your gallery, the site indexes it automatically.</ng-container>
|
<ng-container i18n>If you add a new folder to your gallery, the site indexes it automatically.</ng-container>
|
||||||
<ng-container i18n>If you would like to trigger indexing manually, click index button.</ng-container>
|
<ng-container i18n>If you would like to trigger indexing manually, click index button.</ng-container>
|
||||||
<br/>
|
<br/>
|
||||||
(<ng-container i18n>Note: search only works among the indexed directories</ng-container>)
|
(
|
||||||
|
<ng-container i18n>Note: search only works among the indexed directories</ng-container>
|
||||||
|
)
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div *ngIf="Progress != null">
|
|
||||||
<app-settings-job-progress [progress]="Progress"></app-settings-job-progress>
|
<app-settings-job-progress [progress]="Progress"></app-settings-job-progress>
|
||||||
</div>
|
|
||||||
|
<app-settings-job-button #indexingButton
|
||||||
|
[soloRun]="true"
|
||||||
|
(error)="error=$event"
|
||||||
|
[jobName]="indexingJobName"></app-settings-job-button>
|
||||||
|
|
||||||
|
|
||||||
<button class="btn btn-success ml-0"
|
<app-settings-job-button class="ml-2"
|
||||||
*ngIf="Progress == null"
|
danger="true"
|
||||||
[disabled]="inProgress"
|
[soloRun]="true"
|
||||||
title="Indexes the folders"
|
(error)="error=$event"
|
||||||
i18n-title
|
[disabled]="indexingButton.Running"
|
||||||
(click)="index()">
|
[jobName]="resetJobName"></app-settings-job-button>
|
||||||
<ng-container i18n>Index folders now</ng-container>
|
|
||||||
<span class="oi oi-media-play ml-2"></span>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-secondary ml-0"
|
|
||||||
*ngIf="Progress != null"
|
|
||||||
[disabled]="inProgress || Progress.state !== JobProgressStates.running"
|
|
||||||
(click)="cancelIndexing()" i18n>Cancel converting
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-danger ml-2"
|
|
||||||
[disabled]="inProgress"
|
|
||||||
(click)="resetDatabase()" i18n>Reset Indexes
|
|
||||||
</button>
|
|
||||||
|
|
||||||
|
|
||||||
<hr/>
|
<hr/>
|
||||||
|
@ -25,6 +25,8 @@ export class IndexingSettingsComponent extends SettingsComponent<ServerConfig.In
|
|||||||
|
|
||||||
types: { key: number; value: string }[] = [];
|
types: { key: number; value: string }[] = [];
|
||||||
JobProgressStates = JobProgressStates;
|
JobProgressStates = JobProgressStates;
|
||||||
|
readonly indexingJobName = DefaultsJobs[DefaultsJobs.Indexing];
|
||||||
|
readonly resetJobName = DefaultsJobs[DefaultsJobs['Database Reset']];
|
||||||
|
|
||||||
constructor(_authService: AuthenticationService,
|
constructor(_authService: AuthenticationService,
|
||||||
_navigation: NavigationService,
|
_navigation: NavigationService,
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
:host{
|
||||||
|
display: inline-block;
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
<button class="btn {{danger ? 'btn-danger': 'btn-success'}}"
|
||||||
|
title="Trigger job run manually"
|
||||||
|
i18n-title
|
||||||
|
*ngIf="!Running"
|
||||||
|
[disabled]="disabled || jobsService.jobStartingStopping[jobName]"
|
||||||
|
(click)="start();">
|
||||||
|
<span class="mr-2" *ngIf="!shortName">Run {{jobName}} now</span>
|
||||||
|
<span class="oi oi-media-play"></span>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-secondary"
|
||||||
|
*ngIf="Running"
|
||||||
|
[disabled]="disabled || jobsService.jobStartingStopping[jobName] || Progress.state !== JobProgressStates.running"
|
||||||
|
(click)="stop();">
|
||||||
|
<span class="oi oi-media-stop"></span>
|
||||||
|
<span class="ml-2" *ngIf="!shortName">Cancel {{jobName}}</span>
|
||||||
|
</button>
|
@ -0,0 +1,73 @@
|
|||||||
|
import {Component, EventEmitter, Input, Output} from '@angular/core';
|
||||||
|
import {JobProgressStates} from '../../../../../../common/entities/job/JobProgressDTO';
|
||||||
|
import {ErrorDTO} from '../../../../../../common/entities/Error';
|
||||||
|
import {ScheduledJobsService} from '../../scheduled-jobs.service';
|
||||||
|
import {NotificationService} from '../../../../model/notification.service';
|
||||||
|
import {I18n} from '@ngx-translate/i18n-polyfill';
|
||||||
|
import {JobDTO} from '../../../../../../common/entities/job/JobDTO';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-settings-job-button',
|
||||||
|
templateUrl: './job-button.settings.component.html',
|
||||||
|
styleUrls: ['./job-button.settings.component.css']
|
||||||
|
})
|
||||||
|
export class JobButtonComponent {
|
||||||
|
@Input() jobName: string;
|
||||||
|
@Input() config: any = {};
|
||||||
|
@Input() shortName = false;
|
||||||
|
@Input() disabled = false;
|
||||||
|
@Input() soloRun = false;
|
||||||
|
@Input() danger = false;
|
||||||
|
JobProgressStates = JobProgressStates;
|
||||||
|
@Output() error = new EventEmitter<string>();
|
||||||
|
|
||||||
|
constructor(private notification: NotificationService,
|
||||||
|
public jobsService: ScheduledJobsService,
|
||||||
|
private i18n: I18n) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public get Running() {
|
||||||
|
return this.Progress && (this.Progress.state === JobProgressStates.running || this.Progress.state === JobProgressStates.cancelling);
|
||||||
|
}
|
||||||
|
|
||||||
|
get Progress() {
|
||||||
|
return this.jobsService.progress.value[JobDTO.getHashName(this.jobName, this.config)];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public async start() {
|
||||||
|
this.error.emit('');
|
||||||
|
try {
|
||||||
|
await this.jobsService.start(this.jobName, this.config, this.soloRun);
|
||||||
|
this.notification.info(this.i18n('Job') + ' ' + this.jobName + ' ' + this.i18n('started'));
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
if (err.message) {
|
||||||
|
this.error.emit((<ErrorDTO>err).message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async stop() {
|
||||||
|
this.error.emit('');
|
||||||
|
try {
|
||||||
|
await this.jobsService.stop(this.jobName);
|
||||||
|
this.notification.info(this.i18n('Job') + ' ' + this.jobName + ' ' + this.i18n('stopped'));
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
if (err.message) {
|
||||||
|
this.error.emit((<ErrorDTO>err).message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -8,9 +8,11 @@
|
|||||||
|
|
||||||
<div *ngFor="let schedule of sortedSchedules() as schedules; let i= index">
|
<div *ngFor="let schedule of sortedSchedules() as schedules; let i= index">
|
||||||
<div class="card bg-light {{shouldIdent(schedule,schedules[i-1])? 'ml-4' : ''}}">
|
<div class="card bg-light {{shouldIdent(schedule,schedules[i-1])? 'ml-4' : ''}}">
|
||||||
<div class="card-header clickable"
|
<div class="card-header">
|
||||||
(click)="showDetails[schedule.name]=!showDetails[schedule.name]">
|
|
||||||
<div class="d-flex justify-content-between">
|
<div class="d-flex justify-content-between">
|
||||||
|
<div class="clickable"
|
||||||
|
(click)="showDetails[schedule.name]=!showDetails[schedule.name]">
|
||||||
|
<span class="oi oi-chevron-{{showDetails[schedule.name] ? 'bottom' : 'right'}}"></span>
|
||||||
{{schedule.name}} @<!--
|
{{schedule.name}} @<!--
|
||||||
-->
|
-->
|
||||||
<ng-container [ngSwitch]="schedule.trigger.type">
|
<ng-container [ngSwitch]="schedule.trigger.type">
|
||||||
@ -26,19 +28,14 @@
|
|||||||
{{schedule.trigger.afterScheduleName}}
|
{{schedule.trigger.afterScheduleName}}
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<button class="btn btn-danger job-control-button" (click)="remove(i)"><span class="oi oi-trash"></span>
|
<button class="btn btn-danger job-control-button" (click)="remove(schedule)"><span class="oi oi-trash"></span>
|
||||||
</button>
|
|
||||||
<button class="btn btn-success job-control-button"
|
|
||||||
*ngIf="!getProgress(schedule)"
|
|
||||||
[disabled]="disableButtons"
|
|
||||||
(click)="start(schedule); $event.stopPropagation();"><span class="oi oi-media-play"></span>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-secondary job-control-button"
|
|
||||||
*ngIf="getProgress(schedule)"
|
|
||||||
[disabled]="disableButtons || getProgress(schedule).state !== JobProgressStates.running"
|
|
||||||
(click)="stop(schedule); $event.stopPropagation();"><span class="oi oi-media-stop"></span>
|
|
||||||
</button>
|
</button>
|
||||||
|
<app-settings-job-button class="job-control-button ml-2"
|
||||||
|
(error)="error=$event"
|
||||||
|
[jobName]="schedule.jobName" [config]="schedule.config"
|
||||||
|
[shortName]="true"></app-settings-job-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -120,18 +117,10 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<button class="btn btn-success float-right"
|
<app-settings-job-button class="float-right"
|
||||||
*ngIf="!getProgress(schedule)"
|
[jobName]="schedule.jobName"
|
||||||
[disabled]="disableButtons"
|
(error)="error=$event"
|
||||||
title="Trigger job run manually"
|
[config]="schedule.config"></app-settings-job-button>
|
||||||
i18n-title
|
|
||||||
(click)="start(schedule)" i18n>Start now
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-secondary float-right"
|
|
||||||
*ngIf="getProgress(schedule)"
|
|
||||||
[disabled]="disableButtons || getProgress(schedule).state !== JobProgressStates.running"
|
|
||||||
(click)="stop(schedule)" i18n>Stop
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -194,8 +183,8 @@
|
|||||||
|
|
||||||
<app-settings-job-progress
|
<app-settings-job-progress
|
||||||
class="card-footer bg-transparent"
|
class="card-footer bg-transparent"
|
||||||
*ngIf="getProgress(schedule) || getLastRun(schedule)"
|
*ngIf="getProgress(schedule)"
|
||||||
[progress]="getProgress(schedule) || getLastRun(schedule)">
|
[progress]="getProgress(schedule)">
|
||||||
</app-settings-job-progress>
|
</app-settings-job-progress>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -5,7 +5,6 @@ import {NavigationService} from '../../../model/navigation.service';
|
|||||||
import {NotificationService} from '../../../model/notification.service';
|
import {NotificationService} from '../../../model/notification.service';
|
||||||
import {SettingsComponent} from '../_abstract/abstract.settings.component';
|
import {SettingsComponent} from '../_abstract/abstract.settings.component';
|
||||||
import {I18n} from '@ngx-translate/i18n-polyfill';
|
import {I18n} from '@ngx-translate/i18n-polyfill';
|
||||||
import {ErrorDTO} from '../../../../../common/entities/Error';
|
|
||||||
import {ScheduledJobsService} from '../scheduled-jobs.service';
|
import {ScheduledJobsService} from '../scheduled-jobs.service';
|
||||||
import {
|
import {
|
||||||
AfterJobTrigger,
|
AfterJobTrigger,
|
||||||
@ -18,7 +17,6 @@ import {
|
|||||||
import {Utils} from '../../../../../common/Utils';
|
import {Utils} from '../../../../../common/Utils';
|
||||||
import {ServerConfig} from '../../../../../common/config/private/IPrivateConfig';
|
import {ServerConfig} from '../../../../../common/config/private/IPrivateConfig';
|
||||||
import {ConfigTemplateEntry} from '../../../../../common/entities/job/JobDTO';
|
import {ConfigTemplateEntry} from '../../../../../common/entities/job/JobDTO';
|
||||||
import {Job} from '../../../../../backend/model/jobs/jobs/Job';
|
|
||||||
import {ModalDirective} from 'ngx-bootstrap/modal';
|
import {ModalDirective} from 'ngx-bootstrap/modal';
|
||||||
import {JobProgressDTO, JobProgressStates} from '../../../../../common/entities/job/JobProgressDTO';
|
import {JobProgressDTO, JobProgressStates} from '../../../../../common/entities/job/JobProgressDTO';
|
||||||
import {BackendtextService} from '../../../model/backendtext.service';
|
import {BackendtextService} from '../../../model/backendtext.service';
|
||||||
@ -96,46 +94,8 @@ export class JobsSettingsComponent extends SettingsComponent<ServerConfig.JobCon
|
|||||||
this.jobsService.unsubscribeFromProgress();
|
this.jobsService.unsubscribeFromProgress();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async start(schedule: JobScheduleDTO) {
|
remove(schedule: JobScheduleDTO) {
|
||||||
this.error = '';
|
this.settings.scheduled.splice(this.settings.scheduled.indexOf(schedule), 1);
|
||||||
try {
|
|
||||||
this.disableButtons = true;
|
|
||||||
await this.jobsService.start(schedule.jobName, schedule.config);
|
|
||||||
this.notification.info(this.i18n('Job') + ' ' + schedule.jobName + ' ' + this.i18n('started'));
|
|
||||||
return true;
|
|
||||||
} catch (err) {
|
|
||||||
console.log(err);
|
|
||||||
if (err.message) {
|
|
||||||
this.error = (<ErrorDTO>err).message;
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
this.disableButtons = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async stop(schedule: JobScheduleDTO) {
|
|
||||||
this.error = '';
|
|
||||||
try {
|
|
||||||
this.disableButtons = true;
|
|
||||||
await this.jobsService.stop(schedule.jobName);
|
|
||||||
this.notification.info(this.i18n('Job') + ' ' + schedule.jobName + ' ' + this.i18n('stopped'));
|
|
||||||
return true;
|
|
||||||
} catch (err) {
|
|
||||||
console.log(err);
|
|
||||||
if (err.message) {
|
|
||||||
this.error = (<ErrorDTO>err).message;
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
this.disableButtons = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
remove(index: number) {
|
|
||||||
this.settings.scheduled.splice(index, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
jobTypeChanged(schedule: JobScheduleDTO) {
|
jobTypeChanged(schedule: JobScheduleDTO) {
|
||||||
@ -211,18 +171,11 @@ export class JobsSettingsComponent extends SettingsComponent<ServerConfig.JobCon
|
|||||||
this.jobModal.hide();
|
this.jobModal.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
getConfigHash(schedule: JobScheduleDTO): string {
|
|
||||||
return JSON.stringify(schedule.config);
|
|
||||||
}
|
|
||||||
|
|
||||||
getProgress(schedule: JobScheduleDTO): JobProgressDTO {
|
getProgress(schedule: JobScheduleDTO): JobProgressDTO {
|
||||||
return this.jobsService.getProgress(schedule);
|
return this.jobsService.getProgress(schedule);
|
||||||
}
|
}
|
||||||
|
|
||||||
getLastRun(schedule: JobScheduleDTO): JobProgressDTO {
|
|
||||||
return this.jobsService.getLastRun(schedule);
|
|
||||||
}
|
|
||||||
|
|
||||||
private getNextRunningDate(sch: JobScheduleDTO, list: JobScheduleDTO[], depth: number = 0): number {
|
private getNextRunningDate(sch: JobScheduleDTO, list: JobScheduleDTO[], depth: number = 0): number {
|
||||||
if (depth > list.length) {
|
if (depth > list.length) {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
<div class="row clickable" *ngIf="!IsRunning"
|
<div class="row" *ngIf="Stopped">
|
||||||
(click)="openModal(template)">
|
|
||||||
<div class="col-md-2 col-12" i18n>
|
<div class="col-md-2 col-12" i18n>
|
||||||
Last run:
|
Last run:
|
||||||
</div>
|
</div>
|
||||||
@ -7,19 +6,24 @@
|
|||||||
<span class="oi oi-clock" aria-hidden="true"></span>
|
<span class="oi oi-clock" aria-hidden="true"></span>
|
||||||
{{progress.time.start | date:'medium'}} - {{progress.time.end | date:'mediumTime'}}
|
{{progress.time.start | date:'medium'}} - {{progress.time.end | date:'mediumTime'}}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3 col-6" title="processed+skipped/all" i18n-title>
|
<div class="col-md-2 col-4"
|
||||||
|
title="processed:{{progress.steps.processed}}+ skipped:{{progress.steps.skipped}} / all:{{progress.steps.all}}">
|
||||||
<span class="oi oi-check" aria-hidden="true"></span>
|
<span class="oi oi-check" aria-hidden="true"></span>
|
||||||
{{progress.steps.processed}}+{{progress.steps.skipped}}/{{progress.steps.all}}
|
{{progress.steps.processed + progress.steps.skipped}}/{{progress.steps.all}}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3 col-6" title="Status" i18n-title>
|
<div class="col-md-2 col-4" title="Status" i18n-title>
|
||||||
<span class="oi oi-pulse" aria-hidden="true"></span>
|
<span class="oi oi-pulse" aria-hidden="true"></span>
|
||||||
{{JobProgressStates[progress.state]}}
|
{{JobProgressStates[progress.state]}}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-md-2 col-4">
|
||||||
|
<button class="btn btn-secondary float-right" (click)="openModal(template)">
|
||||||
|
<span class="oi oi-resize-both"></span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="IsRunning" class="clickable" (click)="openModal(template)">
|
</div>
|
||||||
|
<div *ngIf="Running">
|
||||||
|
|
||||||
<div class="form-group row">
|
<div class="input-group form-group">
|
||||||
<div class="col-md-12">
|
|
||||||
<input
|
<input
|
||||||
*ngIf="progress.state === JobProgressStates.running" type="text" class="form-control" disabled
|
*ngIf="progress.state === JobProgressStates.running" type="text" class="form-control" disabled
|
||||||
[ngModel]="progress.logs[progress.logs.length-1].comment" name="details">
|
[ngModel]="progress.logs[progress.logs.length-1].comment" name="details">
|
||||||
@ -27,6 +31,10 @@
|
|||||||
*ngIf="progress.state === JobProgressStates.cancelling" type="text" class="form-control" disabled
|
*ngIf="progress.state === JobProgressStates.cancelling" type="text" class="form-control" disabled
|
||||||
value="Cancelling: {{progress.logs[progress.logs.length-1].comment}}"
|
value="Cancelling: {{progress.logs[progress.logs.length-1].comment}}"
|
||||||
i18n-value name="details">
|
i18n-value name="details">
|
||||||
|
<div class="input-group-append">
|
||||||
|
<button class="btn btn-secondary" (click)="openModal(template)">
|
||||||
|
<span class="oi oi-resize-both"></span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -47,7 +55,7 @@
|
|||||||
aria-valuemax="100"
|
aria-valuemax="100"
|
||||||
style="min-width: 2em;"
|
style="min-width: 2em;"
|
||||||
[style.width.%]="((progress.steps.processed+progress.steps.skipped)/progress.steps.all)*100">
|
[style.width.%]="((progress.steps.processed+progress.steps.skipped)/progress.steps.all)*100">
|
||||||
{{progress.steps.processed}}+{{progress.steps.skipped}}/{{progress.steps.all}}
|
{{progress.steps.processed + progress.steps.skipped}}/{{progress.steps.all}}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
*ngIf="progress.steps.all === 0"
|
*ngIf="progress.steps.all === 0"
|
||||||
@ -69,10 +77,12 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="row">
|
<div class="container">
|
||||||
<div class="col-sm-4">Processed: {{progress.steps.processed}}</div>
|
<div class="row justify-content-between">
|
||||||
<div class="col-sm-4">Skipped: {{progress.steps.skipped}}</div>
|
<div>Processed: {{progress.steps.processed}}</div>
|
||||||
<div class="col-sm-4">All: {{progress.steps.all}}</div>
|
<div>Skipped: {{progress.steps.skipped}}</div>
|
||||||
|
<div>Left: {{progress.steps.all - progress.steps.skipped - progress.steps.processed}}</div>
|
||||||
|
<div>All: {{progress.steps.all}}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mt-3 mb-3">
|
<div class="row mt-3 mb-3">
|
||||||
<div class="progress col-12">
|
<div class="progress col-12">
|
||||||
@ -95,6 +105,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div class="card bg-light">
|
<div class="card bg-light">
|
||||||
<div class="card-header" i18n>
|
<div class="card-header" i18n>
|
||||||
Logs
|
Logs
|
||||||
|
@ -20,6 +20,9 @@ export class JobProgressComponent implements OnDestroy, OnChanges {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get Name(): string {
|
get Name(): string {
|
||||||
|
if (!this.progress) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
return this.progress.HashName;
|
return this.progress.HashName;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,11 +31,15 @@ export class JobProgressComponent implements OnDestroy, OnChanges {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return (this.progress.time.end - this.progress.time.start) /
|
return (this.progress.time.end - this.progress.time.start) /
|
||||||
(this.progress.steps.processed + this.progress.steps.skipped) * this.progress.steps.all;
|
(this.progress.steps.processed) * (this.progress.steps.all - this.progress.steps.skipped);
|
||||||
}
|
}
|
||||||
|
|
||||||
get IsRunning() {
|
get Running() {
|
||||||
return this.progress.state === JobProgressStates.running || this.progress.state === JobProgressStates.canceled;
|
return this.progress && (this.progress.state === JobProgressStates.running || this.progress.state === JobProgressStates.cancelling);
|
||||||
|
}
|
||||||
|
|
||||||
|
get Stopped() {
|
||||||
|
return this.progress && (this.progress.state !== JobProgressStates.running && this.progress.state !== JobProgressStates.cancelling);
|
||||||
}
|
}
|
||||||
|
|
||||||
get TimeLeft(): number {
|
get TimeLeft(): number {
|
||||||
|
@ -100,21 +100,10 @@
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div [hidden]="!settings.client.Converting.enabled">
|
<div [hidden]="!settings.client.Converting.enabled">
|
||||||
<button class="btn btn-success float-left ml-0"
|
<app-settings-job-button class="float-left"
|
||||||
*ngIf="Progress == null"
|
[soloRun]="true"
|
||||||
[disabled]="inProgress"
|
(error)="error=$event"
|
||||||
title="Indexes the folders"
|
[jobName]="jobName"></app-settings-job-button>
|
||||||
i18n-title
|
|
||||||
(click)="convertPhoto()">
|
|
||||||
<ng-container i18n>Convert photos now</ng-container>
|
|
||||||
<span class="oi oi-media-play ml-2"></span>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-secondary float-left ml-0"
|
|
||||||
*ngIf="Progress != null"
|
|
||||||
[disabled]="inProgress || Progress.state !== JobProgressStates.running"
|
|
||||||
(click)="cancelPhotoConverting()" i18n>Cancel converting
|
|
||||||
</button>
|
|
||||||
|
|
||||||
|
|
||||||
<ng-container *ngIf="Progress != null">
|
<ng-container *ngIf="Progress != null">
|
||||||
<br/>
|
<br/>
|
||||||
|
@ -39,6 +39,7 @@ export class PhotoSettingsComponent extends SettingsComponent<{
|
|||||||
}
|
}
|
||||||
return v;
|
return v;
|
||||||
});
|
});
|
||||||
|
readonly jobName = DefaultsJobs[DefaultsJobs['Photo Converting']];
|
||||||
|
|
||||||
constructor(_authService: AuthenticationService,
|
constructor(_authService: AuthenticationService,
|
||||||
_navigation: NavigationService,
|
_navigation: NavigationService,
|
||||||
@ -61,44 +62,6 @@ export class PhotoSettingsComponent extends SettingsComponent<{
|
|||||||
get Progress() {
|
get Progress() {
|
||||||
return this.jobsService.progress.value[JobDTO.getHashName(DefaultsJobs[DefaultsJobs['Photo Converting']])];
|
return this.jobsService.progress.value[JobDTO.getHashName(DefaultsJobs[DefaultsJobs['Photo Converting']])];
|
||||||
}
|
}
|
||||||
|
|
||||||
async convertPhoto() {
|
|
||||||
this.inProgress = true;
|
|
||||||
this.error = '';
|
|
||||||
try {
|
|
||||||
await this.jobsService.start(DefaultsJobs[DefaultsJobs['Photo Converting']]);
|
|
||||||
this.notification.info(this.i18n('Photo converting started'));
|
|
||||||
return true;
|
|
||||||
} catch (err) {
|
|
||||||
console.log(err);
|
|
||||||
if (err.message) {
|
|
||||||
this.error = (<ErrorDTO>err).message;
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
this.inProgress = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
async cancelPhotoConverting() {
|
|
||||||
this.inProgress = true;
|
|
||||||
this.error = '';
|
|
||||||
try {
|
|
||||||
await this.jobsService.stop(DefaultsJobs[DefaultsJobs['Photo Converting']]);
|
|
||||||
this.notification.info(this.i18n('Photo converting interrupted'));
|
|
||||||
return true;
|
|
||||||
} catch (err) {
|
|
||||||
console.log(err);
|
|
||||||
if (err.message) {
|
|
||||||
this.error = (<ErrorDTO>err).message;
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
this.inProgress = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -10,24 +10,19 @@ export class ScheduledJobsService {
|
|||||||
|
|
||||||
|
|
||||||
public progress: BehaviorSubject<{ [key: string]: JobProgressDTO }>;
|
public progress: BehaviorSubject<{ [key: string]: JobProgressDTO }>;
|
||||||
public lastRuns: BehaviorSubject<{ [key: string]: JobProgressDTO }>;
|
|
||||||
public onJobFinish: EventEmitter<string> = new EventEmitter<string>();
|
public onJobFinish: EventEmitter<string> = new EventEmitter<string>();
|
||||||
timer: number = null;
|
timer: number = null;
|
||||||
|
public jobStartingStopping: { [key: string]: boolean } = {};
|
||||||
private subscribers = 0;
|
private subscribers = 0;
|
||||||
|
|
||||||
constructor(private _networkService: NetworkService) {
|
constructor(private _networkService: NetworkService) {
|
||||||
this.progress = new BehaviorSubject({});
|
this.progress = new BehaviorSubject({});
|
||||||
this.lastRuns = new BehaviorSubject({});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getProgress(schedule: JobScheduleDTO): JobProgressDTO {
|
getProgress(schedule: JobScheduleDTO): JobProgressDTO {
|
||||||
return this.progress.value[JobDTO.getHashName(schedule.jobName, schedule.config)];
|
return this.progress.value[JobDTO.getHashName(schedule.jobName, schedule.config)];
|
||||||
}
|
}
|
||||||
|
|
||||||
getLastRun(schedule: JobScheduleDTO): JobProgressDTO {
|
|
||||||
return this.lastRuns.value[JobDTO.getHashName(schedule.jobName, schedule.config)];
|
|
||||||
}
|
|
||||||
|
|
||||||
subscribeToProgress(): void {
|
subscribeToProgress(): void {
|
||||||
this.incSubscribers();
|
this.incSubscribers();
|
||||||
}
|
}
|
||||||
@ -40,20 +35,24 @@ export class ScheduledJobsService {
|
|||||||
return await this.loadProgress();
|
return await this.loadProgress();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async start(id: string, config?: any): Promise<void> {
|
public async start(jobName: string, config?: any, soloStart: boolean = false): Promise<void> {
|
||||||
await this._networkService.postJson('/admin/jobs/scheduled/' + id + '/start', {config: config});
|
this.jobStartingStopping[jobName] = true;
|
||||||
|
await this._networkService.postJson('/admin/jobs/scheduled/' + jobName + '/' + (soloStart === true ? 'soloStart' : 'start'),
|
||||||
|
{config: config});
|
||||||
|
delete this.jobStartingStopping[jobName];
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async stop(id: string): Promise<void> {
|
public async stop(jobName: string): Promise<void> {
|
||||||
await this._networkService.postJson('/admin/jobs/scheduled/' + id + '/stop');
|
this.jobStartingStopping[jobName] = true;
|
||||||
|
await this._networkService.postJson('/admin/jobs/scheduled/' + jobName + '/stop');
|
||||||
|
delete this.jobStartingStopping[jobName];
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async loadProgress(): Promise<void> {
|
protected async loadProgress(): Promise<void> {
|
||||||
const prevPrg = this.progress.value;
|
const prevPrg = this.progress.value;
|
||||||
this.progress.next(await this._networkService.getJson<{ [key: string]: JobProgressDTO }>('/admin/jobs/scheduled/progress'));
|
this.progress.next(await this._networkService.getJson<{ [key: string]: JobProgressDTO }>('/admin/jobs/scheduled/progress'));
|
||||||
this.lastRuns.next(await this._networkService.getJson<{ [key: string]: JobProgressDTO }>('/admin/jobs/scheduled/lastRun'));
|
|
||||||
for (const prg in prevPrg) {
|
for (const prg in prevPrg) {
|
||||||
if (!this.progress.value.hasOwnProperty(prg)) {
|
if (!this.progress.value.hasOwnProperty(prg)) {
|
||||||
this.onJobFinish.emit(prg);
|
this.onJobFinish.emit(prg);
|
||||||
|
@ -74,20 +74,11 @@
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
|
||||||
<button class="btn btn-success float-left ml-0"
|
<app-settings-job-button class="float-left"
|
||||||
*ngIf="Progress == null"
|
[soloRun]="true"
|
||||||
[disabled]="inProgress"
|
(error)="error=$event"
|
||||||
title="Generates all thumbnails now"
|
[jobName]="jobName"
|
||||||
i18n-title
|
[config]="Config"></app-settings-job-button>
|
||||||
(click)="startJob()">
|
|
||||||
<ng-container i18n>Generate thumbnails now</ng-container>
|
|
||||||
<span class="oi oi-media-play ml-2"></span>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-secondary float-left ml-0"
|
|
||||||
*ngIf="Progress != null"
|
|
||||||
[disabled]="inProgress || Progress.state !== JobProgressStates.running"
|
|
||||||
(click)="cancelJob()" i18n>Cancel thumbnail generation
|
|
||||||
</button>
|
|
||||||
|
|
||||||
|
|
||||||
<ng-container *ngIf="Progress != null">
|
<ng-container *ngIf="Progress != null">
|
||||||
|
@ -8,7 +8,6 @@ import {ThumbnailSettingsService} from './thumbnail.settings.service';
|
|||||||
import {I18n} from '@ngx-translate/i18n-polyfill';
|
import {I18n} from '@ngx-translate/i18n-polyfill';
|
||||||
import {ServerConfig} from '../../../../../common/config/private/IPrivateConfig';
|
import {ServerConfig} from '../../../../../common/config/private/IPrivateConfig';
|
||||||
import {DefaultsJobs, JobDTO} from '../../../../../common/entities/job/JobDTO';
|
import {DefaultsJobs, JobDTO} from '../../../../../common/entities/job/JobDTO';
|
||||||
import {ErrorDTO} from '../../../../../common/entities/Error';
|
|
||||||
import {ScheduledJobsService} from '../scheduled-jobs.service';
|
import {ScheduledJobsService} from '../scheduled-jobs.service';
|
||||||
import {JobProgressStates} from '../../../../../common/entities/job/JobProgressDTO';
|
import {JobProgressStates} from '../../../../../common/entities/job/JobProgressDTO';
|
||||||
|
|
||||||
@ -23,6 +22,7 @@ export class ThumbnailSettingsComponent
|
|||||||
extends SettingsComponent<{ server: ServerConfig.ThumbnailConfig, client: ClientConfig.ThumbnailConfig }>
|
extends SettingsComponent<{ server: ServerConfig.ThumbnailConfig, client: ClientConfig.ThumbnailConfig }>
|
||||||
implements OnInit {
|
implements OnInit {
|
||||||
JobProgressStates = JobProgressStates;
|
JobProgressStates = JobProgressStates;
|
||||||
|
readonly jobName = DefaultsJobs[DefaultsJobs['Thumbnail Generation']];
|
||||||
|
|
||||||
constructor(_authService: AuthenticationService,
|
constructor(_authService: AuthenticationService,
|
||||||
_navigation: NavigationService,
|
_navigation: NavigationService,
|
||||||
@ -36,6 +36,10 @@ export class ThumbnailSettingsComponent
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get Config(): any {
|
||||||
|
return {sizes: this.original.client.thumbnailSizes[0]};
|
||||||
|
}
|
||||||
|
|
||||||
get ThumbnailSizes(): string {
|
get ThumbnailSizes(): string {
|
||||||
return this.settings.client.thumbnailSizes.join('; ');
|
return this.settings.client.thumbnailSizes.join('; ');
|
||||||
}
|
}
|
||||||
@ -49,52 +53,12 @@ export class ThumbnailSettingsComponent
|
|||||||
}
|
}
|
||||||
|
|
||||||
get Progress() {
|
get Progress() {
|
||||||
return this.jobsService.progress.value[JobDTO.getHashName(DefaultsJobs[DefaultsJobs['Thumbnail Generation']],
|
return this.jobsService.progress.value[JobDTO.getHashName(this.jobName, this.Config)];
|
||||||
{sizes: this.original.client.thumbnailSizes[0]})];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
super.ngOnInit();
|
super.ngOnInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
async startJob() {
|
|
||||||
this.inProgress = true;
|
|
||||||
this.error = '';
|
|
||||||
try {
|
|
||||||
await this.jobsService.start(DefaultsJobs[DefaultsJobs['Thumbnail Generation']], {sizes: this.original.client.thumbnailSizes[0]});
|
|
||||||
this.notification.info(this.i18n('Thumbnail generation started'));
|
|
||||||
this.inProgress = false;
|
|
||||||
return true;
|
|
||||||
} catch (err) {
|
|
||||||
console.log(err);
|
|
||||||
if (err.message) {
|
|
||||||
this.error = (<ErrorDTO>err).message;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.inProgress = false;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
async cancelJob() {
|
|
||||||
this.inProgress = true;
|
|
||||||
this.error = '';
|
|
||||||
try {
|
|
||||||
await this.jobsService.stop(DefaultsJobs[DefaultsJobs['Thumbnail Generation']]);
|
|
||||||
this.notification.info(this.i18n('Thumbnail generation interrupted'));
|
|
||||||
this.inProgress = false;
|
|
||||||
return true;
|
|
||||||
} catch (err) {
|
|
||||||
console.log(err);
|
|
||||||
if (err.message) {
|
|
||||||
this.error = (<ErrorDTO>err).message;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.inProgress = false;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -115,6 +115,9 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<button class="btn btn-success float-right"
|
<button class="btn btn-success float-right"
|
||||||
[disabled]="!settingsForm.form.valid || !changed || inProgress"
|
[disabled]="!settingsForm.form.valid || !changed || inProgress"
|
||||||
(click)="save()" i18n>Save
|
(click)="save()" i18n>Save
|
||||||
@ -124,22 +127,10 @@
|
|||||||
(click)="reset()" i18n>Reset
|
(click)="reset()" i18n>Reset
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<app-settings-job-button class="float-left"
|
||||||
<button class="btn btn-success float-left ml-0"
|
[soloRun]="true"
|
||||||
*ngIf="Progress == null"
|
(error)="error=$event"
|
||||||
[disabled]="inProgress"
|
[jobName]="jobName"></app-settings-job-button>
|
||||||
title="Indexes the folders"
|
|
||||||
i18n-title
|
|
||||||
(click)="transcode()">
|
|
||||||
<ng-container i18n>Transcode videos now</ng-container>
|
|
||||||
<span class="oi oi-media-play ml-2"></span>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-secondary float-left ml-0"
|
|
||||||
*ngIf="Progress != null"
|
|
||||||
[disabled]="inProgress || Progress.state !== JobProgressStates.running"
|
|
||||||
(click)="cancelTranscoding()" i18n>Cancel transcoding
|
|
||||||
</button>
|
|
||||||
|
|
||||||
|
|
||||||
<ng-container *ngIf="Progress != null">
|
<ng-container *ngIf="Progress != null">
|
||||||
<br/>
|
<br/>
|
||||||
|
@ -8,7 +8,6 @@ import {ClientConfig} from '../../../../../common/config/public/ConfigClass';
|
|||||||
import {I18n} from '@ngx-translate/i18n-polyfill';
|
import {I18n} from '@ngx-translate/i18n-polyfill';
|
||||||
import {ScheduledJobsService} from '../scheduled-jobs.service';
|
import {ScheduledJobsService} from '../scheduled-jobs.service';
|
||||||
import {DefaultsJobs, JobDTO} from '../../../../../common/entities/job/JobDTO';
|
import {DefaultsJobs, JobDTO} from '../../../../../common/entities/job/JobDTO';
|
||||||
import {ErrorDTO} from '../../../../../common/entities/Error';
|
|
||||||
import {ServerConfig} from '../../../../../common/config/private/IPrivateConfig';
|
import {ServerConfig} from '../../../../../common/config/private/IPrivateConfig';
|
||||||
import {JobProgressStates} from '../../../../../common/entities/job/JobProgressDTO';
|
import {JobProgressStates} from '../../../../../common/entities/job/JobProgressDTO';
|
||||||
|
|
||||||
@ -28,6 +27,7 @@ export class VideoSettingsComponent extends SettingsComponent<{ server: ServerCo
|
|||||||
fps = [24, 25, 30, 48, 50, 60];
|
fps = [24, 25, 30, 48, 50, 60];
|
||||||
|
|
||||||
JobProgressStates = JobProgressStates;
|
JobProgressStates = JobProgressStates;
|
||||||
|
readonly jobName = DefaultsJobs[DefaultsJobs['Video Converting']];
|
||||||
|
|
||||||
constructor(_authService: AuthenticationService,
|
constructor(_authService: AuthenticationService,
|
||||||
_navigation: NavigationService,
|
_navigation: NavigationService,
|
||||||
@ -92,43 +92,6 @@ export class VideoSettingsComponent extends SettingsComponent<{ server: ServerCo
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async transcode() {
|
|
||||||
this.inProgress = true;
|
|
||||||
this.error = '';
|
|
||||||
try {
|
|
||||||
await this.jobsService.start(DefaultsJobs[DefaultsJobs['Video Converting']]);
|
|
||||||
this.notification.info(this.i18n('Video transcoding started'));
|
|
||||||
this.inProgress = false;
|
|
||||||
return true;
|
|
||||||
} catch (err) {
|
|
||||||
console.log(err);
|
|
||||||
if (err.message) {
|
|
||||||
this.error = (<ErrorDTO>err).message;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.inProgress = false;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
async cancelTranscoding() {
|
|
||||||
this.inProgress = true;
|
|
||||||
this.error = '';
|
|
||||||
try {
|
|
||||||
await this.jobsService.stop(DefaultsJobs[DefaultsJobs['Video Converting']]);
|
|
||||||
this.notification.info(this.i18n('Video transcoding interrupted'));
|
|
||||||
this.inProgress = false;
|
|
||||||
return true;
|
|
||||||
} catch (err) {
|
|
||||||
console.log(err);
|
|
||||||
if (err.message) {
|
|
||||||
this.error = (<ErrorDTO>err).message;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.inProgress = false;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user