mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-06-05 20:54:56 +00:00
Compare commits
4 Commits
dev/remote
...
copilot/su
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
db4c97047d | ||
|
|
e618d80955 | ||
|
|
c0d0017a8b | ||
|
|
f1120b6ddb |
@@ -59,10 +59,7 @@ function mkFileUrl(props: { ref: ImageRef; preview?: boolean }): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const pathPlusQueryParams = api.apiURL(
|
const pathPlusQueryParams = api.apiURL(
|
||||||
'/view?' +
|
'/view?' + params.toString() + app.getPreviewFormatParam()
|
||||||
params.toString() +
|
|
||||||
app.getPreviewFormatParam() +
|
|
||||||
app.getRandParam()
|
|
||||||
)
|
)
|
||||||
const imageElement = new Image()
|
const imageElement = new Image()
|
||||||
imageElement.crossOrigin = 'anonymous'
|
imageElement.crossOrigin = 'anonymous'
|
||||||
|
|||||||
289
src/composables/queue/useCompletionSummary.test.ts
Normal file
289
src/composables/queue/useCompletionSummary.test.ts
Normal file
@@ -0,0 +1,289 @@
|
|||||||
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||||
|
import { nextTick, reactive } from 'vue'
|
||||||
|
|
||||||
|
import { useCompletionSummary } from '@/composables/queue/useCompletionSummary'
|
||||||
|
import { useExecutionStore } from '@/stores/executionStore'
|
||||||
|
import { useQueueStore } from '@/stores/queueStore'
|
||||||
|
|
||||||
|
type MockTask = {
|
||||||
|
displayStatus: 'Completed' | 'Failed' | 'Cancelled' | 'Running' | 'Pending'
|
||||||
|
executionEndTimestamp?: number
|
||||||
|
previewOutput?: {
|
||||||
|
isImage: boolean
|
||||||
|
url: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vi.mock('@/stores/queueStore', () => {
|
||||||
|
const state = reactive({
|
||||||
|
runningTasks: [] as MockTask[],
|
||||||
|
historyTasks: [] as MockTask[]
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
useQueueStore: () => state
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
vi.mock('@/stores/executionStore', () => {
|
||||||
|
const state = reactive({
|
||||||
|
isIdle: true
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
useExecutionStore: () => state
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('useCompletionSummary', () => {
|
||||||
|
const queueStore = () =>
|
||||||
|
useQueueStore() as {
|
||||||
|
runningTasks: MockTask[]
|
||||||
|
historyTasks: MockTask[]
|
||||||
|
}
|
||||||
|
const executionStore = () => useExecutionStore() as { isIdle: boolean }
|
||||||
|
|
||||||
|
const resetState = () => {
|
||||||
|
queueStore().runningTasks = []
|
||||||
|
queueStore().historyTasks = []
|
||||||
|
executionStore().isIdle = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const createTask = (
|
||||||
|
options: {
|
||||||
|
state?: MockTask['displayStatus']
|
||||||
|
ts?: number
|
||||||
|
previewUrl?: string
|
||||||
|
isImage?: boolean
|
||||||
|
} = {}
|
||||||
|
): MockTask => {
|
||||||
|
const {
|
||||||
|
state = 'Completed',
|
||||||
|
ts = Date.now(),
|
||||||
|
previewUrl,
|
||||||
|
isImage = true
|
||||||
|
} = options
|
||||||
|
|
||||||
|
const task: MockTask = {
|
||||||
|
displayStatus: state,
|
||||||
|
executionEndTimestamp: ts
|
||||||
|
}
|
||||||
|
|
||||||
|
if (previewUrl) {
|
||||||
|
task.previewOutput = {
|
||||||
|
isImage,
|
||||||
|
url: previewUrl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return task
|
||||||
|
}
|
||||||
|
|
||||||
|
const runBatch = async (options: {
|
||||||
|
start: number
|
||||||
|
finish: number
|
||||||
|
tasks: MockTask[]
|
||||||
|
}) => {
|
||||||
|
const { start, finish, tasks } = options
|
||||||
|
|
||||||
|
vi.setSystemTime(start)
|
||||||
|
executionStore().isIdle = false
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
vi.setSystemTime(finish)
|
||||||
|
queueStore().historyTasks = tasks
|
||||||
|
executionStore().isIdle = true
|
||||||
|
await nextTick()
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
resetState()
|
||||||
|
vi.useFakeTimers()
|
||||||
|
vi.setSystemTime(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
vi.runOnlyPendingTimers()
|
||||||
|
vi.useRealTimers()
|
||||||
|
resetState()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('summarizes the most recent batch and auto clears after the dismiss delay', async () => {
|
||||||
|
const { summary } = useCompletionSummary()
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
const start = 1_000
|
||||||
|
const finish = 2_000
|
||||||
|
|
||||||
|
const tasks = [
|
||||||
|
createTask({ ts: start - 100, previewUrl: 'ignored-old' }),
|
||||||
|
createTask({ ts: start + 10, previewUrl: 'img-1' }),
|
||||||
|
createTask({ ts: start + 20, previewUrl: 'img-2' }),
|
||||||
|
createTask({ ts: start + 30, previewUrl: 'img-3' }),
|
||||||
|
createTask({ ts: start + 40, previewUrl: 'img-4' }),
|
||||||
|
createTask({ state: 'Failed', ts: start + 50 })
|
||||||
|
]
|
||||||
|
|
||||||
|
await runBatch({ start, finish, tasks })
|
||||||
|
|
||||||
|
expect(summary.value).toEqual({
|
||||||
|
mode: 'mixed',
|
||||||
|
completedCount: 4,
|
||||||
|
failedCount: 1,
|
||||||
|
thumbnailUrls: ['img-1', 'img-2', 'img-3']
|
||||||
|
})
|
||||||
|
|
||||||
|
vi.advanceTimersByTime(6000)
|
||||||
|
await nextTick()
|
||||||
|
expect(summary.value).toBeNull()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('reports allFailed when every task in the batch failed', async () => {
|
||||||
|
const { summary } = useCompletionSummary()
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
const start = 10_000
|
||||||
|
const finish = 10_200
|
||||||
|
|
||||||
|
await runBatch({
|
||||||
|
start,
|
||||||
|
finish,
|
||||||
|
tasks: [
|
||||||
|
createTask({ state: 'Failed', ts: start + 25 }),
|
||||||
|
createTask({ state: 'Failed', ts: start + 50 })
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(summary.value).toEqual({
|
||||||
|
mode: 'allFailed',
|
||||||
|
completedCount: 0,
|
||||||
|
failedCount: 2,
|
||||||
|
thumbnailUrls: []
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('treats cancelled tasks as failures and skips non-image previews', async () => {
|
||||||
|
const { summary } = useCompletionSummary()
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
const start = 15_000
|
||||||
|
const finish = 15_200
|
||||||
|
|
||||||
|
await runBatch({
|
||||||
|
start,
|
||||||
|
finish,
|
||||||
|
tasks: [
|
||||||
|
createTask({ ts: start + 25, previewUrl: 'img-1' }),
|
||||||
|
createTask({
|
||||||
|
state: 'Cancelled',
|
||||||
|
ts: start + 50,
|
||||||
|
previewUrl: 'thumb-ignore',
|
||||||
|
isImage: false
|
||||||
|
})
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(summary.value).toEqual({
|
||||||
|
mode: 'mixed',
|
||||||
|
completedCount: 1,
|
||||||
|
failedCount: 1,
|
||||||
|
thumbnailUrls: ['img-1']
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('clearSummary dismisses the banner immediately and still tracks future batches', async () => {
|
||||||
|
const { summary, clearSummary } = useCompletionSummary()
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
await runBatch({
|
||||||
|
start: 5_000,
|
||||||
|
finish: 5_100,
|
||||||
|
tasks: [createTask({ ts: 5_050, previewUrl: 'img-1' })]
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(summary.value).toEqual({
|
||||||
|
mode: 'allSuccess',
|
||||||
|
completedCount: 1,
|
||||||
|
failedCount: 0,
|
||||||
|
thumbnailUrls: ['img-1']
|
||||||
|
})
|
||||||
|
|
||||||
|
clearSummary()
|
||||||
|
expect(summary.value).toBeNull()
|
||||||
|
|
||||||
|
await runBatch({
|
||||||
|
start: 6_000,
|
||||||
|
finish: 6_150,
|
||||||
|
tasks: [createTask({ ts: 6_075, previewUrl: 'img-2' })]
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(summary.value).toEqual({
|
||||||
|
mode: 'allSuccess',
|
||||||
|
completedCount: 1,
|
||||||
|
failedCount: 0,
|
||||||
|
thumbnailUrls: ['img-2']
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('ignores batches that have no finished tasks after the active period started', async () => {
|
||||||
|
const { summary } = useCompletionSummary()
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
const start = 20_000
|
||||||
|
const finish = 20_500
|
||||||
|
|
||||||
|
await runBatch({
|
||||||
|
start,
|
||||||
|
finish,
|
||||||
|
tasks: [createTask({ ts: start - 1, previewUrl: 'too-early' })]
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(summary.value).toBeNull()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('derives the active period from running tasks when execution is already idle', async () => {
|
||||||
|
const { summary } = useCompletionSummary()
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
const start = 25_000
|
||||||
|
vi.setSystemTime(start)
|
||||||
|
queueStore().runningTasks = [
|
||||||
|
createTask({ state: 'Running', ts: start + 1 })
|
||||||
|
]
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
const finish = start + 150
|
||||||
|
vi.setSystemTime(finish)
|
||||||
|
queueStore().historyTasks = [
|
||||||
|
createTask({ ts: finish - 10, previewUrl: 'img-running-trigger' })
|
||||||
|
]
|
||||||
|
queueStore().runningTasks = []
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
expect(summary.value).toEqual({
|
||||||
|
mode: 'allSuccess',
|
||||||
|
completedCount: 1,
|
||||||
|
failedCount: 0,
|
||||||
|
thumbnailUrls: ['img-running-trigger']
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('does not emit a summary when every finished task is still running or pending', async () => {
|
||||||
|
const { summary } = useCompletionSummary()
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
const start = 30_000
|
||||||
|
const finish = 30_300
|
||||||
|
|
||||||
|
await runBatch({
|
||||||
|
start,
|
||||||
|
finish,
|
||||||
|
tasks: [
|
||||||
|
createTask({ state: 'Running', ts: start + 20 }),
|
||||||
|
createTask({ state: 'Pending', ts: start + 40 })
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(summary.value).toBeNull()
|
||||||
|
})
|
||||||
|
})
|
||||||
116
src/composables/queue/useCompletionSummary.ts
Normal file
116
src/composables/queue/useCompletionSummary.ts
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
import { computed, ref, watch } from 'vue'
|
||||||
|
|
||||||
|
import { useExecutionStore } from '@/stores/executionStore'
|
||||||
|
import { useQueueStore } from '@/stores/queueStore'
|
||||||
|
import { jobStateFromTask } from '@/utils/queueUtil'
|
||||||
|
|
||||||
|
type CompletionSummaryMode = 'allSuccess' | 'mixed' | 'allFailed'
|
||||||
|
|
||||||
|
type CompletionSummary = {
|
||||||
|
mode: CompletionSummaryMode
|
||||||
|
completedCount: number
|
||||||
|
failedCount: number
|
||||||
|
thumbnailUrls: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tracks queue activity transitions and exposes a short-lived summary of the
|
||||||
|
* most recent generation batch.
|
||||||
|
*/
|
||||||
|
export const useCompletionSummary = () => {
|
||||||
|
const queueStore = useQueueStore()
|
||||||
|
const executionStore = useExecutionStore()
|
||||||
|
|
||||||
|
const isActive = computed(
|
||||||
|
() => queueStore.runningTasks.length > 0 || !executionStore.isIdle
|
||||||
|
)
|
||||||
|
|
||||||
|
const lastActiveStartTs = ref<number | null>(null)
|
||||||
|
const _summary = ref<CompletionSummary | null>(null)
|
||||||
|
const dismissTimer = ref<number | null>(null)
|
||||||
|
|
||||||
|
const clearDismissTimer = () => {
|
||||||
|
if (dismissTimer.value !== null) {
|
||||||
|
clearTimeout(dismissTimer.value)
|
||||||
|
dismissTimer.value = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const startDismissTimer = () => {
|
||||||
|
clearDismissTimer()
|
||||||
|
dismissTimer.value = window.setTimeout(() => {
|
||||||
|
_summary.value = null
|
||||||
|
dismissTimer.value = null
|
||||||
|
}, 6000)
|
||||||
|
}
|
||||||
|
|
||||||
|
const clearSummary = () => {
|
||||||
|
_summary.value = null
|
||||||
|
clearDismissTimer()
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
isActive,
|
||||||
|
(active, prev) => {
|
||||||
|
if (!prev && active) {
|
||||||
|
lastActiveStartTs.value = Date.now()
|
||||||
|
}
|
||||||
|
if (prev && !active) {
|
||||||
|
const start = lastActiveStartTs.value ?? 0
|
||||||
|
const finished = queueStore.historyTasks.filter((t) => {
|
||||||
|
const ts = t.executionEndTimestamp
|
||||||
|
return typeof ts === 'number' && ts >= start
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!finished.length) {
|
||||||
|
_summary.value = null
|
||||||
|
clearDismissTimer()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let completedCount = 0
|
||||||
|
let failedCount = 0
|
||||||
|
const imagePreviews: string[] = []
|
||||||
|
|
||||||
|
for (const task of finished) {
|
||||||
|
const state = jobStateFromTask(task, false)
|
||||||
|
if (state === 'completed') {
|
||||||
|
completedCount++
|
||||||
|
const preview = task.previewOutput
|
||||||
|
if (preview?.isImage) {
|
||||||
|
imagePreviews.push(preview.url)
|
||||||
|
}
|
||||||
|
} else if (state === 'failed') {
|
||||||
|
failedCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (completedCount === 0 && failedCount === 0) {
|
||||||
|
_summary.value = null
|
||||||
|
clearDismissTimer()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let mode: CompletionSummaryMode = 'mixed'
|
||||||
|
if (failedCount === 0) mode = 'allSuccess'
|
||||||
|
else if (completedCount === 0) mode = 'allFailed'
|
||||||
|
|
||||||
|
_summary.value = {
|
||||||
|
mode,
|
||||||
|
completedCount,
|
||||||
|
failedCount,
|
||||||
|
thumbnailUrls: imagePreviews.slice(0, 3)
|
||||||
|
}
|
||||||
|
startDismissTimer()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
const summary = computed(() => _summary.value)
|
||||||
|
|
||||||
|
return {
|
||||||
|
summary,
|
||||||
|
clearSummary
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -231,7 +231,7 @@ export const useQueueNotificationBanners = () => {
|
|||||||
completedCount++
|
completedCount++
|
||||||
const preview = task.previewOutput
|
const preview = task.previewOutput
|
||||||
if (preview?.isImage) {
|
if (preview?.isImage) {
|
||||||
imagePreviews.push(preview.urlWithTimestamp)
|
imagePreviews.push(preview.url)
|
||||||
}
|
}
|
||||||
} else if (state === 'failed') {
|
} else if (state === 'failed') {
|
||||||
failedCount++
|
failedCount++
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'
|
|
||||||
import type { NodeOutputWith } from '@/schemas/apiSchema'
|
import type { NodeOutputWith } from '@/schemas/apiSchema'
|
||||||
import { api } from '@/scripts/api'
|
import { api } from '@/scripts/api'
|
||||||
import { app } from '@/scripts/app'
|
|
||||||
import { useExtensionService } from '@/services/extensionService'
|
import { useExtensionService } from '@/services/extensionService'
|
||||||
|
|
||||||
type ImageCompareOutput = NodeOutputWith<{
|
type ImageCompareOutput = NodeOutputWith<{
|
||||||
@@ -12,7 +10,7 @@ type ImageCompareOutput = NodeOutputWith<{
|
|||||||
useExtensionService().registerExtension({
|
useExtensionService().registerExtension({
|
||||||
name: 'Comfy.ImageCompare',
|
name: 'Comfy.ImageCompare',
|
||||||
|
|
||||||
async nodeCreated(node: LGraphNode) {
|
async nodeCreated(node) {
|
||||||
if (node.constructor.comfyClass !== 'ImageCompare') return
|
if (node.constructor.comfyClass !== 'ImageCompare') return
|
||||||
|
|
||||||
const [oldWidth, oldHeight] = node.size
|
const [oldWidth, oldHeight] = node.size
|
||||||
@@ -24,22 +22,23 @@ useExtensionService().registerExtension({
|
|||||||
onExecuted?.call(this, output)
|
onExecuted?.call(this, output)
|
||||||
|
|
||||||
const { a_images: aImages, b_images: bImages } = output
|
const { a_images: aImages, b_images: bImages } = output
|
||||||
const rand = app.getRandParam()
|
|
||||||
|
|
||||||
const toUrl = (record: Record<string, string>) => {
|
const beforeUrl =
|
||||||
const params = new URLSearchParams(record)
|
aImages && aImages.length > 0
|
||||||
return api.apiURL(`/view?${params}${rand}`)
|
? api.apiURL(`/view?${new URLSearchParams(aImages[0])}`)
|
||||||
}
|
: ''
|
||||||
|
const afterUrl =
|
||||||
const beforeImages =
|
bImages && bImages.length > 0
|
||||||
aImages && aImages.length > 0 ? aImages.map(toUrl) : []
|
? api.apiURL(`/view?${new URLSearchParams(bImages[0])}`)
|
||||||
const afterImages =
|
: ''
|
||||||
bImages && bImages.length > 0 ? bImages.map(toUrl) : []
|
|
||||||
|
|
||||||
const widget = node.widgets?.find((w) => w.type === 'imagecompare')
|
const widget = node.widgets?.find((w) => w.type === 'imagecompare')
|
||||||
|
|
||||||
if (widget) {
|
if (widget) {
|
||||||
widget.value = { beforeImages, afterImages }
|
widget.value = {
|
||||||
|
before: beforeUrl,
|
||||||
|
after: afterUrl
|
||||||
|
}
|
||||||
widget.callback?.(widget.value)
|
widget.callback?.(widget.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import type Load3d from '@/extensions/core/load3d/Load3d'
|
|||||||
import { t } from '@/i18n'
|
import { t } from '@/i18n'
|
||||||
import { useToastStore } from '@/platform/updates/common/toastStore'
|
import { useToastStore } from '@/platform/updates/common/toastStore'
|
||||||
import { api } from '@/scripts/api'
|
import { api } from '@/scripts/api'
|
||||||
import { app } from '@/scripts/app'
|
|
||||||
|
|
||||||
class Load3dUtils {
|
class Load3dUtils {
|
||||||
static async generateThumbnailIfNeeded(
|
static async generateThumbnailIfNeeded(
|
||||||
@@ -133,8 +132,7 @@ class Load3dUtils {
|
|||||||
const params = [
|
const params = [
|
||||||
'filename=' + encodeURIComponent(filename),
|
'filename=' + encodeURIComponent(filename),
|
||||||
'type=' + type,
|
'type=' + type,
|
||||||
'subfolder=' + subfolder,
|
'subfolder=' + subfolder
|
||||||
app.getRandParam().substring(1)
|
|
||||||
].join('&')
|
].join('&')
|
||||||
|
|
||||||
return `/view?${params}`
|
return `/view?${params}`
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import type { ResultItemType } from '@/schemas/apiSchema'
|
import type { ResultItemType } from '@/schemas/apiSchema'
|
||||||
import { app } from '@/scripts/app'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Format time in MM:SS format
|
* Format time in MM:SS format
|
||||||
@@ -20,8 +19,7 @@ export function getResourceURL(
|
|||||||
const params = [
|
const params = [
|
||||||
'filename=' + encodeURIComponent(filename),
|
'filename=' + encodeURIComponent(filename),
|
||||||
'type=' + type,
|
'type=' + type,
|
||||||
'subfolder=' + subfolder,
|
'subfolder=' + subfolder
|
||||||
app.getRandParam().substring(1)
|
|
||||||
].join('&')
|
].join('&')
|
||||||
|
|
||||||
return `/view?${params}`
|
return `/view?${params}`
|
||||||
|
|||||||
@@ -382,11 +382,6 @@ export class ComfyApp {
|
|||||||
else return ''
|
else return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
getRandParam() {
|
|
||||||
if (isCloud) return ''
|
|
||||||
return '&rand=' + Math.random()
|
|
||||||
}
|
|
||||||
|
|
||||||
static onClipspaceEditorSave() {
|
static onClipspaceEditorSave() {
|
||||||
if (ComfyApp.clipspace_return_node) {
|
if (ComfyApp.clipspace_return_node) {
|
||||||
ComfyApp.pasteFromClipspace(ComfyApp.clipspace_return_node)
|
ComfyApp.pasteFromClipspace(ComfyApp.clipspace_return_node)
|
||||||
|
|||||||
@@ -117,12 +117,11 @@ export const useNodeOutputStore = defineStore('nodeOutput', () => {
|
|||||||
const outputs = getNodeOutputs(node)
|
const outputs = getNodeOutputs(node)
|
||||||
if (!outputs?.images?.length) return
|
if (!outputs?.images?.length) return
|
||||||
|
|
||||||
const rand = app.getRandParam()
|
|
||||||
const previewParam = getPreviewParam(node, outputs)
|
const previewParam = getPreviewParam(node, outputs)
|
||||||
|
|
||||||
return outputs.images.map((image) => {
|
return outputs.images.map((image) => {
|
||||||
const params = new URLSearchParams(image)
|
const params = new URLSearchParams(image)
|
||||||
return api.apiURL(`/view?${params}${previewParam}${rand}`)
|
return api.apiURL(`/view?${params}${previewParam}`)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -104,10 +104,6 @@ export class ResultItemImpl {
|
|||||||
return api.apiURL('/view?' + params)
|
return api.apiURL('/view?' + params)
|
||||||
}
|
}
|
||||||
|
|
||||||
get urlWithTimestamp(): string {
|
|
||||||
return `${this.url}&t=${+new Date()}`
|
|
||||||
}
|
|
||||||
|
|
||||||
get isVhsFormat(): boolean {
|
get isVhsFormat(): boolean {
|
||||||
return !!this.format && !!this.frame_rate
|
return !!this.format && !!this.frame_rate
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user