Retry library pulls after Comfy finishes and fall back to disk.
Long jobs were succeeding on Beast then failing the save step when /view briefly dropped; retries plus host-agent file serving keep finished MP4s from being orphaned. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+75
-22
@@ -1964,36 +1964,89 @@ export function renameClipFamily(owner: string, id: string, name: string) {
|
||||
})
|
||||
}
|
||||
|
||||
export async function downloadComfyVideo(video: { filename: string; subfolder: string; type: string }) {
|
||||
const subfolders = [...new Set([video.subfolder, 'video', ''])]
|
||||
let lastStatus = 0
|
||||
function sleep(ms: number) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
function downloadErrorText(error: unknown) {
|
||||
if (!error) return 'Unknown download error'
|
||||
if (typeof error === 'object') {
|
||||
const rec = error as { statusMessage?: string; message?: string; data?: { cause?: string } }
|
||||
const bits = [rec.statusMessage, rec.message, rec.data?.cause].filter(Boolean)
|
||||
if (bits.length) return bits[0] as string
|
||||
}
|
||||
if (error instanceof Error) return error.message
|
||||
return String(error)
|
||||
}
|
||||
|
||||
async function downloadViaHostAgent(file: { filename: string; subfolder: string; type: string }) {
|
||||
const config = useRuntimeConfig()
|
||||
const controlUrl = String(config.comfyControlUrl || process.env.COMFY_CONTROL_URL || '').replace(/\/$/, '')
|
||||
const token = String(config.comfyControlToken || process.env.COMFY_CONTROL_TOKEN || '')
|
||||
if (!controlUrl) return null
|
||||
const subfolders = [...new Set([file.subfolder, file.type === 'input' ? '' : 'video', file.type === 'input' ? '' : 'audio', ''])]
|
||||
for (const subfolder of subfolders) {
|
||||
const params = new URLSearchParams({
|
||||
filename: video.filename,
|
||||
filename: file.filename,
|
||||
subfolder,
|
||||
type: video.type || 'output'
|
||||
type: file.type || 'output'
|
||||
})
|
||||
const res = await comfyFetch(`/view?${params.toString()}`)
|
||||
lastStatus = res.status
|
||||
if (res.ok) return Buffer.from(await res.arrayBuffer())
|
||||
try {
|
||||
const res = await fetch(`${controlUrl}/view?${params.toString()}`, {
|
||||
headers: {
|
||||
Accept: '*/*',
|
||||
...(token ? { Authorization: `Bearer ${token}` } : {})
|
||||
},
|
||||
signal: AbortSignal.timeout(5 * 60 * 1000)
|
||||
})
|
||||
if (res.ok) return Buffer.from(await res.arrayBuffer())
|
||||
} catch {
|
||||
/* try next subfolder / fall through */
|
||||
}
|
||||
}
|
||||
throw new Error(`Failed to fetch completed video from ComfyUI (${lastStatus})`)
|
||||
return null
|
||||
}
|
||||
|
||||
async function downloadComfyMedia(
|
||||
file: { filename: string; subfolder: string; type: string },
|
||||
kind: 'video' | 'audio'
|
||||
) {
|
||||
const extras = kind === 'video' ? ['video', ''] : ['audio', '']
|
||||
const subfolders = [...new Set([file.subfolder, ...extras])]
|
||||
let lastStatus = 0
|
||||
let lastError = ''
|
||||
const attempts = 8
|
||||
for (let attempt = 1; attempt <= attempts; attempt++) {
|
||||
for (const subfolder of subfolders) {
|
||||
const params = new URLSearchParams({
|
||||
filename: file.filename,
|
||||
subfolder,
|
||||
type: file.type || 'output'
|
||||
})
|
||||
try {
|
||||
const res = await comfyFetch(`/view?${params.toString()}`, {
|
||||
signal: AbortSignal.timeout(5 * 60 * 1000)
|
||||
})
|
||||
lastStatus = res.status
|
||||
if (res.ok) return Buffer.from(await res.arrayBuffer())
|
||||
} catch (error) {
|
||||
lastError = downloadErrorText(error)
|
||||
}
|
||||
}
|
||||
const disk = await downloadViaHostAgent(file)
|
||||
if (disk?.length) return disk
|
||||
if (attempt < attempts) await sleep(1500 * attempt)
|
||||
}
|
||||
const detail = lastError || `HTTP ${lastStatus || 'n/a'}`
|
||||
throw new Error(`Failed to fetch completed ${kind} from ComfyUI (${detail}). File may still be on the desktop — use Recover.`)
|
||||
}
|
||||
|
||||
export async function downloadComfyVideo(video: { filename: string; subfolder: string; type: string }) {
|
||||
return downloadComfyMedia(video, 'video')
|
||||
}
|
||||
|
||||
export async function downloadComfyAudio(audio: { filename: string; subfolder: string; type: string }) {
|
||||
const subfolders = [...new Set([audio.subfolder, 'audio', ''])]
|
||||
let lastStatus = 0
|
||||
for (const subfolder of subfolders) {
|
||||
const params = new URLSearchParams({
|
||||
filename: audio.filename,
|
||||
subfolder,
|
||||
type: audio.type || 'output'
|
||||
})
|
||||
const res = await comfyFetch(`/view?${params.toString()}`)
|
||||
lastStatus = res.status
|
||||
if (res.ok) return Buffer.from(await res.arrayBuffer())
|
||||
}
|
||||
throw new Error(`Failed to fetch completed audio from ComfyUI (${lastStatus})`)
|
||||
return downloadComfyMedia(audio, 'audio')
|
||||
}
|
||||
|
||||
export async function importMissingComfyVideos(owner: string, folderId?: string) {
|
||||
|
||||
Reference in New Issue
Block a user