feat(server): refresh face detection (#12335)

* refresh faces

handle non-ml faces

* fix metadata face handling

* updated tests

* added todo comment
This commit is contained in:
Mert 2024-10-03 21:58:28 -04:00 committed by GitHub
parent 9edc9d6151
commit 2c87683fd4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 409 additions and 152 deletions

View file

@ -1,5 +1,5 @@
<script lang="ts" context="module">
export type Colors = 'light-gray' | 'gray';
export type Colors = 'light-gray' | 'gray' | 'dark-gray';
</script>
<script lang="ts">
@ -7,8 +7,9 @@
export let disabled = false;
const colorClasses: Record<Colors, string> = {
'light-gray': 'bg-gray-300/90 dark:bg-gray-600/90',
gray: 'bg-gray-300 dark:bg-gray-600',
'light-gray': 'bg-gray-300/80 dark:bg-gray-700',
gray: 'bg-gray-300/90 dark:bg-gray-700/90',
'dark-gray': 'bg-gray-300 dark:bg-gray-700/80',
};
const hoverClasses = disabled

View file

@ -9,6 +9,7 @@
mdiAllInclusive,
mdiClose,
mdiFastForward,
mdiImageRefreshOutline,
mdiPause,
mdiPlay,
mdiSelectionSearch,
@ -23,16 +24,17 @@
export let description: ComponentType | undefined;
export let jobCounts: JobCountsDto;
export let queueStatus: QueueStatusDto;
export let allowForceCommand = true;
export let icon: string;
export let disabled = false;
export let allText: string;
export let allText: string | undefined;
export let refreshText: string | undefined;
export let missingText: string;
export let onCommand: (command: JobCommandDto) => void;
$: waitingCount = jobCounts.waiting + jobCounts.paused + jobCounts.delayed;
$: isIdle = !queueStatus.isActive && !queueStatus.isPaused;
$: multipleButtons = allText || refreshText;
const commonClasses = 'flex place-items-center justify-between w-full py-2 sm:py-4 pr-4 pl-6';
</script>
@ -121,7 +123,9 @@
<Icon path={mdiAlertCircle} size="36" />
{$t('disabled').toUpperCase()}
</JobTileButton>
{:else if !isIdle}
{/if}
{#if !disabled && !isIdle}
{#if waitingCount > 0}
<JobTileButton color="gray" on:click={() => onCommand({ command: JobCommand.Empty, force: false })}>
<Icon path={mdiClose} size="24" />
@ -141,16 +145,28 @@
{$t('pause').toUpperCase()}
</JobTileButton>
{/if}
{:else if allowForceCommand}
<JobTileButton color="gray" on:click={() => onCommand({ command: JobCommand.Start, force: true })}>
<Icon path={mdiAllInclusive} size="24" />
{allText}
</JobTileButton>
{/if}
{#if !disabled && multipleButtons && isIdle}
{#if allText}
<JobTileButton color="dark-gray" on:click={() => onCommand({ command: JobCommand.Start, force: true })}>
<Icon path={mdiAllInclusive} size="24" />
{allText}
</JobTileButton>
{/if}
{#if refreshText}
<JobTileButton color="gray" on:click={() => onCommand({ command: JobCommand.Start, force: undefined })}>
<Icon path={mdiImageRefreshOutline} size="24" />
{refreshText}
</JobTileButton>
{/if}
<JobTileButton color="light-gray" on:click={() => onCommand({ command: JobCommand.Start, force: false })}>
<Icon path={mdiSelectionSearch} size="24" />
{missingText}
</JobTileButton>
{:else}
{/if}
{#if !disabled && !multipleButtons && isIdle}
<JobTileButton color="light-gray" on:click={() => onCommand({ command: JobCommand.Start, force: false })}>
<Icon path={mdiPlay} size="48" />
{$t('start').toUpperCase()}

View file

@ -32,10 +32,10 @@
subtitle?: string;
description?: ComponentType;
allText?: string;
missingText?: string;
refreshText?: string;
missingText: string;
disabled?: boolean;
icon: string;
allowForceCommand?: boolean;
handleCommand?: (jobId: JobName, jobCommand: JobCommandDto) => Promise<void>;
}
@ -61,43 +61,54 @@
icon: mdiFileJpgBox,
title: $getJobName(JobName.ThumbnailGeneration),
subtitle: $t('admin.thumbnail_generation_job_description'),
allText: $t('all'),
missingText: $t('missing'),
},
[JobName.MetadataExtraction]: {
icon: mdiTable,
title: $getJobName(JobName.MetadataExtraction),
subtitle: $t('admin.metadata_extraction_job_description'),
allText: $t('all'),
missingText: $t('missing'),
},
[JobName.Library]: {
icon: mdiLibraryShelves,
title: $getJobName(JobName.Library),
subtitle: $t('admin.library_tasks_description'),
allText: $t('all').toUpperCase(),
missingText: $t('refresh').toUpperCase(),
allText: $t('all'),
missingText: $t('refresh'),
},
[JobName.Sidecar]: {
title: $getJobName(JobName.Sidecar),
icon: mdiFileXmlBox,
subtitle: $t('admin.sidecar_job_description'),
allText: $t('sync').toUpperCase(),
missingText: $t('discover').toUpperCase(),
allText: $t('sync'),
missingText: $t('discover'),
disabled: !$featureFlags.sidecar,
},
[JobName.SmartSearch]: {
icon: mdiImageSearch,
title: $getJobName(JobName.SmartSearch),
subtitle: $t('admin.smart_search_job_description'),
allText: $t('all'),
missingText: $t('missing'),
disabled: !$featureFlags.smartSearch,
},
[JobName.DuplicateDetection]: {
icon: mdiContentDuplicate,
title: $getJobName(JobName.DuplicateDetection),
subtitle: $t('admin.duplicate_detection_job_description'),
allText: $t('all'),
missingText: $t('missing'),
disabled: !$featureFlags.duplicateDetection,
},
[JobName.FaceDetection]: {
icon: mdiFaceRecognition,
title: $getJobName(JobName.FaceDetection),
subtitle: $t('admin.face_detection_description'),
allText: $t('reset'),
refreshText: $t('refresh'),
missingText: $t('missing'),
handleCommand: handleConfirmCommand,
disabled: !$featureFlags.facialRecognition,
},
@ -105,6 +116,8 @@
icon: mdiTagFaces,
title: $getJobName(JobName.FacialRecognition),
subtitle: $t('admin.facial_recognition_job_description'),
allText: $t('reset'),
missingText: $t('missing'),
handleCommand: handleConfirmCommand,
disabled: !$featureFlags.facialRecognition,
},
@ -112,18 +125,20 @@
icon: mdiVideo,
title: $getJobName(JobName.VideoConversion),
subtitle: $t('admin.video_conversion_job_description'),
allText: $t('all'),
missingText: $t('missing'),
},
[JobName.StorageTemplateMigration]: {
icon: mdiFolderMove,
title: $getJobName(JobName.StorageTemplateMigration),
allowForceCommand: false,
missingText: $t('missing'),
description: StorageMigrationDescription,
},
[JobName.Migration]: {
icon: mdiFolderMove,
title: $getJobName(JobName.Migration),
subtitle: $t('admin.migration_job_description'),
allowForceCommand: false,
missingText: $t('missing'),
},
};
$: jobList = Object.entries(jobDetails) as [JobName, JobDetails][];
@ -150,7 +165,7 @@
</script>
<div class="flex flex-col gap-7">
{#each jobList as [jobName, { title, subtitle, description, disabled, allText, missingText, allowForceCommand, icon, handleCommand: handleCommandOverride }]}
{#each jobList as [jobName, { title, subtitle, description, disabled, allText, refreshText, missingText, icon, handleCommand: handleCommandOverride }]}
{@const { jobCounts, queueStatus } = jobs[jobName]}
<JobTile
{icon}
@ -158,9 +173,9 @@
{disabled}
{subtitle}
{description}
allText={allText || $t('all').toUpperCase()}
missingText={missingText || $t('missing').toUpperCase()}
{allowForceCommand}
allText={allText?.toUpperCase()}
refreshText={refreshText?.toUpperCase()}
missingText={missingText.toUpperCase()}
{jobCounts}
{queueStatus}
onCommand={(command) => (handleCommandOverride || handleCommand)(jobName, command)}