Let image edit run while video Comfy is busy.

Split video/edit job state so the sidecar can edit concurrently, and hide v2 identity-ref thumbnails when the input still is hidden.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Towsty
2026-08-26 14:44:56 -05:00
co-authored by Cursor
parent c55a48a57e
commit f54e8da857
6 changed files with 475 additions and 248 deletions
+1
View File
@@ -72,6 +72,7 @@ export default defineEventHandler(async (event) => {
const clipName = nextClipPartName(clipTitle(source))
const job = createJob()
job.kind = 'video'
job.maxStep = steps
job.hideThumbnail = source.hideThumbnail
const tmpDir = extendTempDir(owner, job.id)
+1
View File
@@ -146,6 +146,7 @@ export default defineEventHandler(async (event) => {
const chainTotal = 1 + extensions.length
const job = createJob()
job.kind = 'video'
job.maxStep = steps
job.hideThumbnail = hideThumbnail
job.library = {
+50 -26
View File
@@ -1,35 +1,59 @@
function livePayload(job: ReturnType<typeof getJob> & {}) {
if (!job) return null
return {
jobId: job.id,
kind: job.kind || 'video',
hideThumbnail: job.hideThumbnail === true,
folderLocked: job.library?.folderLocked === true,
...jobSnapshot(job)
}
}
export default defineEventHandler(async (event) => {
const owner = libraryOwnerKey(event)
const live = listJobs()
.filter(job => job.library?.ownerKey === owner && (job.status === 'queued' || job.status === 'uploading' || job.status === 'running'))
.sort((a, b) => b.startedAt - a.startedAt)[0]
if (live) {
return {
jobId: live.id,
hideThumbnail: live.hideThumbnail === true,
folderLocked: live.library?.folderLocked === true,
...jobSnapshot(live)
.sort((a, b) => b.startedAt - a.startedAt)
const videoLive = live.find(job => job.kind !== 'edit')
const editLive = live.find(job => job.kind === 'edit')
let video = videoLive ? livePayload(videoLive) : null
const edit = editLive ? livePayload(editLive) : null
if (!video) {
const pending = listPendingJobs()
.filter(job => job.ownerKey === owner)
.sort((a, b) => b.startedAt - a.startedAt)[0]
if (pending) {
const resolved = await resolvePendingJob(pending).catch(() => null)
if (resolved) {
video = {
jobId: pending.jobId,
kind: 'video' as const,
hideThumbnail: pending.hideThumbnail === true,
folderLocked: pending.folderLocked === true,
...resolved
}
} else {
video = {
jobId: pending.jobId,
kind: 'video',
type: 'snapshot',
status: 'running',
message: 'Reconnecting to ComfyUI...',
progress: Math.max(8, Math.min(90, Math.round((Date.now() - pending.startedAt) / 1000))),
promptId: pending.promptId,
elapsedMs: Date.now() - pending.startedAt,
hideThumbnail: pending.hideThumbnail === true,
folderLocked: pending.folderLocked === true
}
}
}
}
const pending = listPendingJobs()
.filter(job => job.ownerKey === owner)
.sort((a, b) => b.startedAt - a.startedAt)[0]
if (pending) {
const resolved = await resolvePendingJob(pending).catch(() => null)
if (resolved) return resolved
return {
jobId: pending.jobId,
type: 'snapshot',
status: 'running',
message: 'Reconnecting to ComfyUI...',
progress: Math.max(8, Math.min(90, Math.round((Date.now() - pending.startedAt) / 1000))),
promptId: pending.promptId,
elapsedMs: Date.now() - pending.startedAt,
hideThumbnail: pending.hideThumbnail === true,
folderLocked: pending.folderLocked === true
}
return {
jobId: video?.jobId || edit?.jobId || null,
video,
edit
}
return { jobId: null }
})
+3 -2
View File
@@ -1,5 +1,5 @@
export default defineEventHandler(async (event) => {
const body = await readBody<{ jobId?: string }>(event).catch(() => ({}))
const body = await readBody<{ jobId?: string; kind?: string }>(event).catch(() => ({}))
const jobId = String(body?.jobId || '')
const job = jobId ? getJob(jobId) : undefined
if (job) {
@@ -8,7 +8,8 @@ export default defineEventHandler(async (event) => {
removeExtendTemp(job.library?.extendTmpDir)
}
if (jobId) deletePendingJob(jobId)
const ok = job?.kind === 'edit'
const kind = job?.kind || (body?.kind === 'edit' ? 'edit' : 'video')
const ok = kind === 'edit'
? await interruptImageComfy()
: await interruptComfy()
if (job) {