Start waiting image jobs after video, and add Start now on idle queue rows.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Towsty
2026-08-28 18:16:16 -05:00
co-authored by Cursor
parent 0d8766bcb6
commit 5f91553b8a
4 changed files with 30 additions and 14 deletions
+2 -1
View File
@@ -1,5 +1,5 @@
import { listShotQueues, summarizeQueue } from '~/server/utils/shotQueue'
import { listStudioJobs, summarizeStudioJob } from '~/server/utils/studioQueue'
import { kickStudioQueue, listStudioJobs, summarizeStudioJob } from '~/server/utils/studioQueue'
export default defineEventHandler((event) => {
const { owner } = assertLibraryOwner(event)
@@ -51,6 +51,7 @@ export default defineEventHandler((event) => {
.filter(queue => !claimed.has(queue.id) && queue.status !== 'complete')
.map(summarizeQueue)
: []
if (jobs.some(job => job.status === 'waiting')) void kickStudioQueue()
return {
jobs,
orphans,
+24 -9
View File
@@ -423,10 +423,12 @@ export async function requestCutIn(owner: string, id: string) {
const jobsNow = readJobs(owner)
const running = jobsNow.find(job => job.status === 'running')
|| jobsNow.find(job => job.status === 'held')
if (!running) {
throw createError({ statusCode: 409, statusMessage: 'Nothing is generating, so this job can just wait its turn' })
const target = jobsNow.find(job => job.id === id)
if (!target) throw createError({ statusCode: 404, statusMessage: 'Queued job not found' })
if (target.status !== 'waiting') {
throw createError({ statusCode: 409, statusMessage: 'Only a waiting job can cut in' })
}
if (running.id === id) {
if (running?.id === id) {
throw createError({ statusCode: 409, statusMessage: 'That job is already running' })
}
const updated = await mutate(owner, (jobs) => {
@@ -440,7 +442,7 @@ export async function requestCutIn(owner: string, id: string) {
for (const item of jobs) {
if (item.id !== id && item.status === 'waiting') item.cutIn = false
}
const active = jobs.find(item => item.id === running.id)
const active = running ? jobs.find(item => item.id === running.id) : undefined
if (active && active.status === 'running') {
active.holdForCutIn = true
active.resumeAutoRun = active.payload.queueAutoRun === true
@@ -448,14 +450,13 @@ export async function requestCutIn(owner: string, id: string) {
}
return structuredClone(job)
})
const liveId = running.liveJobId
if (liveId) {
const live = getJob(liveId)
if (running?.liveJobId) {
const live = getJob(running.liveJobId)
if (live?.library) {
live.library.stopAfterCurrent = true
}
}
kickStudioQueue()
await kickStudioQueue()
return updated
}
@@ -533,6 +534,15 @@ export function kickStudioQueue() {
return run
}
let kickRetryTimer: ReturnType<typeof setTimeout> | null = null
function scheduleKickRetry() {
if (kickRetryTimer) return
kickRetryTimer = setTimeout(() => {
kickRetryTimer = null
kickStudioQueue()
}, 2500)
}
function pendingAlive(job: StudioJob) {
if (job.liveJobId && readPendingJob(job.liveJobId)?.promptId) return true
if (!job.shotQueueId) return false
@@ -595,7 +605,12 @@ async function dispatchStudioQueue() {
jobs: current.jobs.map(item => structuredClone(item))
}
})
if (await videoJobsBusy()) return
if (await videoJobsBusy()) {
if (listOwnersWithStudioQueues().some(owner => readJobs(owner).some(job => job.status === 'waiting'))) {
scheduleKickRetry()
}
return
}
const cutIn = pickCutIn(store.jobs)
if (cutIn) {
await startStudioJob(cutIn)