feat(web): pause and resume jobs (#2125)

* feat(web): pause and resume jobs

* add bg color to status instead of using badge

* styling

---------

Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
This commit is contained in:
Michel Heusschen
2023-04-01 06:53:20 +02:00
committed by GitHub
parent 23e4449f27
commit aaaf1a6cf8
7 changed files with 219 additions and 174 deletions

View File

@@ -1,115 +1,87 @@
<script lang="ts">
import {
notificationController,
NotificationType
} from '$lib/components/shared-components/notification/notification';
import { handleError } from '$lib/utils/handle-error';
import { AllJobStatusResponseDto, api, JobCommand, JobName } from '@api';
import { onDestroy, onMount } from 'svelte';
import { AllJobStatusResponseDto, api, JobCommand, JobCommandDto, JobName } from '@api';
import type { ComponentType } from 'svelte';
import JobTile from './job-tile.svelte';
import StorageMigrationDescription from './storage-migration-description.svelte';
let jobs: AllJobStatusResponseDto;
let timer: NodeJS.Timer;
export let jobs: AllJobStatusResponseDto;
const load = async () => {
const { data } = await api.jobApi.getAllJobsStatus();
jobs = data;
type JobDetails = {
title: string;
subtitle?: string;
allowForceCommand?: boolean;
component?: ComponentType;
};
onMount(async () => {
await load();
timer = setInterval(async () => await load(), 5_000);
});
onDestroy(() => {
clearInterval(timer);
});
function getJobLabel(jobName: JobName) {
const names: Record<JobName, string> = {
[JobName.ThumbnailGenerationQueue]: 'Generate Thumbnails',
[JobName.MetadataExtractionQueue]: 'Extract Metadata',
[JobName.VideoConversionQueue]: 'Transcode Videos',
[JobName.ObjectTaggingQueue]: 'Tag Objects',
[JobName.ClipEncodingQueue]: 'Clip Encoding',
[JobName.BackgroundTaskQueue]: 'Background Task',
[JobName.StorageTemplateMigrationQueue]: 'Storage Template Migration',
[JobName.SearchQueue]: 'Search'
};
return names[jobName];
}
const start = async (jobId: JobName, force: boolean) => {
const label = getJobLabel(jobId);
try {
await api.jobApi.sendJobCommand(jobId, { command: JobCommand.Start, force });
jobs[jobId].active += 1;
notificationController.show({
message: `Started job: ${label}`,
type: NotificationType.Info
});
} catch (error) {
handleError(error, `Unable to start job: ${label}`);
const jobDetails: { [Key in JobName]?: JobDetails } = {
[JobName.ThumbnailGenerationQueue]: {
title: 'Generate Thumbnails',
subtitle: 'Regenerate JPEG and WebP thumbnails'
},
[JobName.MetadataExtractionQueue]: {
title: 'Extract Metadata',
subtitle: 'Extract metadata information i.e. GPS, resolution...etc'
},
[JobName.ObjectTaggingQueue]: {
title: 'Tag Objects',
subtitle:
'Run machine learning to tag objects\nNote that some assets may not have any objects detected'
},
[JobName.ClipEncodingQueue]: {
title: 'Encode Clip',
subtitle: 'Run machine learning to generate clip embeddings'
},
[JobName.VideoConversionQueue]: {
title: 'Transcode Videos',
subtitle: 'Transcode videos not in the desired format'
},
[JobName.StorageTemplateMigrationQueue]: {
title: 'Storage Template Migration',
allowForceCommand: false,
component: StorageMigrationDescription
}
};
const jobDetailsArray = Object.entries(jobDetails) as [JobName, JobDetails][];
async function runJob(jobId: JobName, jobCommand: JobCommandDto) {
const title = jobDetails[jobId]?.title;
try {
await api.jobApi.sendJobCommand(jobId, jobCommand);
// TODO: Return actual job status from server and use that.
switch (jobCommand.command) {
case JobCommand.Start:
jobs[jobId].active += 1;
break;
case JobCommand.Resume:
jobs[jobId].active += 1;
jobs[jobId].paused = 0;
break;
case JobCommand.Pause:
jobs[jobId].paused += 1;
jobs[jobId].active = 0;
jobs[jobId].waiting = 0;
break;
}
} catch (error) {
handleError(error, `Command '${jobCommand.command}' failed for job: ${title}`);
}
}
</script>
<div class="flex flex-col gap-7">
{#if jobs}
{#each jobDetailsArray as [jobName, { title, subtitle, allowForceCommand, component }]}
<JobTile
title="Generate thumbnails"
subtitle="Regenerate JPEG and WebP thumbnails"
on:click={(e) => start(JobName.ThumbnailGenerationQueue, e.detail.force)}
jobCounts={jobs[JobName.ThumbnailGenerationQueue]}
/>
<JobTile
title="Extract Metadata"
subtitle="Extract metadata information i.e. GPS, resolution...etc"
on:click={(e) => start(JobName.MetadataExtractionQueue, e.detail.force)}
jobCounts={jobs[JobName.MetadataExtractionQueue]}
/>
<JobTile
title="Tag Objects"
subtitle="Run machine learning to tag objects"
on:click={(e) => start(JobName.ObjectTaggingQueue, e.detail.force)}
jobCounts={jobs[JobName.ObjectTaggingQueue]}
{title}
{subtitle}
{allowForceCommand}
on:command={({ detail }) => runJob(jobName, detail)}
jobCounts={jobs[jobName]}
>
Note that some assets may not have any objects detected
<svelte:component this={component} />
</JobTile>
<JobTile
title="Encode Clip"
subtitle="Run machine learning to generate clip embeddings"
on:click={(e) => start(JobName.ClipEncodingQueue, e.detail.force)}
jobCounts={jobs[JobName.ClipEncodingQueue]}
/>
<JobTile
title="Transcode Videos"
subtitle="Transcode videos not in the desired format"
on:click={(e) => start(JobName.VideoConversionQueue, e.detail.force)}
jobCounts={jobs[JobName.VideoConversionQueue]}
/>
<JobTile
title="Storage migration"
showOptions={false}
subtitle={''}
on:click={(e) => start(JobName.StorageTemplateMigrationQueue, e.detail.force)}
jobCounts={jobs[JobName.StorageTemplateMigrationQueue]}
>
Apply the current
<a
href="/admin/system-settings?open=storage-template"
class="text-immich-primary dark:text-immich-dark-primary">Storage template</a
>
to previously uploaded assets
</JobTile>
{/if}
{/each}
</div>