tts-voice-creator-clone-and.../static/js/rehearser-voice-design.js

74 lines
3.5 KiB
JavaScript

// ── Rehearser voice design controls ─────────────────────────────────────────
// Character development and backend selection resolve the shared Rehearser
// state at interaction time, so they can be loaded as a leaf feature module.
(function (root) {
async function _castDevelop(sp, btn) {
const script = $('reh-script-text')?.value.trim() || linesToScriptText();
if (!script) { toast('Load a script first', 'error'); return; }
const c = rehState.cast[sp];
const orig = btn.innerHTML;
btn.disabled = true; btn.innerHTML = '<span class="reh-imsdb-spinner"></span> Developing…';
try {
const r = await fetch('/api/analyze-characters', {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
script, names: [sp],
llm_url: $('reh-llm-url')?.value.trim() || rehDefaultLlmUrl(),
model: $('reh-llm-model')?.value || '',
language: c.lang || $('reh-design-lang')?.value || 'English',
}),
});
if (!r.ok) { const e = await r.json().catch(()=>({})); throw new Error(e.detail || r.statusText); }
const info = (await r.json()).characters?.[0] || {};
if (info.gender) c.gender = String(info.gender).toUpperCase().charAt(0).replace(/[^MFN]/, 'N');
if (info.description) { c.soul = info.description; c.instruct = c.instruct || info.description; }
renderCastList();
toast(`Developed ${sp}`, 'success');
} catch (e) {
btn.disabled = false; btn.innerHTML = orig;
toast('Develop failed: ' + e.message, 'error');
}
}
async function refreshRehBackends() {
rehInitLlmField();
const sel = $('reh-backend-select'); if (!sel) return;
let backends = typeof availableTtsBackends === 'function' ? availableTtsBackends() : [];
if (!backends.length && typeof refreshTtsBackendAvailability === 'function') {
await refreshTtsBackendAvailability().catch(() => {});
backends = typeof availableTtsBackends === 'function' ? availableTtsBackends() : [];
}
if (!backends.length && typeof _ttsBackends !== 'undefined' && Array.isArray(_ttsBackends)) backends = _ttsBackends;
const prev = sel.value || rehState.backend;
sel.innerHTML = backends.length
? backends.map(b => `<option value="${escHtml(b.id)}">${escHtml(b.label)}</option>`).join('')
: '<option value="">No backend available</option>';
if (prev && [...sel.options].some(o => o.value === prev)) sel.value = prev;
else {
const preferred = ['fishspeech', 'voice_clone', 'customvoice', 'voice_design'];
const pick = preferred.find(id => [...sel.options].some(o => o.value === id));
if (pick) sel.value = pick;
}
rehState.backend = sel.value || '';
_checkToneStyleSupport();
}
(window._ttsRefreshHooks = window._ttsRefreshHooks || []).push(() => {
const sel = $('reh-backend-select'); if (!sel) return;
const backends = typeof availableTtsBackends === 'function' ? availableTtsBackends() : [];
if (!backends.length) return;
const prev = sel.value || rehState.backend;
sel.innerHTML = backends.map(b => `<option value="${escHtml(b.id)}">${escHtml(b.label)}</option>`).join('');
if (prev && [...sel.options].some(o => o.value === prev)) sel.value = prev;
else if (sel.options.length) sel.value = sel.options[0].value;
rehState.backend = sel.value || '';
});
Object.assign(root, { _castDevelop, refreshRehBackends });
root.TTVC = root.TTVC || {};
root.TTVC.rehearser = root.TTVC.rehearser || {};
root.TTVC.rehearser.voiceDesign = { develop: _castDevelop, refreshBackends: refreshRehBackends };
})(window);