mirror of
https://github.com/immich-app/immich
synced 2025-11-07 17:27:20 +00:00
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:
parent
9edc9d6151
commit
2c87683fd4
21 changed files with 409 additions and 152 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()}
|
||||
|
|
|
|||
|
|
@ -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)}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue