mirror of
https://github.com/immich-app/immich.git
synced 2025-12-15 17:23:15 +03:00
269 lines
8.0 KiB
TypeScript
269 lines
8.0 KiB
TypeScript
import { goto } from '$app/navigation';
|
|
import { AppRoute } from '$lib/constants';
|
|
import { eventManager } from '$lib/managers/event-manager.svelte';
|
|
import { queueManager } from '$lib/managers/queue-manager.svelte';
|
|
import JobCreateModal from '$lib/modals/JobCreateModal.svelte';
|
|
import type { HeaderButtonActionItem } from '$lib/types';
|
|
import { handleError } from '$lib/utils/handle-error';
|
|
import { getFormatter } from '$lib/utils/i18n';
|
|
import {
|
|
emptyQueue,
|
|
getQueue,
|
|
QueueCommand,
|
|
QueueName,
|
|
runQueueCommandLegacy,
|
|
updateQueue,
|
|
type QueueResponseDto,
|
|
} from '@immich/sdk';
|
|
import { modalManager, toastManager, type ActionItem, type IconLike } from '@immich/ui';
|
|
import {
|
|
mdiClose,
|
|
mdiCog,
|
|
mdiContentDuplicate,
|
|
mdiDatabaseOutline,
|
|
mdiFaceRecognition,
|
|
mdiFileJpgBox,
|
|
mdiFileXmlBox,
|
|
mdiFolderMove,
|
|
mdiImageSearch,
|
|
mdiLibraryShelves,
|
|
mdiOcr,
|
|
mdiPause,
|
|
mdiPlay,
|
|
mdiPlus,
|
|
mdiStateMachine,
|
|
mdiTable,
|
|
mdiTagFaces,
|
|
mdiTrashCanOutline,
|
|
mdiTrayFull,
|
|
mdiVideo,
|
|
} from '@mdi/js';
|
|
import type { MessageFormatter } from 'svelte-i18n';
|
|
|
|
type QueueItem = {
|
|
icon: IconLike;
|
|
title: string;
|
|
subtitle?: string;
|
|
};
|
|
|
|
export const getQueuesActions = ($t: MessageFormatter, queues: QueueResponseDto[] | undefined) => {
|
|
const pausedQueues = (queues ?? []).filter(({ isPaused }) => isPaused).map(({ name }) => name);
|
|
|
|
const ResumePaused: HeaderButtonActionItem = {
|
|
title: $t('resume_paused_jobs', { values: { count: pausedQueues.length } }),
|
|
$if: () => pausedQueues.length > 0,
|
|
icon: mdiPlay,
|
|
onAction: () => handleResumePausedJobs(pausedQueues),
|
|
data: {
|
|
title: pausedQueues.join(', '),
|
|
},
|
|
};
|
|
|
|
const CreateJob: ActionItem = {
|
|
icon: mdiPlus,
|
|
title: $t('admin.create_job'),
|
|
type: $t('command'),
|
|
shortcuts: { shift: true, key: 'n' },
|
|
onAction: async () => {
|
|
await modalManager.show(JobCreateModal, {});
|
|
},
|
|
};
|
|
|
|
const ManageConcurrency: ActionItem = {
|
|
icon: mdiCog,
|
|
title: $t('admin.manage_concurrency'),
|
|
description: $t('admin.manage_concurrency_description'),
|
|
type: $t('page'),
|
|
onAction: () => goto(`${AppRoute.ADMIN_SETTINGS}?isOpen=job`),
|
|
};
|
|
|
|
return { ResumePaused, ManageConcurrency, CreateJob };
|
|
};
|
|
|
|
export const getQueueActions = ($t: MessageFormatter, queue: QueueResponseDto) => {
|
|
const Pause: ActionItem = {
|
|
icon: mdiPause,
|
|
title: $t('pause'),
|
|
$if: () => !queue.isPaused,
|
|
onAction: () => handlePauseQueue(queue),
|
|
};
|
|
|
|
const Resume: ActionItem = {
|
|
icon: mdiPlay,
|
|
title: $t('resume'),
|
|
$if: () => queue.isPaused,
|
|
onAction: () => handleResumeQueue(queue),
|
|
};
|
|
|
|
const Empty: ActionItem = {
|
|
icon: mdiClose,
|
|
title: $t('clear'),
|
|
onAction: () => handleEmptyQueue(queue),
|
|
};
|
|
|
|
const RemoveFailedJobs: ActionItem = {
|
|
icon: mdiTrashCanOutline,
|
|
color: 'danger',
|
|
title: $t('admin.remove_failed_jobs'),
|
|
onAction: () => handleRemoveFailedJobs(queue),
|
|
};
|
|
|
|
return { Pause, Resume, Empty, RemoveFailedJobs };
|
|
};
|
|
|
|
export const handlePauseQueue = async (queue: QueueResponseDto) => {
|
|
const response = await updateQueue({ name: queue.name, queueUpdateDto: { isPaused: true } });
|
|
eventManager.emit('QueueUpdate', response);
|
|
};
|
|
|
|
export const handleResumeQueue = async (queue: QueueResponseDto) => {
|
|
const response = await updateQueue({ name: queue.name, queueUpdateDto: { isPaused: false } });
|
|
eventManager.emit('QueueUpdate', response);
|
|
};
|
|
|
|
export const handleEmptyQueue = async (queue: QueueResponseDto) => {
|
|
const $t = await getFormatter();
|
|
const item = asQueueItem($t, queue);
|
|
|
|
try {
|
|
await emptyQueue({ name: queue.name, queueDeleteDto: { failed: false } });
|
|
const response = await getQueue({ name: queue.name });
|
|
eventManager.emit('QueueUpdate', response);
|
|
toastManager.success($t('admin.cleared_jobs', { values: { job: item.title } }));
|
|
} catch (error) {
|
|
handleError(error, $t('errors.something_went_wrong'));
|
|
}
|
|
};
|
|
|
|
const handleResumePausedJobs = async (queues: QueueName[]) => {
|
|
const $t = await getFormatter();
|
|
|
|
try {
|
|
for (const name of queues) {
|
|
await runQueueCommandLegacy({ name, queueCommandDto: { command: QueueCommand.Resume, force: false } });
|
|
}
|
|
await queueManager.refresh();
|
|
} catch (error) {
|
|
handleError(error, $t('admin.failed_job_command', { values: { command: 'resume', job: 'paused jobs' } }));
|
|
}
|
|
};
|
|
|
|
const handleRemoveFailedJobs = async (queue: QueueResponseDto) => {
|
|
const $t = await getFormatter();
|
|
|
|
try {
|
|
await emptyQueue({ name: queue.name, queueDeleteDto: { failed: true } });
|
|
const response = await getQueue({ name: queue.name });
|
|
eventManager.emit('QueueUpdate', response);
|
|
toastManager.success();
|
|
} catch (error) {
|
|
handleError(error, $t('errors.something_went_wrong'));
|
|
}
|
|
};
|
|
|
|
export const asQueueItem = ($t: MessageFormatter, queue: { name: QueueName }): QueueItem => {
|
|
// TODO merge this mapping with data from QueuePanel.svelte
|
|
const items: Record<QueueName, QueueItem> = {
|
|
[QueueName.ThumbnailGeneration]: {
|
|
icon: mdiFileJpgBox,
|
|
title: $t('admin.thumbnail_generation_job'),
|
|
subtitle: $t('admin.thumbnail_generation_job_description'),
|
|
},
|
|
[QueueName.MetadataExtraction]: {
|
|
icon: mdiTable,
|
|
title: $t('admin.metadata_extraction_job'),
|
|
subtitle: $t('admin.metadata_extraction_job_description'),
|
|
},
|
|
[QueueName.Library]: {
|
|
icon: mdiLibraryShelves,
|
|
title: $t('external_libraries'),
|
|
subtitle: $t('admin.library_tasks_description'),
|
|
},
|
|
[QueueName.Sidecar]: {
|
|
title: $t('admin.sidecar_job'),
|
|
icon: mdiFileXmlBox,
|
|
subtitle: $t('admin.sidecar_job_description'),
|
|
},
|
|
[QueueName.SmartSearch]: {
|
|
icon: mdiImageSearch,
|
|
title: $t('admin.machine_learning_smart_search'),
|
|
subtitle: $t('admin.smart_search_job_description'),
|
|
},
|
|
[QueueName.DuplicateDetection]: {
|
|
icon: mdiContentDuplicate,
|
|
title: $t('admin.machine_learning_duplicate_detection'),
|
|
subtitle: $t('admin.duplicate_detection_job_description'),
|
|
},
|
|
[QueueName.FaceDetection]: {
|
|
icon: mdiFaceRecognition,
|
|
title: $t('admin.face_detection'),
|
|
subtitle: $t('admin.face_detection_description'),
|
|
},
|
|
[QueueName.FacialRecognition]: {
|
|
icon: mdiTagFaces,
|
|
title: $t('admin.machine_learning_facial_recognition'),
|
|
subtitle: $t('admin.facial_recognition_job_description'),
|
|
},
|
|
[QueueName.Ocr]: {
|
|
icon: mdiOcr,
|
|
title: $t('admin.machine_learning_ocr'),
|
|
subtitle: $t('admin.ocr_job_description'),
|
|
},
|
|
[QueueName.VideoConversion]: {
|
|
icon: mdiVideo,
|
|
title: $t('admin.video_conversion_job'),
|
|
subtitle: $t('admin.video_conversion_job_description'),
|
|
},
|
|
[QueueName.StorageTemplateMigration]: {
|
|
icon: mdiFolderMove,
|
|
title: $t('admin.storage_template_migration'),
|
|
},
|
|
[QueueName.Migration]: {
|
|
icon: mdiFolderMove,
|
|
title: $t('admin.migration_job'),
|
|
subtitle: $t('admin.migration_job_description'),
|
|
},
|
|
[QueueName.BackgroundTask]: {
|
|
icon: mdiTrayFull,
|
|
title: $t('admin.background_task_job'),
|
|
},
|
|
[QueueName.Search]: {
|
|
icon: '',
|
|
title: $t('search'),
|
|
},
|
|
[QueueName.Notifications]: {
|
|
icon: '',
|
|
title: $t('notifications'),
|
|
},
|
|
[QueueName.BackupDatabase]: {
|
|
icon: mdiDatabaseOutline,
|
|
title: $t('admin.backup_database'),
|
|
},
|
|
[QueueName.Workflow]: {
|
|
icon: mdiStateMachine,
|
|
title: $t('workflow'),
|
|
},
|
|
};
|
|
|
|
return items[queue.name];
|
|
};
|
|
|
|
export const asQueueSlug = (name: QueueName) => {
|
|
return name.replaceAll(/[A-Z]/g, (m) => '-' + m.toLowerCase());
|
|
};
|
|
|
|
export const fromQueueSlug = (slug: string): QueueName | undefined => {
|
|
const name = slug.replaceAll(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
if (Object.values(QueueName).includes(name as QueueName)) {
|
|
return name as QueueName;
|
|
}
|
|
};
|
|
|
|
export const getQueueDetailUrl = (queue: QueueResponseDto) => {
|
|
return `${AppRoute.ADMIN_QUEUES}/${asQueueSlug(queue.name)}`;
|
|
};
|
|
|
|
export const handleViewQueue = (queue: QueueResponseDto) => {
|
|
return goto(getQueueDetailUrl(queue));
|
|
};
|