Add job status to maintenance menu

Signed-off-by: trivernis <trivernis@protonmail.com>
pull/25/head
trivernis 2 years ago
parent 6dfefe01c2
commit 5dd8eefdcc
Signed by: Trivernis
GPG Key ID: DFFFCC2C7A02DB45

@ -1,6 +1,6 @@
[package] [package]
name = "mediarepo-api" name = "mediarepo-api"
version = "0.32.0" version = "0.32.1"
edition = "2018" edition = "2018"
license = "gpl-3" license = "gpl-3"

@ -34,4 +34,10 @@ impl JobApi {
Ok(()) Ok(())
} }
/// Checks if a particular job is already running
#[tracing::instrument(level = "debug", skip(self))]
pub async fn is_job_running(&self, job_type: JobType) -> ApiResult<bool> {
self.emit_and_get("is_job_running", job_type, None).await
}
} }

@ -9,3 +9,11 @@ pub async fn run_job(api_state: ApiAccess<'_>, job_type: JobType, sync: bool) ->
Ok(()) Ok(())
} }
#[tauri::command]
pub async fn is_job_running(api_state: ApiAccess<'_>, job_type: JobType) -> PluginResult<bool> {
let api = api_state.api().await?;
let running = api.job.is_job_running(job_type).await?;
Ok(running)
}

@ -75,7 +75,8 @@ impl<R: Runtime> MediarepoPlugin<R> {
get_file_tag_map, get_file_tag_map,
all_sorting_presets, all_sorting_presets,
add_sorting_preset, add_sorting_preset,
delete_sorting_preset delete_sorting_preset,
is_job_running
]), ]),
} }
} }

@ -20,8 +20,9 @@ impl NamespaceProvider for JobsNamespace {
fn register(handler: &mut EventHandler) { fn register(handler: &mut EventHandler) {
events!(handler, events!(handler,
"run_job" => Self::run_job "run_job" => Self::run_job,
) "is_job_running" => Self::is_job_running
);
} }
} }
@ -59,6 +60,26 @@ impl JobsNamespace {
Ok(Response::empty()) Ok(Response::empty())
} }
#[tracing::instrument(skip_all)]
pub async fn is_job_running(ctx: &Context, event: Event) -> IPCResult<Response> {
let job_type = event.payload::<JobType>()?;
let dispatcher = get_job_dispatcher_from_context(ctx).await;
let running = match job_type {
JobType::MigrateContentDescriptors => {
is_job_running::<MigrateCDsJob>(&dispatcher).await
}
JobType::CalculateSizes => is_job_running::<CalculateSizesJob>(&dispatcher).await,
JobType::GenerateThumbnails => {
is_job_running::<GenerateMissingThumbsJob>(&dispatcher).await
}
JobType::CheckIntegrity => is_job_running::<CheckIntegrityJob>(&dispatcher).await,
JobType::Vacuum => is_job_running::<VacuumJob>(&dispatcher).await,
};
Response::payload(&ctx, running)
}
} }
async fn dispatch_job<J: 'static + Job>( async fn dispatch_job<J: 'static + Job>(
@ -107,3 +128,12 @@ async fn calculate_all_sizes(ctx: &Context) -> RepoResult<()> {
Ok(()) Ok(())
} }
async fn is_job_running<T: 'static + Job>(dispatcher: &JobDispatcher) -> bool {
if let Some(handle) = dispatcher.get_handle::<T>().await {
let state = handle.state().await;
state == JobState::Running
} else {
false
}
}

@ -19,6 +19,7 @@ import {
GetSizeRequest, GetSizeRequest,
GetTagsForFilesRequest, GetTagsForFilesRequest,
InitRepositoryRequest, InitRepositoryRequest,
IsJobRunningRequest,
ReadFileRequest, ReadFileRequest,
RemoveRepositoryRequest, RemoveRepositoryRequest,
ResolvePathsToFilesRequest, ResolvePathsToFilesRequest,
@ -187,6 +188,10 @@ export class MediarepoApi {
return this.invokePlugin(ApiFunction.RunJob, request); return this.invokePlugin(ApiFunction.RunJob, request);
} }
public static async isJobRunning(request: IsJobRunningRequest): Promise<boolean> {
return this.invokePlugin(ApiFunction.IsJobRunning, request);
}
public static async getAllSortingPresets(): Promise<SortingPresetData[]> { public static async getAllSortingPresets(): Promise<SortingPresetData[]> {
return ShortCache.cached("sorting-presets", () => this.invokePlugin(ApiFunction.GetAllSortingPresets), 1000); return ShortCache.cached("sorting-presets", () => this.invokePlugin(ApiFunction.GetAllSortingPresets), 1000);
} }

@ -40,6 +40,7 @@ export enum ApiFunction {
SetFrontendState = "set_frontend_state", SetFrontendState = "set_frontend_state",
// jobs // jobs
RunJob = "run_job", RunJob = "run_job",
IsJobRunning = "is_job_running",
// presets // presets
GetAllSortingPresets = "all_sorting_presets", GetAllSortingPresets = "all_sorting_presets",
AddSortingPreset = "add_sorting_preset", AddSortingPreset = "add_sorting_preset",

@ -117,3 +117,7 @@ export type AddSortingPresetRequest = {
export type DeleteSortingPresetRequest = { export type DeleteSortingPresetRequest = {
id: number id: number
}; };
export type IsJobRunningRequest = {
jobType: JobType,
}

@ -17,7 +17,7 @@ import {MatSelectModule} from "@angular/material/select";
import {MatCheckboxModule} from "@angular/material/checkbox"; import {MatCheckboxModule} from "@angular/material/checkbox";
import {MatDividerModule} from "@angular/material/divider"; import {MatDividerModule} from "@angular/material/divider";
import {NgIconsModule} from "@ng-icons/core"; import {NgIconsModule} from "@ng-icons/core";
import {MatPlus} from "@ng-icons/material-icons/baseline"; import {MatMoreVert, MatPlus} from "@ng-icons/material-icons/baseline";
import {MatMenuModule} from "@angular/material/menu"; import {MatMenuModule} from "@angular/material/menu";
import {InputModule} from "../shared/input/input.module"; import {InputModule} from "../shared/input/input.module";
import {SidebarModule} from "../shared/sidebar/sidebar.module"; import {SidebarModule} from "../shared/sidebar/sidebar.module";
@ -72,7 +72,7 @@ import {AboutDialogComponent} from "./repositories-tab/repository-overview/about
MatProgressBarModule, MatProgressBarModule,
MatCheckboxModule, MatCheckboxModule,
ScrollingModule, ScrollingModule,
NgIconsModule.withIcons({ MatPlus }), NgIconsModule.withIcons({ MatPlus, MatMoreVert }),
FlexModule, FlexModule,
MatButtonModule, MatButtonModule,
MatMenuModule, MatMenuModule,

@ -1,4 +1,4 @@
import {ChangeDetectionStrategy, ChangeDetectorRef, Component} from "@angular/core"; import {ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit} from "@angular/core";
import {JobService} from "../../../../services/job/job.service"; import {JobService} from "../../../../services/job/job.service";
import {JobType} from "../../../../../api/api-types/job"; import {JobType} from "../../../../../api/api-types/job";
import {MatDialog} from "@angular/material/dialog"; import {MatDialog} from "@angular/material/dialog";
@ -13,12 +13,12 @@ import {BehaviorSubject} from "rxjs";
styleUrls: ["./repository-maintenance.component.scss"], styleUrls: ["./repository-maintenance.component.scss"],
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class RepositoryMaintenanceComponent { export class RepositoryMaintenanceComponent implements OnInit, OnDestroy {
public jobState: { [Property in JobType]?: boolean } = { public jobState: { [Property in JobType]?: boolean } = {
CalculateSizes: false, CalculateSizes: false,
GenerateThumbnails: false, GenerateThumbnails: false,
}; };
private jobStatusInterval: any;
constructor( constructor(
private changeDetector: ChangeDetectorRef, private changeDetector: ChangeDetectorRef,
@ -28,14 +28,23 @@ export class RepositoryMaintenanceComponent {
) { ) {
} }
public ngOnDestroy(): void {
clearInterval(this.jobStatusInterval);
}
public async ngOnInit() {
await this.updateJobStatus();
this.jobStatusInterval = setInterval(() => this.updateJobStatus(), 10000);
}
public async runJob(jobType: JobType, runAsync: boolean) { public async runJob(jobType: JobType, runAsync: boolean) {
if (runAsync) { if (runAsync) {
this.jobState[jobType] = true; this.jobState[jobType] = true;
this.jobService.runJob(jobType).then(() => this.delay(1000)).catch(this.logger.error).finally(() => { this.jobService.runJob(jobType).then(() => this.delay(1000)).catch(this.logger.error).finally(() => {
this.jobState[jobType] = false; this.jobState[jobType] = false;
this.changeDetector.detectChanges(); this.changeDetector.markForCheck();
}); });
this.changeDetector.detectChanges(); this.changeDetector.markForCheck();
} else { } else {
const dialog = this.dialog.open<BusyDialogComponent, BusyDialogData>(BusyDialogComponent, { const dialog = this.dialog.open<BusyDialogComponent, BusyDialogData>(BusyDialogComponent, {
disableClose: true, disableClose: true,
@ -48,13 +57,13 @@ export class RepositoryMaintenanceComponent {
} }
}); });
try { try {
this.changeDetector.detectChanges(); this.changeDetector.markForCheck();
await this.jobService.runJob(jobType); await this.jobService.runJob(jobType);
} catch (err: any) { } catch (err: any) {
this.logger.error(err); this.logger.error(err);
} finally { } finally {
dialog.close(); dialog.close();
this.changeDetector.detectChanges(); this.changeDetector.markForCheck();
} }
} }
} }
@ -65,4 +74,12 @@ export class RepositoryMaintenanceComponent {
ms ms
)); ));
} }
private async updateJobStatus() {
const indexedTypes: JobType[] = ["CalculateSizes", "GenerateThumbnails"];
for (const jobType of indexedTypes) {
this.jobState[jobType] = await this.jobService.isJobRunning(jobType);
}
this.changeDetector.markForCheck();
}
} }

@ -13,4 +13,8 @@ export class JobService {
public async runJob(jobType: JobType, sync: boolean = true): Promise<void> { public async runJob(jobType: JobType, sync: boolean = true): Promise<void> {
return MediarepoApi.runJob({ jobType, sync }); return MediarepoApi.runJob({ jobType, sync });
} }
public async isJobRunning(jobType: JobType): Promise<boolean> {
return MediarepoApi.isJobRunning({ jobType });
}
} }

Loading…
Cancel
Save