Let queued video, image, and music jobs open in an inspect modal.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+76
-1
@@ -124,14 +124,32 @@
|
||||
<p v-if="blockReason" class="text-xs text-zinc-400">{{ blockReason }}</p>
|
||||
<p v-else-if="queued" class="text-xs text-zinc-400">
|
||||
Waiting in the job queue.
|
||||
<button type="button" class="text-amber-200 hover:text-amber-100" @click="inspectJobId = studioJobId || liveMusicId">Inspect</button>
|
||||
·
|
||||
<NuxtLink to="/queue" class="text-amber-200 hover:text-amber-100">Open Queue</NuxtLink>
|
||||
</p>
|
||||
|
||||
<div v-if="musicPresets.length" class="rounded-2xl border border-white/10 bg-zinc-950/40 p-4">
|
||||
<label class="block text-sm">
|
||||
<span class="mb-1 block font-medium text-zinc-300">Templates</span>
|
||||
<select v-model="presetId" class="w-full rounded-xl border border-white/10 bg-zinc-950 px-3 py-2 text-sm" @change="applyPreset">
|
||||
<option value="">Load a saved template</option>
|
||||
<option v-for="preset in musicPresets" :key="preset.id" :value="preset.id">{{ preset.name }}</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="glass min-w-0 max-w-full space-y-4 rounded-3xl p-5 sm:p-6">
|
||||
<div>
|
||||
<h2 class="font-display text-2xl font-bold">Output</h2>
|
||||
<p class="mt-1 text-sm text-zinc-400">{{ status || 'Waiting for a generate.' }}</p>
|
||||
<button
|
||||
v-if="studioJobId || liveMusicId"
|
||||
type="button"
|
||||
class="mt-2 text-xs text-amber-200 hover:text-amber-100"
|
||||
@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)}%` }" />
|
||||
@@ -150,6 +168,7 @@
|
||||
</p>
|
||||
</section>
|
||||
</main>
|
||||
<QueuedJobModal :job-id="inspectJobId" @close="inspectJobId = ''" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -167,6 +186,8 @@ import {
|
||||
} from '~/utils/music'
|
||||
import { ensureGenerationNotifyPermission, notifyGenerationComplete } from '~/utils/generationNotify'
|
||||
import type { LibraryFolder } from '~/utils/libraryBrowse'
|
||||
import { LOAD_MUSIC_JOB_KEY } from '~/utils/queuedJob'
|
||||
import type { GenerationPreset, MusicPresetSettings } from '~/utils/generationPresets'
|
||||
|
||||
const tagsPlaceholder = DEFAULT_MUSIC_TAGS
|
||||
const lyricsPlaceholder = DEFAULT_MUSIC_LYRICS
|
||||
@@ -197,6 +218,10 @@ const studioJobId = ref('')
|
||||
const queued = ref(false)
|
||||
const queueCount = ref(0)
|
||||
const downloadName = ref('track.flac')
|
||||
const inspectJobId = ref('')
|
||||
const liveMusicId = ref('')
|
||||
const musicPresets = ref<GenerationPreset[]>([])
|
||||
const presetId = ref('')
|
||||
let source: EventSource | null = null
|
||||
let listenGen = 0
|
||||
let settled = false
|
||||
@@ -253,12 +278,15 @@ async function refreshStudioQueue() {
|
||||
jobs?: Array<{
|
||||
id: string
|
||||
status: string
|
||||
kind?: string
|
||||
liveJobId?: string
|
||||
lastError?: string
|
||||
}>
|
||||
waitingCount?: number
|
||||
}>('/api/studio-queue').catch(() => ({ jobs: [] as Array<{ id: string; status: string; liveJobId?: string; lastError?: string }>, waitingCount: 0 }))
|
||||
}>('/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'))
|
||||
liveMusicId.value = musicRow?.id || ''
|
||||
queueCount.value = data.waitingCount || rows.filter(job => job.status === 'waiting').length
|
||||
if (!studioJobId.value) return
|
||||
const row = rows.find(job => job.id === studioJobId.value)
|
||||
@@ -406,8 +434,55 @@ watch(instrumental, (on) => {
|
||||
if (on && !lyrics.value.trim()) lyrics.value = DEFAULT_MUSIC_LYRICS
|
||||
})
|
||||
|
||||
function applyIncomingMusic() {
|
||||
try {
|
||||
const raw = sessionStorage.getItem(LOAD_MUSIC_JOB_KEY)
|
||||
if (!raw) return
|
||||
sessionStorage.removeItem(LOAD_MUSIC_JOB_KEY)
|
||||
const loaded = JSON.parse(raw) as {
|
||||
name?: string
|
||||
tags?: string
|
||||
lyrics?: string
|
||||
instrumental?: boolean
|
||||
duration?: number
|
||||
steps?: number
|
||||
seed?: string
|
||||
folderId?: string
|
||||
}
|
||||
if (typeof loaded.tags === 'string') tags.value = loaded.tags
|
||||
if (typeof loaded.lyrics === 'string') lyrics.value = loaded.lyrics
|
||||
instrumental.value = loaded.instrumental === true
|
||||
if (typeof loaded.duration === 'number') duration.value = loaded.duration
|
||||
if (typeof loaded.steps === 'number') steps.value = loaded.steps
|
||||
if (typeof loaded.seed === 'string') seed.value = loaded.seed
|
||||
if (typeof loaded.name === 'string') name.value = loaded.name
|
||||
if (loaded.folderId && folders.value.some(folder => folder.id === loaded.folderId)) folderId.value = loaded.folderId
|
||||
status.value = 'Loaded from the queue'
|
||||
} catch { /* ignore */ }
|
||||
}
|
||||
|
||||
async function loadMusicPresets() {
|
||||
const data = await $fetch<{ presets?: GenerationPreset[] }>('/api/generation-presets', { query: { kind: 'music' } }).catch(() => ({ presets: [] }))
|
||||
musicPresets.value = data.presets || []
|
||||
}
|
||||
|
||||
function applyPreset() {
|
||||
const preset = musicPresets.value.find(item => item.id === presetId.value)
|
||||
if (!preset) return
|
||||
const settings = preset.settings as MusicPresetSettings
|
||||
if (typeof settings.tags === 'string') tags.value = settings.tags
|
||||
if (typeof settings.lyrics === 'string') lyrics.value = settings.lyrics
|
||||
instrumental.value = settings.instrumental === true
|
||||
if (typeof settings.duration === 'number') duration.value = settings.duration
|
||||
if (typeof settings.steps === 'number') steps.value = settings.steps
|
||||
if (typeof settings.seed === 'number') seed.value = String(settings.seed)
|
||||
name.value = preset.name
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await loadLibrary()
|
||||
await loadMusicPresets()
|
||||
applyIncomingMusic()
|
||||
await refreshStudioQueue()
|
||||
stopQueuePoll()
|
||||
queuePoll = setInterval(() => { void refreshStudioQueue() }, 8000)
|
||||
|
||||
Reference in New Issue
Block a user