const $ = (id) => document.getElementById(id); async function fetchHealth() { try { const res = await fetch("/api/health"); const data = await res.json(); $("healthInfo").textContent = `모델: ${data.model} · 샘플 ${data.samples_count}개`; } catch { $("healthInfo").textContent = "API 서버에 연결할 수 없습니다."; } } async function loadSamples() { const select = $("sampleSelect"); try { const res = await fetch("/api/voice-samples"); const data = await res.json(); for (const s of data.samples) { const opt = document.createElement("option"); opt.value = s.path; opt.textContent = `${s.label}/${s.id}${s.has_transcript ? "" : " (대본 없음)"}`; select.appendChild(opt); } } catch (e) { console.warn("samples load failed", e); } } async function uploadIfNeeded() { const fileInput = $("fileUpload"); if (!fileInput.files?.length) return null; const form = new FormData(); form.append("file", fileInput.files[0]); const refText = $("refText").value.trim(); if (refText) form.append("ref_text", refText); const res = await fetch("/api/voice-sample", { method: "POST", body: form }); if (!res.ok) { const err = await res.json().catch(() => ({})); throw new Error(err.detail || "업로드 실패"); } const data = await res.json(); return data.path; } $("generateBtn").addEventListener("click", async () => { const text = $("text").value.trim(); if (!text) { $("status").textContent = "텍스트를 입력하세요."; return; } const btn = $("generateBtn"); btn.disabled = true; $("status").textContent = "음성 생성 중… (GPU 추론은 수십 초 걸릴 수 있습니다)"; $("resultSection").hidden = true; try { let refAudio = $("sampleSelect").value || null; const uploaded = await uploadIfNeeded(); if (uploaded) refAudio = uploaded; const body = { text, preprocess: true, ref_text: $("refText").value.trim() || null, ref_audio: refAudio, }; const res = await fetch("/api/tts", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body), }); if (!res.ok) { const err = await res.json().catch(() => ({})); const detail = typeof err.detail === "string" ? err.detail : JSON.stringify(err.detail || err); throw new Error(detail || res.statusText); } const data = await res.json(); const url = data.audio_url + "?t=" + Date.now(); $("player").src = url; $("downloadLink").href = url; $("downloadLink").download = `${data.job_id}.wav`; $("resultSection").hidden = false; $("status").textContent = `완료 (모델: ${data.model}, job: ${data.job_id})`; } catch (e) { $("status").textContent = `오류: ${e.message}`; } finally { btn.disabled = false; } }); fetchHealth(); loadSamples();