import { existsSync } from 'node:fs' import { upscaleOptions } from '~/shared/video-upscale.mjs' import { addStudioJob, listStudioJobs, kickStudioQueue, type StudioJobPayload } from '../../../../utils/studioQueue' import { listJobs } from '../../../../utils/jobs' const submitting = new Set() export default defineEventHandler(async event => { const { owner } = assertLibraryOwner(event) const clip = getClip(owner, String(getRouterParam(event, 'id') || '')) assertFolderAccess(event, clip.folderId) const body = await readBody(event) let options try { options = upscaleOptions(body) } catch { throw createError({ statusCode: 400, statusMessage: 'Invalid upscale settings.' }) } const key = `${owner}:${clip.id}` if (submitting.has(key)) throw createError({ statusCode: 409, statusMessage: 'This clip is already being queued.' }) submitting.add(key) try { if (!existsSync(clipVideoPath(owner, clip.id))) throw createError({ statusCode: 404, statusMessage: 'Completed video file is missing.' }) if (listStudioJobs(owner).some(row => ['waiting', 'held', 'running'].includes(row.status) && (row.payload.upscale?.sourceId === clip.id || row.status === 'running' && row.payload.extendFromClipId === clip.id)) || listJobs().some(job => job.library?.ownerKey === owner && job.clipId === clip.id && ['running', 'queued', 'uploading'].includes(job.status))) throw createError({ statusCode: 409, statusMessage: 'This clip already has a generation or upscale in progress.' }) const payload = { ...clip, name: `Upscale ยท ${clip.name || 'Video'}`, prompt: clip.prompt || 'Upscale video', folderId: clip.folderId, steps: 0, cfg: 0, seed: 0, turbo: false, fps: clip.fps || 24, duration: clip.duration || 0, sound: clip.sound !== false, samplerName: '', scheduler: '', useIdentityRefs: false, referenceStillIds: [], extensions: [], queueAutoRun: true, upscale: { ...options, sourceId: clip.id } } as StudioJobPayload const job = await addStudioJob({ ownerKey: owner, payload, familyId: crypto.randomUUID(), kind: 'video' }) kickStudioQueue() return { id: job.id, status: 'queued' } } finally { submitting.delete(key) } })