Improve music activity reporting and preserve Comfy host fixes

This commit is contained in:
Towsty
2026-09-06 23:05:46 -05:00
parent 53bb58bbd5
commit 5f96807496
8 changed files with 246 additions and 10 deletions
+55 -8
View File
@@ -186,9 +186,13 @@
@click="inspectJobId = studioJobId || liveMusicId"
>Inspect this job</button>
</div>
<div v-if="busy" class="h-2 overflow-hidden rounded-full bg-zinc-800">
<div class="progress-glow h-full rounded-full bg-amber-400 transition-[width]" :style="{ width: `${Math.max(progress, 4)}%` }" />
<div v-if="busy" class="h-2 overflow-hidden rounded-full bg-zinc-800" role="progressbar" aria-label="Music generation" :aria-valuenow="indeterminateMusic ? undefined : progress" :aria-valuetext="indeterminateMusic ? 'Completion percentage unavailable' : `${progress}%`">
<div class="progress-glow h-full rounded-full bg-amber-400 transition-[width]" :class="{ 'music-activity': indeterminateMusic && recentMusicCheck }" :style="{ width: indeterminateMusic ? '30%' : `${Math.max(progress, 4)}%` }" />
</div>
<p v-if="busy && !queued && jobId" class="text-xs text-zinc-400">
{{ musicElapsed }} elapsed · {{ musicActivityText }}
<span v-if="indeterminateMusic" class="mt-1 block">YuE does not report a completion percentage during this stage.</span>
</p>
<AudioPlayer
v-if="audioUrl"
:src="audioUrl"
@@ -257,6 +261,23 @@ const forceClearing = ref(false)
const error = ref('')
const status = ref('')
const progress = ref(0)
const musicClock = ref(Date.now())
const musicStartedAt = ref(0)
const activeMusicEngine = ref('')
const musicActivity = ref<{ checkedAt: number; running: boolean } | null>(null)
let musicClockTimer: ReturnType<typeof setInterval> | null = null
const indeterminateMusic = computed(() => !queued.value && activeMusicEngine.value === 'yue' && progress.value < 98)
const recentMusicCheck = computed(() => Boolean(musicActivity.value?.running && musicClock.value - musicActivity.value.checkedAt < 15000))
const musicElapsed = computed(() => {
const seconds = musicStartedAt.value ? Math.max(0, Math.floor((musicClock.value - musicStartedAt.value) / 1000)) : 0
return `${Math.floor(seconds / 60)}:${String(seconds % 60).padStart(2, '0')}`
})
const musicActivityText = computed(() => {
if (!musicActivity.value) return 'Waiting for a Comfy activity check…'
const age = Math.max(0, Math.floor((musicClock.value - musicActivity.value.checkedAt) / 1000))
if (age >= 15) return `Activity check delayed · last response ${age}s ago`
return musicActivity.value.running ? `Comfy confirms this job is running · checked ${age}s ago` : 'Comfy responded · checking for the result…'
})
const audioUrl = ref('')
const trackId = ref('')
const jobId = ref('')
@@ -367,12 +388,23 @@ function stopRecoverPoll() {
async function tryRecover() {
if (settled || !busy.value || queued.value) return
const currentId = jobId.value
if (currentId) {
try {
const snapshot = await $fetch<Record<string, any>>(`/api/generate/${currentId}`, { timeout: 10000 })
if (jobId.value !== currentId || settled) return
applyEvent(snapshot)
return
} catch {
// Fall back to saved-output recovery when the live job is unavailable.
}
}
try {
const recovered = await $fetch<Record<string, any>>('/api/generate/recover', {
method: 'POST',
body: { jobId: jobId.value || undefined }
})
if (recovered?.trackId) applyEvent(recovered)
if (jobId.value === currentId && !settled && recovered?.trackId) applyEvent(recovered)
} catch {
/* still running */
}
@@ -393,14 +425,17 @@ async function attachLiveMusic(opts: {
settled = false
if (typeof opts.progress === 'number') progress.value = opts.progress
if (opts.message) status.value = opts.message
else if (!status.value || /waiting for a generate|waiting in the job queue|waiting for gpu/i.test(status.value)) {
else if (!status.value || /queueing|waiting for a generate|waiting in the job queue|waiting for gpu/i.test(status.value)) {
status.value = selectedEngine.value === 'yue'
? 'YuE running on Comfy — Stage A can take 10–20+ minutes'
: 'Generating…'
}
if (jobId.value !== liveId) {
musicActivity.value = null
musicStartedAt.value = Date.now()
jobId.value = liveId
listen(liveId)
void tryRecover()
}
if (!recoverPoll) {
recoverPoll = setInterval(() => { void tryRecover() }, 8000)
@@ -419,7 +454,9 @@ async function refreshStudioQueue() {
waitingCount?: number
}>('/api/studio-queue').catch(() => ({ jobs: [] as Array<{ id: string; status: string; kind?: string; liveJobId?: string; lastError?: string }>, waitingCount: 0 }))
const rows = data.jobs || []
const musicRow = rows.find(job => job.kind === 'music' && (job.status === 'waiting' || job.status === 'running' || job.status === 'held'))
const musicRow = rows.find(job => job.kind === 'music' && job.id === studioJobId.value)
|| rows.find(job => job.kind === 'music' && job.status === 'running')
|| rows.find(job => job.kind === 'music' && (job.status === 'waiting' || job.status === 'held'))
liveMusicId.value = musicRow?.id || ''
queueCount.value = data.waitingCount || rows.filter(job => job.status === 'waiting').length
@@ -493,6 +530,9 @@ async function resumeActiveMusic() {
}
function applyEvent(payload: Record<string, any>) {
if (typeof payload.elapsedMs === 'number') musicStartedAt.value = Date.now() - payload.elapsedMs
if (payload.engine) activeMusicEngine.value = payload.engine
if (payload.musicActivity) musicActivity.value = payload.musicActivity
if (payload.message) status.value = payload.message
if (typeof payload.progress === 'number') progress.value = payload.progress
if (payload.type === 'complete' || payload.status === 'complete') {
@@ -628,13 +668,12 @@ async function generate() {
await refreshStudioQueue()
stopQueuePoll()
queuePoll = setInterval(() => { void refreshStudioQueue() }, 2500)
if (started.queued) {
if (started.queued && !jobId.value) {
queued.value = true
status.value = 'Waiting in the job queue…'
return
}
jobId.value = started.jobId
listen(started.jobId)
if (!jobId.value) await attachLiveMusic({ studioId: started.studioJobId, liveId: started.jobId })
stopRecoverPoll()
recoverPoll = setInterval(() => { void tryRecover() }, 8000)
window.setTimeout(() => { void tryRecover() }, 12000)
@@ -721,6 +760,7 @@ function applyPreset() {
}
onMounted(async () => {
musicClockTimer = setInterval(() => { musicClock.value = Date.now() }, 1000)
await loadLibrary()
await loadMusicPresets()
const route = useRoute()
@@ -749,8 +789,15 @@ onMounted(async () => {
})
onBeforeUnmount(() => {
if (musicClockTimer) clearInterval(musicClockTimer)
stopListen()
stopRecoverPoll()
stopQueuePoll()
})
</script>
<style scoped>
.music-activity { animation: music-travel 2s ease-in-out infinite alternate; }
@keyframes music-travel { from { transform: translateX(0); } to { transform: translateX(233%); } }
@media (prefers-reduced-motion: reduce) { .music-activity { animation: none; } }
</style>