mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-03-03 12:10:11 +00:00
feat: add job output preview helper
This commit is contained in:
@@ -180,6 +180,87 @@ describe('jobOutputCache', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('getPreviewableOutputsFromJobDetail', () => {
|
||||
it('returns empty array when job detail or outputs are missing', async () => {
|
||||
const { getPreviewableOutputsFromJobDetail } =
|
||||
await import('@/services/jobOutputCache')
|
||||
|
||||
expect(getPreviewableOutputsFromJobDetail(undefined)).toEqual([])
|
||||
|
||||
const jobDetail: JobDetail = {
|
||||
id: 'job-empty',
|
||||
status: 'completed',
|
||||
create_time: Date.now(),
|
||||
priority: 0
|
||||
}
|
||||
|
||||
expect(getPreviewableOutputsFromJobDetail(jobDetail)).toEqual([])
|
||||
})
|
||||
|
||||
it('maps previewable outputs and skips animated/text entries', async () => {
|
||||
const { getPreviewableOutputsFromJobDetail } =
|
||||
await import('@/services/jobOutputCache')
|
||||
const jobDetail: JobDetail = {
|
||||
id: 'job-previewable',
|
||||
status: 'completed',
|
||||
create_time: Date.now(),
|
||||
priority: 0,
|
||||
outputs: {
|
||||
'node-1': {
|
||||
images: [
|
||||
{ filename: 'image.png', subfolder: '', type: 'output' },
|
||||
{ filename: 'image.webp', subfolder: '', type: 'temp' }
|
||||
],
|
||||
animated: [true],
|
||||
text: 'hello'
|
||||
},
|
||||
'node-2': {
|
||||
video: [{ filename: 'clip.mp4', subfolder: '', type: 'output' }],
|
||||
audio: [{ filename: 'sound.mp3', subfolder: '', type: 'output' }]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const result = getPreviewableOutputsFromJobDetail(jobDetail)
|
||||
|
||||
expect(result).toHaveLength(4)
|
||||
expect(result.map((item) => item.filename).sort()).toEqual(
|
||||
['image.png', 'image.webp', 'clip.mp4', 'sound.mp3'].sort()
|
||||
)
|
||||
|
||||
const image = result.find((item) => item.filename === 'image.png')
|
||||
const video = result.find((item) => item.filename === 'clip.mp4')
|
||||
|
||||
expect(image).toBeInstanceOf(ResultItemImpl)
|
||||
expect(image?.nodeId).toBe('node-1')
|
||||
expect(image?.mediaType).toBe('images')
|
||||
expect(video?.nodeId).toBe('node-2')
|
||||
expect(video?.mediaType).toBe('video')
|
||||
})
|
||||
|
||||
it('filters non-previewable outputs and non-object items', async () => {
|
||||
const { getPreviewableOutputsFromJobDetail } =
|
||||
await import('@/services/jobOutputCache')
|
||||
const jobDetail: JobDetail = {
|
||||
id: 'job-filter',
|
||||
status: 'completed',
|
||||
create_time: Date.now(),
|
||||
priority: 0,
|
||||
outputs: {
|
||||
'node-3': {
|
||||
images: [{ filename: 'valid.png', subfolder: '', type: 'output' }],
|
||||
text: ['not-object'],
|
||||
unknown: [{ filename: 'data.bin', subfolder: '', type: 'output' }]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const result = getPreviewableOutputsFromJobDetail(jobDetail)
|
||||
|
||||
expect(result.map((item) => item.filename)).toEqual(['valid.png'])
|
||||
})
|
||||
})
|
||||
|
||||
describe('getJobDetail', () => {
|
||||
it('fetches and caches job detail', async () => {
|
||||
const { getJobDetail } = await import('@/services/jobOutputCache')
|
||||
|
||||
@@ -11,6 +11,7 @@ import QuickLRU from '@alloc/quick-lru'
|
||||
import type { JobDetail } from '@/platform/remote/comfyui/jobs/jobTypes'
|
||||
import { extractWorkflow } from '@/platform/remote/comfyui/jobs/fetchJobs'
|
||||
import type { ComfyWorkflowJSON } from '@/platform/workflow/validation/schemas/workflowSchema'
|
||||
import type { ResultItem, TaskOutput } from '@/schemas/apiSchema'
|
||||
import { api } from '@/scripts/api'
|
||||
import { ResultItemImpl } from '@/stores/queueStore'
|
||||
import type { TaskItemImpl } from '@/stores/queueStore'
|
||||
@@ -75,6 +76,40 @@ export async function getOutputsForTask(
|
||||
}
|
||||
}
|
||||
|
||||
function getPreviewableOutputs(outputs?: TaskOutput): ResultItemImpl[] {
|
||||
if (!outputs) return []
|
||||
const resultItems = Object.entries(outputs).flatMap(([nodeId, nodeOutputs]) =>
|
||||
Object.entries(nodeOutputs)
|
||||
.filter(([mediaType, items]) => mediaType !== 'animated' && items)
|
||||
.flatMap(([mediaType, items]) => {
|
||||
if (!Array.isArray(items)) {
|
||||
return []
|
||||
}
|
||||
|
||||
return items.filter(isResultItem).map(
|
||||
(item) =>
|
||||
new ResultItemImpl({
|
||||
...item,
|
||||
nodeId,
|
||||
mediaType
|
||||
})
|
||||
)
|
||||
})
|
||||
)
|
||||
|
||||
return ResultItemImpl.filterPreviewable(resultItems)
|
||||
}
|
||||
|
||||
function isResultItem(item: unknown): item is ResultItem {
|
||||
return typeof item === 'object' && item !== null
|
||||
}
|
||||
|
||||
export function getPreviewableOutputsFromJobDetail(
|
||||
jobDetail?: JobDetail
|
||||
): ResultItemImpl[] {
|
||||
return getPreviewableOutputs(jobDetail?.outputs)
|
||||
}
|
||||
|
||||
// ===== Job Detail Caching =====
|
||||
|
||||
export async function getJobDetail(
|
||||
|
||||
Reference in New Issue
Block a user