Add ACE-Step 1.5 and YuE on Music, and put Video/Image/Music cards back.
Recover finished Comfy audio when queueing hangs so a completed track is not stuck on Queueing. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+57
-16
@@ -1,9 +1,9 @@
|
||||
import { createJob, emitJob, type Job } from '~/server/utils/jobs'
|
||||
import { extractAudio, fetchHistory, queuePrompt } from '~/server/utils/comfy'
|
||||
import { extractAudio, fetchHistory, fetchHistoryAll, findHistoryAudio, queuePrompt } from '~/server/utils/comfy'
|
||||
import { comfyWsUrl } from '~/server/utils/comfy'
|
||||
import { ensureComfyReady } from '~/server/utils/comfyLifecycle'
|
||||
import { downloadComfyAudio, saveTrack } from '~/server/utils/library'
|
||||
import { buildMusicWorkflow, musicFilenamePrefix } from '~/server/utils/musicWorkflow'
|
||||
import { buildMusicWorkflow, musicFilenamePrefix, assertMusicEngineNodes } from '~/server/utils/musicWorkflow'
|
||||
import { NODE_LABELS } from '~/server/utils/workflow'
|
||||
import { musicExtension } from '~/utils/music'
|
||||
import type { MusicEngine } from '~/utils/music'
|
||||
@@ -131,13 +131,35 @@ function watchMusicJob(job: Job): Promise<void> {
|
||||
resolve()
|
||||
}
|
||||
|
||||
const adoptPromptId = (id: unknown) => {
|
||||
const promptId = String(id || '').trim()
|
||||
if (promptId && !job.promptId) job.promptId = promptId
|
||||
}
|
||||
|
||||
const loadJobAudio = async () => {
|
||||
if (job.promptId) {
|
||||
const scoped = await fetchHistory(job.promptId)
|
||||
const fromPrompt = extractAudio(scoped, job.promptId)
|
||||
if (fromPrompt) return fromPrompt
|
||||
}
|
||||
const found = findHistoryAudio(await fetchHistoryAll(), {
|
||||
promptId: job.promptId,
|
||||
tags: job.library?.tags,
|
||||
since: job.startedAt - 60_000
|
||||
})
|
||||
if (found) {
|
||||
job.promptId = found.promptId
|
||||
return found.audio
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
const succeed = async () => {
|
||||
if (settled || finishing || !job.promptId) return false
|
||||
if (settled || finishing) return false
|
||||
finishing = true
|
||||
job.saving = true
|
||||
try {
|
||||
const history = await fetchHistory(job.promptId)
|
||||
const audio = extractAudio(history, job.promptId)
|
||||
const audio = await loadJobAudio()
|
||||
if (!audio) {
|
||||
finishing = false
|
||||
job.saving = false
|
||||
@@ -205,14 +227,20 @@ function watchMusicJob(job: Job): Promise<void> {
|
||||
}
|
||||
|
||||
const pollHistory = async () => {
|
||||
if (settled || finishing || !job.promptId) return
|
||||
if (settled || finishing) return
|
||||
try {
|
||||
const history = await fetchHistory(job.promptId)
|
||||
if (extractAudio(history, job.promptId)) {
|
||||
const history = await fetchHistoryAll()
|
||||
const found = findHistoryAudio(history, {
|
||||
promptId: job.promptId,
|
||||
tags: job.library?.tags,
|
||||
since: job.startedAt - 60_000
|
||||
})
|
||||
if (found) {
|
||||
job.promptId = found.promptId
|
||||
await succeed()
|
||||
return
|
||||
}
|
||||
const entry = (history?.[job.promptId] || history) as {
|
||||
const entry = (job.promptId ? history?.[job.promptId] : undefined) as {
|
||||
status?: { status_str?: string; completed?: boolean }
|
||||
} | undefined
|
||||
const status = entry?.status?.status_str
|
||||
@@ -242,6 +270,7 @@ function watchMusicJob(job: Job): Promise<void> {
|
||||
}
|
||||
const type = String(payload.type || '')
|
||||
const data = payload.data || {}
|
||||
adoptPromptId(data.prompt_id)
|
||||
if (type === 'status') markActivity()
|
||||
if (type === 'progress') {
|
||||
const value = Number(data.value || 0)
|
||||
@@ -259,7 +288,7 @@ function watchMusicJob(job: Job): Promise<void> {
|
||||
}
|
||||
if (type === 'executing') {
|
||||
const node = data.node == null ? '' : String(data.node)
|
||||
if (!node && job.promptId) void succeed()
|
||||
if (!node) void succeed()
|
||||
else if (node) {
|
||||
emitLocal({
|
||||
type: 'status',
|
||||
@@ -327,9 +356,11 @@ export async function startMusicJob(params: MusicJobParams) {
|
||||
queuePending: status.queuePending
|
||||
})
|
||||
})
|
||||
job.status = 'uploading'
|
||||
emitJob(job, { type: 'status', message: 'Queueing ACE-Step...', progress: 6 })
|
||||
job.status = 'running'
|
||||
const engineLabel = params.engine === 'yue' ? 'YuE' : params.engine === 'ace-step-1.5' ? 'ACE-Step 1.5' : 'ACE-Step'
|
||||
emitJob(job, { type: 'status', message: `Queueing ${engineLabel}...`, progress: 6 })
|
||||
await waitForSocket(job, 4000)
|
||||
await assertMusicEngineNodes(params.engine)
|
||||
const graph = buildMusicWorkflow({
|
||||
tags: params.tags,
|
||||
lyrics: params.instrumental ? '' : params.lyrics,
|
||||
@@ -344,10 +375,20 @@ export async function startMusicJob(params: MusicJobParams) {
|
||||
engine: params.engine,
|
||||
vocalMultiplier: params.instrumental ? 0.2 : 1
|
||||
})
|
||||
const queued = await queuePrompt(graph, job.clientId)
|
||||
job.promptId = queued.prompt_id
|
||||
job.status = 'running'
|
||||
emitJob(job, { type: 'status', message: 'Job queued on ComfyUI', progress: 8 })
|
||||
try {
|
||||
const queued = await queuePrompt(graph, job.clientId, { timeoutMs: 25_000 })
|
||||
if (queued?.prompt_id) job.promptId = queued.prompt_id
|
||||
emitJob(job, { type: 'status', message: 'Job queued on ComfyUI', progress: 8 })
|
||||
} catch (queueError: unknown) {
|
||||
const statusCode = Number((queueError as { statusCode?: number; status?: number })?.statusCode
|
||||
|| (queueError as { status?: number })?.status || 0)
|
||||
if (statusCode >= 400) throw queueError
|
||||
emitJob(job, {
|
||||
type: 'status',
|
||||
message: 'Waiting for ComfyUI to pick up the job...',
|
||||
progress: 8
|
||||
})
|
||||
}
|
||||
await done
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : String(error)
|
||||
|
||||
Reference in New Issue
Block a user