import { emitJob, getJob, listJobs, type Job } from '~/server/utils/jobs' import { listStudioJobs, type StudioJob } from '~/server/utils/studioQueue' const LIVE_ERROR_KEEP_MS = 60 * 60 * 1000 function liveMusicActive(job: Job) { if (job.kind !== 'music') return false if (job.status === 'queued' || job.status === 'uploading' || job.status === 'running') return true if (job.status === 'error' && Date.now() - job.startedAt < LIVE_ERROR_KEEP_MS) return true return false } export function listLiveMusicJobs(owner: string) { return listJobs() .filter(job => liveMusicActive(job) && job.library?.ownerKey === owner) .sort((a, b) => b.startedAt - a.startedAt) } export function summarizeLiveMusicJob(job: Job) { const lib = job.library const tags = String(lib?.tags || lib?.prompt || '').trim() return { id: job.id, createdAt: job.startedAt, updatedAt: job.startedAt, status: job.status === 'error' ? 'error' : 'running', kind: 'music' as const, name: (lib?.name || '').trim() || tags.slice(0, 80) || 'Music', prompt: tags, shotCount: 1, liveJobId: job.id, source: 'live' as const, lastError: job.error, hideThumbnail: true, folderLocked: lib?.folderLocked === true } } export function inspectStudioJob(job: StudioJob) { return { source: 'studio' as const, id: job.id, createdAt: job.createdAt, updatedAt: job.updatedAt, status: job.status, kind: job.kind === 'music' ? 'music' as const : job.kind === 'edit' ? 'edit' as const : 'video' as const, name: job.name, prompt: job.prompt, shotCount: job.shotCount, familyId: job.familyId, shotQueueId: job.shotQueueId, liveJobId: job.liveJobId, lastError: job.lastError, imagePipeline: job.payload.imagePipeline || 'v1', cutIn: job.cutIn === true, holdForCutIn: job.holdForCutIn === true, pauseAfterCurrent: job.pauseAfterCurrent === true, pausedByUser: job.pausedByUser === true, payload: job.kind === 'music' ? { ...job.payload, tags: job.payload.prompt } : job.payload } } export function inspectLiveMusicJob(job: Job) { const lib = job.library const tags = String(lib?.tags || lib?.prompt || '').trim() return { source: 'live' as const, id: job.id, createdAt: job.startedAt, updatedAt: job.startedAt, status: job.status === 'error' ? 'error' : job.status === 'cancelled' ? 'cancelled' : 'running', kind: 'music' as const, name: (lib?.name || '').trim() || tags.slice(0, 80) || 'Music', prompt: tags, shotCount: 1, liveJobId: job.id, lastError: job.error, imagePipeline: undefined, payload: { name: lib?.name || '', prompt: tags, tags, lyrics: lib?.lyrics || '', instrumental: lib?.instrumental === true, folderId: lib?.folderId || '', duration: lib?.duration, steps: lib?.steps, seed: lib?.seed, cfg: lib?.cfg, engine: lib?.engine, lyricsStrength: lib?.lyricsStrength, hideThumbnail: true, folderLocked: lib?.folderLocked === true } } } export function findInspectJob(owner: string, id: string) { const studio = listStudioJobs(owner).find(item => item.id === id) if (studio) return inspectStudioJob(studio) const live = getJob(id) if (live?.kind === 'music' && live.library?.ownerKey === owner) return inspectLiveMusicJob(live) return null } export async function cancelLiveMusicJob(owner: string, id: string) { const live = getJob(id) if (!live || live.kind !== 'music' || live.library?.ownerKey !== owner) { throw createError({ statusCode: 404, statusMessage: 'Queued job not found' }) } live.status = 'cancelled' if (live.library) live.library.stopAfterCurrent = true emitJob(live, { type: 'error', error: 'Job interrupted.', message: 'Job interrupted.' }) const { interruptComfy } = await import('~/server/utils/comfy') await interruptComfy().catch(() => false) }