Delete Comfy video, image, and audio files from the desktop after they are saved to the library.
Audio never called purge, image stills used untagged names, and a locked file could abort the whole host-agent delete. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+60
-14
@@ -531,12 +531,59 @@ export function isOurComfyVideo(video: { filename: string; subfolder: string })
|
||||
})
|
||||
}
|
||||
|
||||
export function comfySweepNamePrefixes() {
|
||||
const names = new Set(['MiniMax_H3', 'xAIGen', 'XAIgen', 'LTX23', 'AIGen', 'v2-krea', 'v2-generate'])
|
||||
for (const raw of [comfyFilenamePrefix(), comfyAudioPrefix(), comfyImagePrefix()]) {
|
||||
const last = raw.replace(/\/$/, '').split('/').pop()
|
||||
if (last) names.add(last)
|
||||
}
|
||||
return [...names]
|
||||
}
|
||||
|
||||
export function comfyStudioFileTag() {
|
||||
const last = comfyFilenamePrefix().replace(/\/$/, '').split('/').pop() || ''
|
||||
if (/xaigen/i.test(last)) return 'xAIGen'
|
||||
return 'AIGen'
|
||||
}
|
||||
|
||||
export function comfyImagePrefix() {
|
||||
const config = useRuntimeConfig()
|
||||
const explicit = String(config.comfyImagePrefix || process.env.COMFY_IMAGE_PREFIX || '').trim()
|
||||
if (explicit) return explicit.replace(/\/$/, '')
|
||||
return `image/${comfyStudioFileTag()}`
|
||||
}
|
||||
|
||||
export function purgeComfyEnabled() {
|
||||
return useRuntimeConfig().purgeComfyOutputs !== false
|
||||
}
|
||||
|
||||
type ComfyDiskFile = { filename: string; subfolder?: string; type?: string }
|
||||
|
||||
async function deleteComfyHttpFile(file: ComfyDiskFile) {
|
||||
const payload = {
|
||||
filename: file.filename,
|
||||
subfolder: file.subfolder || '',
|
||||
type: file.type || 'output'
|
||||
}
|
||||
for (const path of ['/delete', '/aigen/purge']) {
|
||||
try {
|
||||
const res = await comfyFetch(path, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(payload)
|
||||
})
|
||||
if (res.ok) return true
|
||||
} catch {
|
||||
/* Comfy may not expose this route */
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
async function purgeOnDesktop(opts: {
|
||||
video?: { filename: string; subfolder: string; type: string }
|
||||
video?: ComfyDiskFile
|
||||
audio?: ComfyDiskFile
|
||||
output?: ComfyDiskFile
|
||||
imageName?: string
|
||||
imageSubfolder?: string
|
||||
extraImageNames?: string[]
|
||||
@@ -558,9 +605,12 @@ async function purgeOnDesktop(opts: {
|
||||
imageSubfolder: opts.imageSubfolder || '',
|
||||
imageNames: (opts.extraImageNames || []).filter(Boolean),
|
||||
video: opts.video || null,
|
||||
output: opts.video && /\.(png|jpe?g|webp|gif)$/i.test(opts.video.filename) ? opts.video : null
|
||||
audio: opts.audio || null,
|
||||
output: opts.output || (opts.video && /\.(png|jpe?g|webp|gif)$/i.test(opts.video.filename) ? opts.video : null),
|
||||
sweepPrefixes: comfySweepNamePrefixes(),
|
||||
sweepSubfolders: ['video', 'audio', 'image', 'still']
|
||||
}),
|
||||
signal: AbortSignal.timeout(8000)
|
||||
signal: AbortSignal.timeout(12000)
|
||||
})
|
||||
} catch {
|
||||
// Host agent may be down; Comfy HTTP purge is the fallback.
|
||||
@@ -568,7 +618,9 @@ async function purgeOnDesktop(opts: {
|
||||
}
|
||||
|
||||
export async function purgeComfyArtifacts(opts: {
|
||||
video?: { filename: string; subfolder: string; type: string }
|
||||
video?: ComfyDiskFile
|
||||
audio?: ComfyDiskFile
|
||||
output?: ComfyDiskFile
|
||||
imageName?: string
|
||||
imageSubfolder?: string
|
||||
extraImageNames?: string[]
|
||||
@@ -576,23 +628,17 @@ export async function purgeComfyArtifacts(opts: {
|
||||
}) {
|
||||
if (!purgeComfyEnabled()) return
|
||||
await purgeOnDesktop(opts)
|
||||
const files: { filename: string; subfolder: string; type: string }[] = []
|
||||
const files: ComfyDiskFile[] = []
|
||||
if (opts.video?.filename) files.push(opts.video)
|
||||
if (opts.audio?.filename) files.push(opts.audio)
|
||||
if (opts.output?.filename) files.push(opts.output)
|
||||
if (opts.imageName) files.push({ filename: opts.imageName, subfolder: opts.imageSubfolder || '', type: 'input' })
|
||||
for (const name of opts.extraImageNames || []) {
|
||||
if (!name || name === opts.imageName) continue
|
||||
files.push({ filename: name, subfolder: opts.imageSubfolder || '', type: 'input' })
|
||||
}
|
||||
for (const file of files) {
|
||||
try {
|
||||
await comfyFetch('/aigen/purge', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(file)
|
||||
})
|
||||
} catch {
|
||||
// Comfy may not have the purge route loaded yet; never fail the saved clip.
|
||||
}
|
||||
await deleteComfyHttpFile(file)
|
||||
}
|
||||
if (opts.promptId) {
|
||||
try {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createJob, emitJob, type Job } from '~/server/utils/jobs'
|
||||
import { ensureComfyReady } from '~/server/utils/comfyLifecycle'
|
||||
import { assertImageScaleToTotalPixelsNode, getComfyHost, uploadImage, queuePrompt } from '~/server/utils/comfy'
|
||||
import { assertImageScaleToTotalPixelsNode, comfyImagePrefix, getComfyHost, uploadImage, queuePrompt } from '~/server/utils/comfy'
|
||||
import { withImageComfyHost, waitForImageEdit, downloadEditedImage, purgeImageComfyArtifacts } from '~/server/utils/imageComfy'
|
||||
import { buildEditWorkflow } from '~/server/utils/imageWorkflow'
|
||||
import { ensureComfyLoraNames } from '~/server/utils/loras'
|
||||
@@ -128,7 +128,7 @@ export async function runEdit(job: Job, params: EditRunParams) {
|
||||
steps: params.steps,
|
||||
seed,
|
||||
cfg: params.cfg,
|
||||
filenamePrefix: `${comfyJobPrefix(job.id)}_p${index + 1}`,
|
||||
filenamePrefix: `${comfyImagePrefix()}_${comfyJobPrefix(job.id)}_p${index + 1}`,
|
||||
loraStack: params.loraStack || params.loraName,
|
||||
scaleToTotalPixels: params.scaleToTotalPixels,
|
||||
scaleMegapixels: params.scaleMegapixels
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createJob, emitJob, type Job } from '~/server/utils/jobs'
|
||||
import { ensureComfyReady } from '~/server/utils/comfyLifecycle'
|
||||
import { assertImageScaleToTotalPixelsNode, getComfyHost, uploadImage, queuePrompt } from '~/server/utils/comfy'
|
||||
import { assertImageScaleToTotalPixelsNode, comfyImagePrefix, getComfyHost, uploadImage, queuePrompt } from '~/server/utils/comfy'
|
||||
import { withImageComfyHost, waitForImageEdit, downloadEditedImage, purgeImageComfyArtifacts } from '~/server/utils/imageComfy'
|
||||
import { buildImageV2Workflow, IMAGE_V2_NODE_LABELS, resolveKreaGenerateAssets } from '~/server/utils/imageWorkflowV2'
|
||||
import { ensureComfyLoraNames, persistLoraFields } from '~/server/utils/loras'
|
||||
@@ -243,7 +243,7 @@ export async function runEditV2(job: Job, params: EditV2RunParams) {
|
||||
height: params.height,
|
||||
aspect: params.aspect,
|
||||
turbo: params.turbo === true,
|
||||
filenamePrefix: chainTotal > 1 ? `${comfyJobPrefix(job.id)}_p${index + 1}` : comfyJobPrefix(job.id),
|
||||
filenamePrefix: chainTotal > 1 ? `${comfyImagePrefix()}_${comfyJobPrefix(job.id)}_p${index + 1}` : `${comfyImagePrefix()}_${comfyJobPrefix(job.id)}`,
|
||||
kreaUnetName: krea?.unet,
|
||||
kreaClipName: krea?.clip,
|
||||
kreaVaeName: krea?.vae,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { AsyncLocalStorage } from 'node:async_hooks'
|
||||
import { getComfyHost, viaAgentFrontDoor } from '~/server/utils/comfy'
|
||||
import { getComfyHost, viaAgentFrontDoor, comfySweepNamePrefixes } from '~/server/utils/comfy'
|
||||
import { comfyJobPrefix } from '~/utils/outputNames'
|
||||
|
||||
const imageHostAls = new AsyncLocalStorage<string>()
|
||||
@@ -369,8 +369,12 @@ async function purgeImageOnDesktop(opts: {
|
||||
output?: { 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 || '')
|
||||
const controlUrl = String(
|
||||
config.imageComfyControlUrl || process.env.IMAGE_COMFY_CONTROL_URL || config.comfyControlUrl || process.env.COMFY_CONTROL_URL || ''
|
||||
).replace(/\/$/, '')
|
||||
const token = String(
|
||||
config.imageComfyControlToken || process.env.IMAGE_COMFY_CONTROL_TOKEN || config.comfyControlToken || process.env.COMFY_CONTROL_TOKEN || ''
|
||||
)
|
||||
if (!controlUrl) return
|
||||
try {
|
||||
await fetch(`${controlUrl}/purge`, {
|
||||
@@ -384,7 +388,9 @@ async function purgeImageOnDesktop(opts: {
|
||||
imageName: opts.inputName || '',
|
||||
imageSubfolder: opts.inputSubfolder || '',
|
||||
imageNames: (opts.extraInputNames || []).filter(Boolean),
|
||||
output: opts.output || null
|
||||
output: opts.output || null,
|
||||
sweepPrefixes: comfySweepNamePrefixes(),
|
||||
sweepSubfolders: ['video', 'audio', 'image', 'still']
|
||||
}),
|
||||
signal: AbortSignal.timeout(8000)
|
||||
})
|
||||
|
||||
@@ -2087,6 +2087,7 @@ export async function importMissingComfyAudio(owner: string, folderId?: string)
|
||||
comfyFilename: item.audio.filename
|
||||
})
|
||||
imported.push(track)
|
||||
await purgeComfyArtifacts({ audio: item.audio, promptId: item.promptId })
|
||||
}
|
||||
return imported
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createJob, emitJob, type Job } from '~/server/utils/jobs'
|
||||
import { extractAudio, fetchHistory, fetchHistoryAll, findHistoryAudio, queuePrompt } from '~/server/utils/comfy'
|
||||
import { extractAudio, fetchHistory, fetchHistoryAll, findHistoryAudio, purgeComfyArtifacts, queuePrompt } from '~/server/utils/comfy'
|
||||
import { comfyWsUrl } from '~/server/utils/comfy'
|
||||
import { ensureComfyReady } from '~/server/utils/comfyLifecycle'
|
||||
import { downloadComfyAudio, saveTrack } from '~/server/utils/library'
|
||||
@@ -195,6 +195,10 @@ function watchMusicJob(job: Job): Promise<void> {
|
||||
})
|
||||
job.trackId = track.id
|
||||
job.library.audioExt = ext
|
||||
await purgeComfyArtifacts({
|
||||
audio,
|
||||
promptId: job.promptId
|
||||
})
|
||||
}
|
||||
job.status = 'complete'
|
||||
emitJob(job, {
|
||||
|
||||
Reference in New Issue
Block a user