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:
+129
-20
@@ -2,7 +2,7 @@ import http from 'node:http'
|
|||||||
import net from 'node:net'
|
import net from 'node:net'
|
||||||
import { execFile, spawn } from 'node:child_process'
|
import { execFile, spawn } from 'node:child_process'
|
||||||
import { promisify } from 'node:util'
|
import { promisify } from 'node:util'
|
||||||
import { readdirSync, existsSync, rmSync, readFileSync, openSync, mkdirSync, writeFileSync, unlinkSync } from 'node:fs'
|
import { readdirSync, existsSync, rmSync, readFileSync, openSync, mkdirSync, writeFileSync, unlinkSync, statSync } from 'node:fs'
|
||||||
import { basename, dirname, join, resolve, relative, isAbsolute } from 'node:path'
|
import { basename, dirname, join, resolve, relative, isAbsolute } from 'node:path'
|
||||||
import { tmpdir } from 'node:os'
|
import { tmpdir } from 'node:os'
|
||||||
|
|
||||||
@@ -584,19 +584,6 @@ function rootsForType(type) {
|
|||||||
return [...outputRoots(), ...tempRoots()]
|
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) {
|
function safeFile(root, subfolder, filename) {
|
||||||
const name = basename(String(filename || ''))
|
const name = basename(String(filename || ''))
|
||||||
if (!name || name === '.' || name === '..') return null
|
if (!name || name === '.' || name === '..') return null
|
||||||
@@ -609,8 +596,91 @@ function safeFile(root, subfolder, filename) {
|
|||||||
|
|
||||||
function removeFile(path) {
|
function removeFile(path) {
|
||||||
if (!path || !existsSync(path)) return false
|
if (!path || !existsSync(path)) return false
|
||||||
rmSync(path, { force: true })
|
try {
|
||||||
return true
|
rmSync(path, { force: true })
|
||||||
|
return true
|
||||||
|
} catch {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function fileStem(filename) {
|
||||||
|
const name = basename(String(filename || ''))
|
||||||
|
const dot = name.lastIndexOf('.')
|
||||||
|
return dot > 0 ? name.slice(0, dot) : name
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeNamedFile(roots, filename, subfolders) {
|
||||||
|
const deleted = []
|
||||||
|
const name = basename(String(filename || ''))
|
||||||
|
if (!name) return deleted
|
||||||
|
const stem = fileStem(name)
|
||||||
|
const subs = [...new Set((subfolders || []).map(item => String(item || '')))]
|
||||||
|
for (const root of roots) {
|
||||||
|
if (!existsSync(root)) continue
|
||||||
|
for (const sub of subs) {
|
||||||
|
const dir = sub ? join(root, String(sub).replace(/\.\./g, '')) : root
|
||||||
|
const exact = safeFile(root, sub, name)
|
||||||
|
if (removeFile(exact)) deleted.push(exact)
|
||||||
|
if (!stem || !existsSync(dir)) continue
|
||||||
|
let files = []
|
||||||
|
try { files = readdirSync(dir) } catch { continue }
|
||||||
|
for (const file of files) {
|
||||||
|
if (file === name || file.startsWith(`${stem}.`)) {
|
||||||
|
const path = join(dir, file)
|
||||||
|
if (removeFile(path)) deleted.push(path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return deleted
|
||||||
|
}
|
||||||
|
|
||||||
|
function defaultSweepPrefixes() {
|
||||||
|
return ['MiniMax_H3', 'xAIGen', 'XAIgen', 'LTX23', 'AIGen', 'v2-krea', 'v2-generate']
|
||||||
|
}
|
||||||
|
|
||||||
|
function looksLikeStudioStill(name) {
|
||||||
|
return /^[0-9a-f]{12}(_p\d+)?(_\d{5})?_?\.(png|jpe?g|webp)$/i.test(String(name || ''))
|
||||||
|
}
|
||||||
|
|
||||||
|
function isStudioOutputName(file, sub, prefixes) {
|
||||||
|
if (looksLikeStudioStill(file) && (!sub || sub === 'image' || sub === 'still')) return true
|
||||||
|
if (sub === 'audio') {
|
||||||
|
return prefixes.some(prefix => file.startsWith(prefix)) || file.startsWith('ComfyUI')
|
||||||
|
}
|
||||||
|
if (!sub) {
|
||||||
|
return prefixes.some(prefix => prefix !== 'LTX23' && file.startsWith(prefix))
|
||||||
|
}
|
||||||
|
return prefixes.some(prefix => file.startsWith(prefix))
|
||||||
|
}
|
||||||
|
|
||||||
|
function sweepStaleStudioOutputs(prefixes, subfolders, maxAgeMs = 15 * 60 * 1000) {
|
||||||
|
const deleted = []
|
||||||
|
const names = [...new Set([...defaultSweepPrefixes(), ...(prefixes || [])])].map(item => String(item || '').trim()).filter(Boolean)
|
||||||
|
const subs = [...new Set(['video', 'audio', 'image', 'still', ...(subfolders || [])])].map(item => String(item || ''))
|
||||||
|
const cutoff = Date.now() - Math.max(60_000, Number(maxAgeMs) || 900_000)
|
||||||
|
for (const root of outputRoots()) {
|
||||||
|
if (!existsSync(root)) continue
|
||||||
|
for (const sub of ['', ...subs]) {
|
||||||
|
const dir = sub ? join(root, sub) : root
|
||||||
|
if (!existsSync(dir)) continue
|
||||||
|
let files = []
|
||||||
|
try { files = readdirSync(dir) } catch { continue }
|
||||||
|
for (const file of files) {
|
||||||
|
if (!isStudioOutputName(file, sub, names)) continue
|
||||||
|
const path = join(dir, file)
|
||||||
|
try {
|
||||||
|
const st = statSync(path)
|
||||||
|
if (st.isDirectory() || st.mtimeMs > cutoff) continue
|
||||||
|
} catch {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (removeFile(path)) deleted.push(path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return deleted
|
||||||
}
|
}
|
||||||
|
|
||||||
function readJson(req) {
|
function readJson(req) {
|
||||||
@@ -649,25 +719,37 @@ function purgeDesktopFiles(body) {
|
|||||||
[video.subfolder, imageOut ? '' : 'video', '']
|
[video.subfolder, imageOut ? '' : 'video', '']
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
const audio = body?.audio || null
|
||||||
|
const audioName = String(audio?.filename || '')
|
||||||
|
if (audioName) {
|
||||||
|
deleted.push(...removeNamedFile(
|
||||||
|
rootsForType(String(audio?.type || 'output')),
|
||||||
|
audioName,
|
||||||
|
[audio.subfolder, 'audio', '']
|
||||||
|
))
|
||||||
|
}
|
||||||
const output = body?.output || null
|
const output = body?.output || null
|
||||||
const outputName = String(output?.filename || '')
|
const outputName = String(output?.filename || '')
|
||||||
if (outputName) {
|
if (outputName) {
|
||||||
deleted.push(...removeNamedFile(
|
deleted.push(...removeNamedFile(
|
||||||
rootsForType(String(output?.type || 'output')),
|
rootsForType(String(output?.type || 'output')),
|
||||||
outputName,
|
outputName,
|
||||||
[output.subfolder, '']
|
[output.subfolder, 'image', 'still', '']
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
deleted.push(...sweepStaleStudioOutputs(body?.sweepPrefixes, body?.sweepSubfolders, body?.sweepMaxAgeMs))
|
||||||
|
const unique = [...new Set(deleted)]
|
||||||
console.log(JSON.stringify({
|
console.log(JSON.stringify({
|
||||||
src: 'comfy-host-agent',
|
src: 'comfy-host-agent',
|
||||||
event: 'purge',
|
event: 'purge',
|
||||||
deleted: deleted.length,
|
deleted: unique.length,
|
||||||
files: deleted.map((path) => basename(path)),
|
files: unique.map((path) => basename(path)),
|
||||||
imageNames: names,
|
imageNames: names,
|
||||||
video: videoName || null,
|
video: videoName || null,
|
||||||
|
audio: audioName || null,
|
||||||
output: outputName || null
|
output: outputName || null
|
||||||
}))
|
}))
|
||||||
return { ok: true, deleted }
|
return { ok: true, deleted: unique }
|
||||||
}
|
}
|
||||||
|
|
||||||
const server = http.createServer(async (req, res) => {
|
const server = http.createServer(async (req, res) => {
|
||||||
@@ -760,14 +842,41 @@ const server = http.createServer(async (req, res) => {
|
|||||||
const body = await readJson(req)
|
const body = await readJson(req)
|
||||||
return json(res, 200, purgeDesktopFiles(body))
|
return json(res, 200, purgeDesktopFiles(body))
|
||||||
}
|
}
|
||||||
|
if (req.method === 'POST' && url.pathname === '/sweep') {
|
||||||
|
const body = await readJson(req)
|
||||||
|
const deleted = sweepStaleStudioOutputs(body?.sweepPrefixes, body?.sweepSubfolders, body?.sweepMaxAgeMs)
|
||||||
|
console.log(JSON.stringify({
|
||||||
|
src: 'comfy-host-agent',
|
||||||
|
event: 'sweep',
|
||||||
|
deleted: deleted.length,
|
||||||
|
files: deleted.map((path) => basename(path))
|
||||||
|
}))
|
||||||
|
return json(res, 200, { ok: true, deleted })
|
||||||
|
}
|
||||||
json(res, 404, { ok: false, error: 'not found' })
|
json(res, 404, { ok: false, error: 'not found' })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
function logStartupSweep() {
|
||||||
|
try {
|
||||||
|
const deleted = sweepStaleStudioOutputs()
|
||||||
|
if (!deleted.length) return
|
||||||
|
console.log(JSON.stringify({
|
||||||
|
src: 'comfy-host-agent',
|
||||||
|
event: 'startup-sweep',
|
||||||
|
deleted: deleted.length,
|
||||||
|
files: deleted.map((path) => basename(path))
|
||||||
|
}))
|
||||||
|
} catch (error) {
|
||||||
|
console.log(JSON.stringify({ src: 'comfy-host-agent', event: 'startup-sweep-error', error: String(error?.message || error) }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
server.listen(port, '0.0.0.0', async () => {
|
server.listen(port, '0.0.0.0', async () => {
|
||||||
ensureProxyListening()
|
ensureProxyListening()
|
||||||
lastHealthyPort = await syncProxy() || 0
|
lastHealthyPort = await syncProxy() || 0
|
||||||
lastProcessUp = Boolean(lastHealthyPort) || await processUp().catch(() => false)
|
lastProcessUp = Boolean(lastHealthyPort) || await processUp().catch(() => false)
|
||||||
if (lastHealthyPort) await noteQueue(lastHealthyPort)
|
if (lastHealthyPort) await noteQueue(lastHealthyPort)
|
||||||
|
logStartupSweep()
|
||||||
console.log(JSON.stringify({
|
console.log(JSON.stringify({
|
||||||
src: 'comfy-host-agent',
|
src: 'comfy-host-agent',
|
||||||
event: 'listen',
|
event: 'listen',
|
||||||
|
|||||||
+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() {
|
export function purgeComfyEnabled() {
|
||||||
return useRuntimeConfig().purgeComfyOutputs !== false
|
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: {
|
async function purgeOnDesktop(opts: {
|
||||||
video?: { filename: string; subfolder: string; type: string }
|
video?: ComfyDiskFile
|
||||||
|
audio?: ComfyDiskFile
|
||||||
|
output?: ComfyDiskFile
|
||||||
imageName?: string
|
imageName?: string
|
||||||
imageSubfolder?: string
|
imageSubfolder?: string
|
||||||
extraImageNames?: string[]
|
extraImageNames?: string[]
|
||||||
@@ -558,9 +605,12 @@ async function purgeOnDesktop(opts: {
|
|||||||
imageSubfolder: opts.imageSubfolder || '',
|
imageSubfolder: opts.imageSubfolder || '',
|
||||||
imageNames: (opts.extraImageNames || []).filter(Boolean),
|
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
|
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 {
|
} catch {
|
||||||
// Host agent may be down; Comfy HTTP purge is the fallback.
|
// Host agent may be down; Comfy HTTP purge is the fallback.
|
||||||
@@ -568,7 +618,9 @@ async function purgeOnDesktop(opts: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function purgeComfyArtifacts(opts: {
|
export async function purgeComfyArtifacts(opts: {
|
||||||
video?: { filename: string; subfolder: string; type: string }
|
video?: ComfyDiskFile
|
||||||
|
audio?: ComfyDiskFile
|
||||||
|
output?: ComfyDiskFile
|
||||||
imageName?: string
|
imageName?: string
|
||||||
imageSubfolder?: string
|
imageSubfolder?: string
|
||||||
extraImageNames?: string[]
|
extraImageNames?: string[]
|
||||||
@@ -576,23 +628,17 @@ export async function purgeComfyArtifacts(opts: {
|
|||||||
}) {
|
}) {
|
||||||
if (!purgeComfyEnabled()) return
|
if (!purgeComfyEnabled()) return
|
||||||
await purgeOnDesktop(opts)
|
await purgeOnDesktop(opts)
|
||||||
const files: { filename: string; subfolder: string; type: string }[] = []
|
const files: ComfyDiskFile[] = []
|
||||||
if (opts.video?.filename) files.push(opts.video)
|
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' })
|
if (opts.imageName) files.push({ filename: opts.imageName, subfolder: opts.imageSubfolder || '', type: 'input' })
|
||||||
for (const name of opts.extraImageNames || []) {
|
for (const name of opts.extraImageNames || []) {
|
||||||
if (!name || name === opts.imageName) continue
|
if (!name || name === opts.imageName) continue
|
||||||
files.push({ filename: name, subfolder: opts.imageSubfolder || '', type: 'input' })
|
files.push({ filename: name, subfolder: opts.imageSubfolder || '', type: 'input' })
|
||||||
}
|
}
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
try {
|
await deleteComfyHttpFile(file)
|
||||||
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.
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (opts.promptId) {
|
if (opts.promptId) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { createJob, emitJob, type Job } from '~/server/utils/jobs'
|
import { createJob, emitJob, type Job } from '~/server/utils/jobs'
|
||||||
import { ensureComfyReady } from '~/server/utils/comfyLifecycle'
|
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 { withImageComfyHost, waitForImageEdit, downloadEditedImage, purgeImageComfyArtifacts } from '~/server/utils/imageComfy'
|
||||||
import { buildEditWorkflow } from '~/server/utils/imageWorkflow'
|
import { buildEditWorkflow } from '~/server/utils/imageWorkflow'
|
||||||
import { ensureComfyLoraNames } from '~/server/utils/loras'
|
import { ensureComfyLoraNames } from '~/server/utils/loras'
|
||||||
@@ -128,7 +128,7 @@ export async function runEdit(job: Job, params: EditRunParams) {
|
|||||||
steps: params.steps,
|
steps: params.steps,
|
||||||
seed,
|
seed,
|
||||||
cfg: params.cfg,
|
cfg: params.cfg,
|
||||||
filenamePrefix: `${comfyJobPrefix(job.id)}_p${index + 1}`,
|
filenamePrefix: `${comfyImagePrefix()}_${comfyJobPrefix(job.id)}_p${index + 1}`,
|
||||||
loraStack: params.loraStack || params.loraName,
|
loraStack: params.loraStack || params.loraName,
|
||||||
scaleToTotalPixels: params.scaleToTotalPixels,
|
scaleToTotalPixels: params.scaleToTotalPixels,
|
||||||
scaleMegapixels: params.scaleMegapixels
|
scaleMegapixels: params.scaleMegapixels
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { createJob, emitJob, type Job } from '~/server/utils/jobs'
|
import { createJob, emitJob, type Job } from '~/server/utils/jobs'
|
||||||
import { ensureComfyReady } from '~/server/utils/comfyLifecycle'
|
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 { withImageComfyHost, waitForImageEdit, downloadEditedImage, purgeImageComfyArtifacts } from '~/server/utils/imageComfy'
|
||||||
import { buildImageV2Workflow, IMAGE_V2_NODE_LABELS, resolveKreaGenerateAssets } from '~/server/utils/imageWorkflowV2'
|
import { buildImageV2Workflow, IMAGE_V2_NODE_LABELS, resolveKreaGenerateAssets } from '~/server/utils/imageWorkflowV2'
|
||||||
import { ensureComfyLoraNames, persistLoraFields } from '~/server/utils/loras'
|
import { ensureComfyLoraNames, persistLoraFields } from '~/server/utils/loras'
|
||||||
@@ -243,7 +243,7 @@ export async function runEditV2(job: Job, params: EditV2RunParams) {
|
|||||||
height: params.height,
|
height: params.height,
|
||||||
aspect: params.aspect,
|
aspect: params.aspect,
|
||||||
turbo: params.turbo === true,
|
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,
|
kreaUnetName: krea?.unet,
|
||||||
kreaClipName: krea?.clip,
|
kreaClipName: krea?.clip,
|
||||||
kreaVaeName: krea?.vae,
|
kreaVaeName: krea?.vae,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { AsyncLocalStorage } from 'node:async_hooks'
|
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'
|
import { comfyJobPrefix } from '~/utils/outputNames'
|
||||||
|
|
||||||
const imageHostAls = new AsyncLocalStorage<string>()
|
const imageHostAls = new AsyncLocalStorage<string>()
|
||||||
@@ -369,8 +369,12 @@ async function purgeImageOnDesktop(opts: {
|
|||||||
output?: { filename: string; subfolder: string; type: string }
|
output?: { filename: string; subfolder: string; type: string }
|
||||||
}) {
|
}) {
|
||||||
const config = useRuntimeConfig()
|
const config = useRuntimeConfig()
|
||||||
const controlUrl = String(config.comfyControlUrl || process.env.COMFY_CONTROL_URL || '').replace(/\/$/, '')
|
const controlUrl = String(
|
||||||
const token = String(config.comfyControlToken || process.env.COMFY_CONTROL_TOKEN || '')
|
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
|
if (!controlUrl) return
|
||||||
try {
|
try {
|
||||||
await fetch(`${controlUrl}/purge`, {
|
await fetch(`${controlUrl}/purge`, {
|
||||||
@@ -384,7 +388,9 @@ async function purgeImageOnDesktop(opts: {
|
|||||||
imageName: opts.inputName || '',
|
imageName: opts.inputName || '',
|
||||||
imageSubfolder: opts.inputSubfolder || '',
|
imageSubfolder: opts.inputSubfolder || '',
|
||||||
imageNames: (opts.extraInputNames || []).filter(Boolean),
|
imageNames: (opts.extraInputNames || []).filter(Boolean),
|
||||||
output: opts.output || null
|
output: opts.output || null,
|
||||||
|
sweepPrefixes: comfySweepNamePrefixes(),
|
||||||
|
sweepSubfolders: ['video', 'audio', 'image', 'still']
|
||||||
}),
|
}),
|
||||||
signal: AbortSignal.timeout(8000)
|
signal: AbortSignal.timeout(8000)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -2087,6 +2087,7 @@ export async function importMissingComfyAudio(owner: string, folderId?: string)
|
|||||||
comfyFilename: item.audio.filename
|
comfyFilename: item.audio.filename
|
||||||
})
|
})
|
||||||
imported.push(track)
|
imported.push(track)
|
||||||
|
await purgeComfyArtifacts({ audio: item.audio, promptId: item.promptId })
|
||||||
}
|
}
|
||||||
return imported
|
return imported
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { createJob, emitJob, type Job } from '~/server/utils/jobs'
|
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 { comfyWsUrl } from '~/server/utils/comfy'
|
||||||
import { ensureComfyReady } from '~/server/utils/comfyLifecycle'
|
import { ensureComfyReady } from '~/server/utils/comfyLifecycle'
|
||||||
import { downloadComfyAudio, saveTrack } from '~/server/utils/library'
|
import { downloadComfyAudio, saveTrack } from '~/server/utils/library'
|
||||||
@@ -195,6 +195,10 @@ function watchMusicJob(job: Job): Promise<void> {
|
|||||||
})
|
})
|
||||||
job.trackId = track.id
|
job.trackId = track.id
|
||||||
job.library.audioExt = ext
|
job.library.audioExt = ext
|
||||||
|
await purgeComfyArtifacts({
|
||||||
|
audio,
|
||||||
|
promptId: job.promptId
|
||||||
|
})
|
||||||
}
|
}
|
||||||
job.status = 'complete'
|
job.status = 'complete'
|
||||||
emitJob(job, {
|
emitJob(job, {
|
||||||
|
|||||||
Reference in New Issue
Block a user