From 9cb8b9bda395e466592f2227ae1b4930840177ad Mon Sep 17 00:00:00 2001 From: Towsty Date: Thu, 3 Sep 2026 00:02:17 -0500 Subject: [PATCH] Delete imported Comfy stills from disk instead of looking for them in the video folder. Co-authored-by: Cursor --- scripts/comfy-host-agent.mjs | 107 +++++++++++++++++++++++------------ server/utils/comfy.ts | 3 +- server/utils/imageChain.ts | 14 ++--- server/utils/imageChainV2.ts | 14 ++--- server/utils/imageComfy.ts | 14 ++++- 5 files changed, 99 insertions(+), 53 deletions(-) diff --git a/scripts/comfy-host-agent.mjs b/scripts/comfy-host-agent.mjs index 919301e..235b1d2 100644 --- a/scripts/comfy-host-agent.mjs +++ b/scripts/comfy-host-agent.mjs @@ -530,34 +530,73 @@ function sharedRoots() { const local = process.env.LOCALAPPDATA || '' return { input: process.env.COMFY_INPUT_DIR || join(local, 'Comfy-Desktop', 'ComfyUI-Shared', 'input'), - output: process.env.COMFY_OUTPUT_DIR || join(local, 'Comfy-Desktop', 'ComfyUI-Shared', 'output') + output: process.env.COMFY_OUTPUT_DIR || join(local, 'Comfy-Desktop', 'ComfyUI-Shared', 'output'), + temp: process.env.COMFY_TEMP_DIR || join(local, 'Comfy-Desktop', 'ComfyUI-Shared', 'temp') } } -function inputRoots() { +function installLeafRoots(leaf) { const local = process.env.LOCALAPPDATA || '' - const roots = [ - process.env.COMFY_INPUT_DIR || join(local, 'Comfy-Desktop', 'ComfyUI-Shared', 'input') - ] - const cwd = process.env.COMFY_LAUNCH_CWD - if (cwd) roots.push(join(cwd, 'input')) + const roots = [] const installs = join(local, 'Comfy-Desktop', 'ComfyUI-Installs') try { for (const dir of readdirSync(installs, { withFileTypes: true })) { if (!dir.isDirectory()) continue - roots.push(join(installs, dir.name, 'ComfyUI', 'input')) + roots.push(join(installs, dir.name, 'ComfyUI', leaf)) } } catch { /* ignore */ } + return roots +} + +function inputRoots() { + const roots = [sharedRoots().input] + const cwd = process.env.COMFY_LAUNCH_CWD + if (cwd) roots.push(join(cwd, 'input')) + roots.push(...installLeafRoots('input')) return [...new Set(roots)] } function outputRoots() { const roots = [sharedRoots().output] const cwd = process.env.COMFY_LAUNCH_CWD - if (cwd) roots.push(join(cwd, 'output')) + if (cwd) { + roots.push(join(cwd, 'output')) + roots.push(join(cwd, 'ComfyUI', 'output')) + } + roots.push(...installLeafRoots('output')) return [...new Set(roots)] } +function tempRoots() { + const roots = [sharedRoots().temp] + const cwd = process.env.COMFY_LAUNCH_CWD + if (cwd) { + roots.push(join(cwd, 'temp')) + roots.push(join(cwd, 'ComfyUI', 'temp')) + } + roots.push(...installLeafRoots('temp')) + return [...new Set(roots)] +} + +function rootsForType(type) { + if (type === 'input') return inputRoots() + if (type === 'temp') return [...tempRoots(), ...outputRoots()] + return [...outputRoots(), ...tempRoots()] +} + +function removeNamedFile(roots, filename, subfolders) { + const deleted = [] + const subs = [...new Set((subfolders || []).map(item => String(item || '')))] + for (const root of roots) { + if (!existsSync(root)) continue + for (const sub of subs) { + const path = safeFile(root, sub, filename) + if (removeFile(path)) deleted.push(path) + } + } + return deleted +} + function safeFile(root, subfolder, filename) { const name = basename(String(filename || '')) if (!name || name === '.' || name === '..') return null @@ -597,43 +636,37 @@ function purgeDesktopFiles(body) { ].map(name => String(name || '').trim()).filter(Boolean) const imageSub = String(body?.imageSubfolder || '') for (const imageName of names) { - for (const root of inputRoots()) { - if (!existsSync(root)) continue - const nested = safeFile(root, imageSub, imageName) - if (removeFile(nested)) deleted.push(nested) - if (imageSub) { - const flat = safeFile(root, '', imageName) - if (removeFile(flat)) deleted.push(flat) - } - } + deleted.push(...removeNamedFile(inputRoots(), imageName, [imageSub, ''])) } const video = body?.video || null const videoName = String(video?.filename || '') if (videoName) { - for (const root of outputRoots()) { - const nested = safeFile(root, video.subfolder || 'video', videoName) - if (removeFile(nested)) deleted.push(nested) - const flat = safeFile(root, '', videoName) - if (removeFile(flat)) deleted.push(flat) - } + const type = String(video?.type || 'output') + const imageOut = /\.(png|jpe?g|webp|gif)$/i.test(videoName) + deleted.push(...removeNamedFile( + rootsForType(type), + videoName, + [video.subfolder, imageOut ? '' : 'video', ''] + )) } const output = body?.output || null const outputName = String(output?.filename || '') if (outputName) { - const type = String(output?.type || 'output') - const sub = String(output?.subfolder || '') - const rootsForType = type === 'input' ? inputRoots() : outputRoots() - for (const root of rootsForType) { - if (!existsSync(root)) continue - const nested = safeFile(root, sub, outputName) - if (removeFile(nested)) deleted.push(nested) - if (sub) { - const flat = safeFile(root, '', outputName) - if (removeFile(flat)) deleted.push(flat) - } - } + deleted.push(...removeNamedFile( + rootsForType(String(output?.type || 'output')), + outputName, + [output.subfolder, ''] + )) } - console.log(JSON.stringify({ src: 'comfy-host-agent', event: 'purge', deleted: deleted.length, files: deleted.map((path) => basename(path)), imageNames: names })) + console.log(JSON.stringify({ + src: 'comfy-host-agent', + event: 'purge', + deleted: deleted.length, + files: deleted.map((path) => basename(path)), + imageNames: names, + video: videoName || null, + output: outputName || null + })) return { ok: true, deleted } } diff --git a/server/utils/comfy.ts b/server/utils/comfy.ts index d24ac2b..7ca8b5f 100644 --- a/server/utils/comfy.ts +++ b/server/utils/comfy.ts @@ -451,7 +451,8 @@ async function purgeOnDesktop(opts: { imageName: opts.imageName || '', imageSubfolder: opts.imageSubfolder || '', imageNames: (opts.extraImageNames || []).filter(Boolean), - video: opts.video || null + video: opts.video || null, + output: opts.video && /\.(png|jpe?g|webp|gif)$/i.test(opts.video.filename) ? opts.video : null }), signal: AbortSignal.timeout(8000) }) diff --git a/server/utils/imageChain.ts b/server/utils/imageChain.ts index c6e92fd..7ebc89f 100644 --- a/server/utils/imageChain.ts +++ b/server/utils/imageChain.ts @@ -1,7 +1,7 @@ import { createJob, emitJob, type Job } from '~/server/utils/jobs' import { ensureComfyReady } from '~/server/utils/comfyLifecycle' -import { assertImageScaleToTotalPixelsNode, getComfyHost, uploadImage, queuePrompt, purgeComfyArtifacts } from '~/server/utils/comfy' -import { withImageComfyHost, waitForImageEdit, downloadEditedImage } from '~/server/utils/imageComfy' +import { assertImageScaleToTotalPixelsNode, 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' import { imageDimensions } from '~/server/utils/resolution' @@ -188,11 +188,11 @@ export async function runEdit(job: Job, params: EditRunParams) { }) job.stillId = still?.id if (!batch) parentStillId = still?.id - await purgeComfyArtifacts({ - video: { filename: output.filename, subfolder: output.subfolder, type: output.type }, - imageName: uploaded.name, - imageSubfolder: uploaded.subfolder, - extraImageNames: uploadedRef?.name ? [uploadedRef.name] : [], + await purgeImageComfyArtifacts({ + output: { filename: output.filename, subfolder: output.subfolder, type: output.type }, + inputName: uploaded.name, + inputSubfolder: uploaded.subfolder, + extraInputNames: uploadedRef?.name ? [uploadedRef.name] : [], promptId: job.promptId }) diff --git a/server/utils/imageChainV2.ts b/server/utils/imageChainV2.ts index 29246c6..ecb2d83 100644 --- a/server/utils/imageChainV2.ts +++ b/server/utils/imageChainV2.ts @@ -1,7 +1,7 @@ import { createJob, emitJob, type Job } from '~/server/utils/jobs' import { ensureComfyReady } from '~/server/utils/comfyLifecycle' -import { assertImageScaleToTotalPixelsNode, getComfyHost, uploadImage, queuePrompt, purgeComfyArtifacts } from '~/server/utils/comfy' -import { withImageComfyHost, waitForImageEdit, downloadEditedImage } from '~/server/utils/imageComfy' +import { assertImageScaleToTotalPixelsNode, 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' import { imageDimensions } from '~/server/utils/resolution' @@ -321,11 +321,11 @@ export async function runEditV2(job: Job, params: EditV2RunParams) { }) job.stillId = still?.id if (!batch) parentStillId = still?.id - await purgeComfyArtifacts({ - video: { filename: output.filename, subfolder: output.subfolder, type: output.type }, - imageName: uploaded?.name, - imageSubfolder: uploaded?.subfolder, - extraImageNames: last + await purgeImageComfyArtifacts({ + output: { filename: output.filename, subfolder: output.subfolder, type: output.type }, + inputName: uploaded?.name, + inputSubfolder: uploaded?.subfolder, + extraInputNames: last ? [uploadedRef?.name, uploadedMask?.name].filter((name): name is string => Boolean(name)) : [], promptId: job.promptId diff --git a/server/utils/imageComfy.ts b/server/utils/imageComfy.ts index 361967a..6101e58 100644 --- a/server/utils/imageComfy.ts +++ b/server/utils/imageComfy.ts @@ -365,6 +365,7 @@ async function deleteSidecarFile(file: { filename: string; subfolder?: string; t async function purgeImageOnDesktop(opts: { inputName?: string inputSubfolder?: string + extraInputNames?: string[] output?: { filename: string; subfolder: string; type: string } }) { const config = useRuntimeConfig() @@ -382,9 +383,10 @@ async function purgeImageOnDesktop(opts: { body: JSON.stringify({ imageName: opts.inputName || '', imageSubfolder: opts.inputSubfolder || '', + imageNames: (opts.extraInputNames || []).filter(Boolean), output: opts.output || null }), - signal: AbortSignal.timeout(4000) + signal: AbortSignal.timeout(8000) }) } catch { /* Host agent purge is optional; Comfy HTTP delete is the primary path. */ @@ -395,6 +397,7 @@ export async function purgeImageComfyArtifacts(opts: { output?: { filename: string; subfolder: string; type: string } inputName?: string inputSubfolder?: string + extraInputNames?: string[] promptId?: string }) { if (!purgeImageEnabled()) return @@ -413,12 +416,21 @@ export async function purgeImageComfyArtifacts(opts: { type: 'input' }) } + for (const name of opts.extraInputNames || []) { + if (!name || name === opts.inputName) continue + files.push({ + filename: name, + subfolder: opts.inputSubfolder || '', + type: 'input' + }) + } for (const file of files) { await deleteSidecarFile(file) } await purgeImageOnDesktop({ inputName: opts.inputName, inputSubfolder: opts.inputSubfolder, + extraInputNames: opts.extraInputNames, output: opts.output }) if (opts.promptId) {