Put ACE-Step music jobs on the same studio queue as video and image.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Towsty
2026-09-03 00:08:07 -05:00
co-authored by Cursor
parent 9cb8b9bda3
commit 53990192e4
8 changed files with 233 additions and 34 deletions
+24 -4
View File
@@ -11,7 +11,7 @@
<div>
<h2 class="font-display text-2xl font-bold">Queue</h2>
<p class="mt-1 max-w-2xl text-sm text-zinc-400">
Jobs you submitted from Studio. A long shot script is one job; its shots are the list underneath. Run next inserts a waiting job after the current shot as its own clip.
Jobs you submitted from Studio or Music. A long shot script is one job; its shots are the list underneath. Run next inserts a waiting job after the current shot as its own clip.
</p>
</div>
<div class="flex flex-wrap items-center gap-2 sm:gap-3">
@@ -47,7 +47,7 @@
This batch is paused. It will not continue until you resume.
</p>
<p v-else-if="!jobs.length && !orphans.length" class="glass rounded-3xl px-5 py-10 text-center text-sm text-zinc-400">
Nothing queued. Generate from Studio — a 30-shot script takes one slot, and you can line up more jobs behind it.
Nothing queued. Generate from Studio or Music — a 30-shot script takes one slot, and you can line up more jobs behind it.
</p>
<section
@@ -572,6 +572,22 @@ function progressLine(queue: Queue) {
}
function jobLine(job: StudioJobRow) {
if (job.kind === 'music') {
if (job.status === 'waiting') {
return job.cutIn
? 'Next after the current shot · ACE-Step'
: 'Waiting · ACE-Step'
}
if (job.status === 'held') {
if (jobIsHeldPaused(job)) return 'Paused. This track will not continue until you resume.'
return job.holdForCutIn ? 'Paused so another job can cut in' : 'Paused'
}
if (job.status === 'running') {
if (jobIsLatched(job)) return 'Generating track · will pause after this'
return 'Generating track'
}
return job.status
}
if (job.kind === 'edit') {
const passes = job.shotCount || 1
const passLine = passes > 1 ? `${passes} passes` : 'image edit'
@@ -790,7 +806,7 @@ function listenJob(job: StudioJobRow) {
void loadQueues()
}
if (payload.type === 'complete' && !(Number(payload.queuedRemaining) > 0)) {
void notifyGenerationComplete(job.kind === 'edit' || payload.stillId ? 'image' : 'video')
void notifyGenerationComplete(job.kind === 'music' ? 'music' : job.kind === 'edit' || payload.stillId ? 'image' : 'video')
}
if (payload.type === 'complete' || payload.type === 'error') {
source.close()
@@ -801,6 +817,7 @@ function listenJob(job: StudioJobRow) {
}
function jobKindLabel(job: StudioJobRow) {
if (job.kind === 'music') return 'MUSIC'
if (job.kind === 'edit') {
if (job.imagePipeline === 'v2') return 'IMAGE V2'
return (job.shotCount || 1) > 1 ? 'EDIT' : 'IMAGE'
@@ -824,6 +841,7 @@ function promptSnippet(text?: string) {
}
function shotProgressDetail(job: StudioJobRow) {
if (job.kind === 'music') return promptSnippet(job.prompt)
const queue = job.shots
if (!queue) return job.kind === 'edit' ? promptSnippet(job.prompt) : ''
const total = queue.totalCount || job.shotCount || 1
@@ -841,7 +859,9 @@ function askRemoveJob(job: StudioJobRow) {
name: job.name || 'Untitled job',
detail: shotProgressDetail(job),
consequence: generating
? 'This stops the job that’s generating on Beast and removes only this row. Other queue items stay. Saved clips stay in the library.'
? (job.kind === 'music'
? 'This stops the track that’s generating on Beast and removes only this row. Other queue items stay. Saved tracks stay in the library.'
: 'This stops the job that’s generating on Beast and removes only this row. Other queue items stay. Saved clips stay in the library.')
: pausedBatch
? 'This drops the rest of this shot list only. It will not resume, and it will not stop a different job that’s generating. Saved clips stay in the library.'
: 'This removes only this job from the queue. Other items stay. Saved clips stay in the library.',