Replace Comfy YuE with standalone YuEGP profile 1
This commit is contained in:
+36
-15
@@ -26,6 +26,14 @@
|
||||
|
||||
<GenerationSettings :advanced="advanced">
|
||||
<label class="block space-y-2 text-sm"><span>Model</span><select :value="engineFamily" class="w-full rounded-xl border border-white/15 bg-zinc-900 p-3" @change="selectEngineFamily(($event.target as HTMLSelectElement).value as 'ace' | 'yue')"><option value="ace">ACE-Step</option><option value="yue">YuE</option></select></label>
|
||||
<label v-if="engineFamily === 'yue'" class="block space-y-2 text-sm">
|
||||
<span>YuEGP profile</span>
|
||||
<select v-model.number="yueProfile" class="w-full rounded-xl border border-white/15 bg-zinc-900 p-3">
|
||||
<option :value="1">1 · Full quality · 16GB (default)</option>
|
||||
<option :value="3">3 · Lower memory · Manual fallback</option>
|
||||
</select>
|
||||
<span class="block text-xs text-zinc-400">One lyric section. Compile off. Profile 3 is never selected automatically.</span>
|
||||
</label>
|
||||
<div v-if="!advanced" class="space-y-4">
|
||||
<label v-if="engineFamily !== 'yue'" class="block text-sm">Steps<input v-model.number="steps" type="number" :min="stepsMin" :max="stepsMax" class="mt-2 w-full rounded-xl border border-white/15 bg-zinc-900 p-3"></label>
|
||||
<label class="block text-sm">Seed<input v-model="seed" placeholder="random" class="mt-2 w-full rounded-xl border border-white/15 bg-zinc-900 p-3"></label>
|
||||
@@ -65,7 +73,7 @@
|
||||
<div class="mb-1 flex flex-wrap items-center justify-between gap-2">
|
||||
<span class="text-sm font-medium text-zinc-300">Lyrics</span>
|
||||
<label class="flex cursor-pointer items-center gap-2 text-xs text-zinc-400">
|
||||
<input v-model="instrumental" type="checkbox" class="accent-amber-400">
|
||||
<input v-model="instrumental" :disabled="engineFamily === 'yue'" type="checkbox" class="accent-amber-400">
|
||||
Instrumental
|
||||
</label>
|
||||
</div>
|
||||
@@ -100,7 +108,7 @@
|
||||
:max="durationMax"
|
||||
step="5"
|
||||
>
|
||||
<span class="mt-1 block text-[11px] text-zinc-500">{{ engineFamily === 'yue' ? 'YuE uses this as stage-A length' : `${durationMin}–${durationMax} seconds` }}</span>
|
||||
<span class="mt-1 block text-[11px] text-zinc-500">{{ engineFamily === 'yue' ? 'Approximate length · one lyric section' : `${durationMin}–${durationMax} seconds` }}</span>
|
||||
</label>
|
||||
<label v-if="advanced && engineFamily !== 'yue'" class="block text-sm">
|
||||
<span class="mb-1 block font-medium text-zinc-300">Steps · {{ steps }}</span>
|
||||
@@ -200,7 +208,8 @@
|
||||
</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>
|
||||
<span v-if="activeMusicEngine === 'yue' && !indeterminateMusic" class="mt-1 block">Progress is for the current stage.</span>
|
||||
<span v-if="indeterminateMusic" class="mt-1 block">Waiting for a progress report.</span>
|
||||
</p>
|
||||
<AudioPlayer
|
||||
v-if="audioUrl"
|
||||
@@ -237,7 +246,8 @@ import {
|
||||
MUSIC_STEPS_MIN,
|
||||
MUSIC_STRUCTURE_CHIPS,
|
||||
parseMusicEngine,
|
||||
type MusicEngine
|
||||
type MusicEngine,
|
||||
yueLyricsProblem,
|
||||
} from '~/utils/music'
|
||||
import { ensureGenerationNotifyPermission, notifyGenerationComplete } from '~/utils/generationNotify'
|
||||
import type { LibraryFolder } from '~/utils/libraryBrowse'
|
||||
@@ -248,7 +258,7 @@ const tagsPlaceholder = DEFAULT_MUSIC_TAGS
|
||||
const lyricsPlaceholder = DEFAULT_MUSIC_LYRICS
|
||||
const chips = MUSIC_STRUCTURE_CHIPS
|
||||
const durationMin = MUSIC_DURATION_MIN
|
||||
const durationMax = MUSIC_DURATION_MAX
|
||||
const durationMax = computed(() => engineFamily.value === 'yue' ? 150 : MUSIC_DURATION_MAX)
|
||||
const stepsMin = MUSIC_STEPS_MIN
|
||||
const stepsMax = MUSIC_STEPS_MAX
|
||||
|
||||
@@ -266,6 +276,7 @@ const folders = ref<LibraryFolder[]>([])
|
||||
const lyricsEl = ref<HTMLTextAreaElement | null>(null)
|
||||
const ace15 = ref(false)
|
||||
const engineFamily = ref<'ace' | 'yue'>('ace')
|
||||
const yueProfile = ref<1 | 3>(1)
|
||||
const busy = ref(false)
|
||||
const forceClearing = ref(false)
|
||||
const error = ref('')
|
||||
@@ -276,17 +287,17 @@ 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 indeterminateMusic = computed(() => !queued.value && activeMusicEngine.value === 'yue' && progress.value < 98 && !musicActivity.value?.running)
|
||||
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…'
|
||||
if (!musicActivity.value) return 'Waiting for a generator 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…'
|
||||
return musicActivity.value.running ? `Generator confirms this job is running · checked ${age}s ago` : 'Generator responded · checking for the result…'
|
||||
})
|
||||
const audioUrl = ref('')
|
||||
const trackId = ref('')
|
||||
@@ -305,7 +316,7 @@ let settled = false
|
||||
let queuePoll: ReturnType<typeof setInterval> | null = null
|
||||
let recoverPoll: ReturnType<typeof setInterval> | null = null
|
||||
|
||||
const blocked = computed(() => !tags.value.trim() || (!instrumental.value && !lyrics.value.trim()) || !folderId.value)
|
||||
const blocked = computed(() => Boolean(blockReason.value))
|
||||
const headerSubtitle = computed(() => {
|
||||
if (engineFamily.value === 'yue') return 'YuE · lyrics-to-song'
|
||||
return ace15.value ? 'ACE-Step 1.5 · vocals' : 'ACE-Step · vocals'
|
||||
@@ -316,13 +327,18 @@ const selectedEngine = computed<MusicEngine>(() => {
|
||||
})
|
||||
const blockReason = computed(() => {
|
||||
if (!folderId.value) return 'Choose a library folder before generating.'
|
||||
if (engineFamily.value === 'yue' && instrumental.value) return 'YuEGP requires lyrics. Use ACE for instrumental music.'
|
||||
if (engineFamily.value === 'yue' && duration.value > 150) return 'YuEGP supports up to 150 seconds per section.'
|
||||
if (!tags.value.trim()) return 'Add genre and style tags.'
|
||||
if (engineFamily.value === 'yue') { const problem = yueLyricsProblem(lyrics.value); if (problem) return problem }
|
||||
if (!instrumental.value && !lyrics.value.trim()) return 'Write lyrics, or turn on Instrumental.'
|
||||
return ''
|
||||
})
|
||||
|
||||
function selectEngineFamily(family: 'ace' | 'yue') {
|
||||
engineFamily.value = family
|
||||
if (family === 'yue') { instrumental.value = false; duration.value = Math.min(150, duration.value) }
|
||||
if (family === 'yue' && lyrics.value === DEFAULT_MUSIC_LYRICS) lyrics.value = '[Verse 1]\n'
|
||||
if (family === 'ace' && ace15.value && steps.value === MUSIC_STEPS_DEFAULT) {
|
||||
steps.value = MUSIC_STEPS_DEFAULT_15
|
||||
}
|
||||
@@ -437,7 +453,7 @@ async function attachLiveMusic(opts: {
|
||||
if (opts.message) status.value = opts.message
|
||||
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'
|
||||
? 'YuEGP is generating your song'
|
||||
: 'Generating…'
|
||||
}
|
||||
if (jobId.value !== liveId) {
|
||||
@@ -482,7 +498,7 @@ async function refreshStudioQueue() {
|
||||
status.value = musicRow.status === 'held'
|
||||
? 'Paused in the job queue'
|
||||
: (selectedEngine.value === 'yue'
|
||||
? 'Waiting for GPU… YuE is heavy; do not spam Generate'
|
||||
? 'Waiting for the GPU to run YuEGP'
|
||||
: 'Waiting in the job queue…')
|
||||
}
|
||||
|
||||
@@ -494,7 +510,7 @@ async function refreshStudioQueue() {
|
||||
}
|
||||
if (jobId.value && /waiting in the job queue/i.test(status.value)) {
|
||||
status.value = selectedEngine.value === 'yue'
|
||||
? 'YuE running on Comfy — Stage A can take 10–20+ minutes'
|
||||
? 'YuEGP is generating your song'
|
||||
: 'Generating…'
|
||||
}
|
||||
return
|
||||
@@ -505,7 +521,7 @@ async function refreshStudioQueue() {
|
||||
status.value = row.status === 'held'
|
||||
? 'Paused in the job queue'
|
||||
: (selectedEngine.value === 'yue'
|
||||
? 'Waiting for GPU… YuE is heavy; do not spam Generate'
|
||||
? 'Waiting for the GPU to run YuEGP'
|
||||
: 'Waiting in the job queue…')
|
||||
return
|
||||
}
|
||||
@@ -553,7 +569,7 @@ function applyEvent(payload: Record<string, any>) {
|
||||
if (payload.trackId) {
|
||||
trackId.value = payload.trackId
|
||||
audioUrl.value = `/api/library/tracks/${payload.trackId}/audio`
|
||||
downloadName.value = `${(name.value || 'track').replace(/[^\w.-]+/g, '-')}.flac`
|
||||
downloadName.value = `${(name.value || 'track').replace(/[^\w.-]+/g, '-')}.${payload.audioExt === 'wav' ? 'wav' : 'flac'}`
|
||||
} else if (payload.filename) {
|
||||
const params = new URLSearchParams({
|
||||
filename: payload.filename,
|
||||
@@ -669,7 +685,8 @@ async function generate() {
|
||||
instrumental: instrumental.value,
|
||||
folderId: folderId.value,
|
||||
name: name.value.trim(),
|
||||
engine: selectedEngine.value
|
||||
engine: selectedEngine.value,
|
||||
...(engineFamily.value === 'yue' ? { yueProfile: yueProfile.value } : {})
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -715,6 +732,7 @@ function applyIncomingMusic() {
|
||||
if (payload.seed != null) seed.value = String(payload.seed)
|
||||
if (typeof payload.name === 'string') name.value = payload.name
|
||||
if (payload.folderId && folders.value.some(folder => folder.id === payload.folderId)) folderId.value = payload.folderId
|
||||
if (payload.yueProfile === 1 || payload.yueProfile === 3) yueProfile.value = payload.yueProfile
|
||||
if (payload.musicEngine || payload.engine) applyEngine(parseMusicEngine(payload.musicEngine || payload.engine))
|
||||
status.value = 'Loaded from generation log'
|
||||
return
|
||||
@@ -736,6 +754,7 @@ function applyIncomingMusic() {
|
||||
seed?: string
|
||||
folderId?: string
|
||||
engine?: string
|
||||
yueProfile?: 1 | 3
|
||||
}
|
||||
if (typeof loaded.tags === 'string') tags.value = loaded.tags
|
||||
if (typeof loaded.lyrics === 'string') lyrics.value = loaded.lyrics
|
||||
@@ -746,6 +765,7 @@ function applyIncomingMusic() {
|
||||
if (typeof loaded.name === 'string') name.value = loaded.name
|
||||
if (loaded.folderId && folders.value.some(folder => folder.id === loaded.folderId)) folderId.value = loaded.folderId
|
||||
if (loaded.engine) applyEngine(parseMusicEngine(loaded.engine))
|
||||
yueProfile.value = loaded.yueProfile === 3 ? 3 : 1
|
||||
status.value = 'Loaded from the queue'
|
||||
} catch { /* ignore */ }
|
||||
}
|
||||
@@ -766,6 +786,7 @@ function applyPreset() {
|
||||
if (typeof settings.steps === 'number') steps.value = settings.steps
|
||||
if (typeof settings.seed === 'number') seed.value = String(settings.seed)
|
||||
if (settings.engine) applyEngine(settings.engine)
|
||||
yueProfile.value = settings.yueProfile === 3 ? 3 : 1
|
||||
name.value = preset.name
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user