Add save progress UI and fix MMS errors

Show a progress bar during MP3 generation, update styles, and improve MMS error messaging.
This commit is contained in:
dsyoon
2026-01-30 19:01:24 +09:00
parent 39387a0544
commit c40fa33e7b
5 changed files with 70 additions and 1 deletions

2
.env
View File

@@ -4,4 +4,4 @@ DB_NAME=tts
DB_USER=ncue
DB_PASSWORD=ncue5004!
TTS_ENGINE=mms
MMS_MODEL=facebook/mms-tts-kor
MMS_MODEL=facebook/mms-tts-kor

View File

@@ -5,10 +5,47 @@ const editBtn = document.getElementById("edit-btn");
const deleteBtn = document.getElementById("delete-btn");
const cancelBtn = document.getElementById("cancel-btn");
const downloadLink = document.getElementById("download-link");
const progressWrap = document.getElementById("save-progress");
const progressBar = document.getElementById("save-progress-bar");
let items = [];
let editMode = false;
const selectedIds = new Set();
let progressTimer = null;
function startProgress() {
let value = 0;
progressWrap.classList.remove("hidden");
progressBar.style.width = "0%";
progressBar.setAttribute("aria-valuenow", "0");
if (progressTimer) {
clearInterval(progressTimer);
}
progressTimer = setInterval(() => {
value = Math.min(value + Math.random() * 8 + 2, 90);
progressBar.style.width = `${value}%`;
progressBar.setAttribute("aria-valuenow", `${Math.round(value)}`);
}, 300);
}
function finishProgress(success = true) {
if (progressTimer) {
clearInterval(progressTimer);
progressTimer = null;
}
progressBar.style.width = "100%";
progressBar.setAttribute("aria-valuenow", "100");
const delay = success ? 400 : 1200;
setTimeout(() => {
progressBar.style.width = "0%";
progressBar.setAttribute("aria-valuenow", "0");
progressWrap.classList.add("hidden");
}, delay);
}
function setEditMode(isEdit) {
editMode = isEdit;
@@ -69,6 +106,7 @@ async function handleSave() {
return;
}
startProgress();
const res = await fetch("/api/tts", {
method: "POST",
headers: { "Content-Type": "application/json" },
@@ -78,12 +116,14 @@ async function handleSave() {
if (!res.ok) {
const err = await res.json().catch(() => ({}));
alert(err.detail || "저장에 실패했습니다.");
finishProgress(false);
return;
}
const created = await res.json();
items.unshift(created);
renderList();
finishProgress(true);
}
async function handleItemClick(item) {

View File

@@ -65,6 +65,21 @@ button.danger {
color: #ffffff;
}
.progress-wrap {
width: 100%;
height: 10px;
background: #f1d9a6;
border-radius: 6px;
overflow: hidden;
}
.progress-bar {
height: 100%;
width: 0%;
background: #f5a623;
transition: width 0.2s ease;
}
.tts-list {
list-style: none;
padding: 0;

View File

@@ -12,6 +12,16 @@
<div class="panel-header">입력 텍스트</div>
<textarea id="text-input" rows="16" placeholder="텍스트를 입력하세요"></textarea>
<button id="save-btn" class="primary">mp3 저장</button>
<div id="save-progress" class="progress-wrap hidden" aria-label="저장 진행률">
<div
id="save-progress-bar"
class="progress-bar"
role="progressbar"
aria-valuemin="0"
aria-valuemax="100"
aria-valuenow="0"
></div>
</div>
</section>
<section class="panel right">

View File

@@ -32,6 +32,10 @@ def _get_mms():
def _text_to_wav_mms(text: str, wav_path: str) -> None:
try:
import torch
except Exception as exc:
raise RuntimeError("MMS TTS 사용을 위해 torch/numpy가 정상 설치되어야 합니다.") from exc
try:
import soundfile as sf
except Exception as exc:
raise RuntimeError("MMS TTS 사용을 위해 soundfile 설치가 필요합니다.") from exc