Add ACE-Step music generation to the studio and library.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -32,11 +32,13 @@ export default defineEventHandler(async (event) => {
|
||||
const live = listJobs()
|
||||
.filter(job => job.library?.ownerKey === owner && jobLooksLive(job, comfyBusy))
|
||||
.sort((a, b) => b.startedAt - a.startedAt)
|
||||
const videoLive = live.find(job => job.kind !== 'edit')
|
||||
const videoLive = live.find(job => job.kind !== 'edit' && job.kind !== 'music')
|
||||
const editLive = live.find(job => job.kind === 'edit')
|
||||
const musicLive = live.find(job => job.kind === 'music')
|
||||
|
||||
let video = videoLive ? livePayload(videoLive) : null
|
||||
const edit = editLive ? livePayload(editLive) : null
|
||||
const music = musicLive ? livePayload(musicLive) : null
|
||||
|
||||
if (!video) {
|
||||
const pending = listPendingJobs()
|
||||
@@ -70,8 +72,9 @@ export default defineEventHandler(async (event) => {
|
||||
}
|
||||
|
||||
return {
|
||||
jobId: video?.jobId || edit?.jobId || null,
|
||||
jobId: video?.jobId || edit?.jobId || music?.jobId || null,
|
||||
video,
|
||||
edit
|
||||
edit,
|
||||
music
|
||||
}
|
||||
})
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
import { startMusicJob } from '~/server/utils/musicChain'
|
||||
import { comfyConfigured } from '~/server/utils/comfy'
|
||||
import {
|
||||
clampLyricsStrength,
|
||||
clampMusicCfg,
|
||||
clampMusicDuration,
|
||||
clampMusicSteps,
|
||||
parseMusicEngine,
|
||||
parseMusicSeed,
|
||||
sanitizeMusicLyrics,
|
||||
sanitizeMusicTags
|
||||
} from '~/utils/music'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const body = await readBody<{
|
||||
tags?: unknown
|
||||
lyrics?: unknown
|
||||
duration?: unknown
|
||||
steps?: unknown
|
||||
seed?: unknown
|
||||
cfg?: unknown
|
||||
lyricsStrength?: unknown
|
||||
instrumental?: unknown
|
||||
folderId?: unknown
|
||||
name?: unknown
|
||||
engine?: unknown
|
||||
sampler_name?: unknown
|
||||
scheduler?: unknown
|
||||
}>(event).catch(() => ({} as Record<string, unknown>))
|
||||
|
||||
if (!comfyConfigured()) {
|
||||
throw createError({
|
||||
statusCode: 503,
|
||||
statusMessage: 'Beast ComfyUI is not configured. Set COMFY_HOST.'
|
||||
})
|
||||
}
|
||||
|
||||
const instrumental = body.instrumental === true || body.instrumental === 'true'
|
||||
const tags = sanitizeMusicTags(body.tags)
|
||||
const lyrics = sanitizeMusicLyrics(body.lyrics, instrumental)
|
||||
if (!tags) {
|
||||
throw createError({ statusCode: 400, statusMessage: 'Genre and style tags are required' })
|
||||
}
|
||||
if (!instrumental && !lyrics) {
|
||||
throw createError({ statusCode: 400, statusMessage: 'Lyrics are required unless Instrumental is on' })
|
||||
}
|
||||
|
||||
const ownerKey = libraryOwnerKey(event)
|
||||
const library = publicLibrary(event)
|
||||
const folderId = library.folders.some(folder => folder.id === String(body.folderId || ''))
|
||||
? String(body.folderId)
|
||||
: library.folders[0]?.id
|
||||
if (!folderId) {
|
||||
throw createError({ statusCode: 400, statusMessage: 'Create a library folder before generating' })
|
||||
}
|
||||
assertFolderExists(event, folderId)
|
||||
const destFolder = library.folders.find(folder => folder.id === folderId)
|
||||
const folderLocked = Boolean(destFolder?.protected && !destFolder.unlocked)
|
||||
|
||||
const duration = clampMusicDuration(body.duration)
|
||||
const steps = clampMusicSteps(body.steps)
|
||||
const cfg = clampMusicCfg(body.cfg)
|
||||
const lyricsStrength = clampLyricsStrength(body.lyricsStrength)
|
||||
const seed = parseMusicSeed(body.seed)
|
||||
const engine = parseMusicEngine(body.engine)
|
||||
const clipName = allocateOutputName(ownerKey, folderId, String(body.name || '').trim(), {
|
||||
engine: engine === 'ace-step-1.5' ? 'ace15' : 'ace'
|
||||
})
|
||||
|
||||
const job = await startMusicJob({
|
||||
ownerKey,
|
||||
folderId,
|
||||
name: clipName,
|
||||
tags,
|
||||
lyrics,
|
||||
duration,
|
||||
steps,
|
||||
seed,
|
||||
cfg,
|
||||
lyricsStrength,
|
||||
instrumental,
|
||||
folderLocked,
|
||||
engine,
|
||||
samplerName: String(body.sampler_name || 'euler'),
|
||||
scheduler: String(body.scheduler || 'simple')
|
||||
})
|
||||
|
||||
return {
|
||||
jobId: job.id,
|
||||
queued: job.status === 'queued',
|
||||
seed,
|
||||
duration,
|
||||
steps,
|
||||
cfg,
|
||||
folderId,
|
||||
folderLocked,
|
||||
engine,
|
||||
name: clipName
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user