fix: retry TTS_BUSY up to 3 times with backoff

Auto-retries on TTS_BUSY (2s/4s/6s backoff) so pre-warm race at
startup is transparent to the user.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
posimai 2026-03-22 18:23:39 +09:00
parent 605b2697d1
commit 1353d455e4
1 changed files with 31 additions and 22 deletions

View File

@ -671,20 +671,27 @@ async function tryVoicevox(text) {
if (!apiKey) return false;
const ctx = getAudioCtx();
for (let attempt = 0; attempt < 4; attempt++) {
if (attempt > 0) await new Promise(r => setTimeout(r, 2000 * attempt));
const res = await fetch(TTS_API, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${apiKey}` },
body: JSON.stringify({ text: preprocessText(text), speaker: ttsSpeaker }),
});
if (!res.ok) {
const msg = res.status === 401 ? 'APIキーが無効です' : res.status === 503 ? 'VOICEVOXが起動していません' : `サーバーエラー (${res.status})`;
if (res.status === 503) {
const body = await res.json().catch(() => ({}));
if (body.error === 'TTS_BUSY' && attempt < 3) continue; // 自動リトライ
const msg = body.error === 'TTS_BUSY' ? 'サーバーが混雑しています' : 'VOICEVOXが起動していません';
if (currentIdx === 0 && typeof showToast === 'function') showToast('VOICEVOX: ' + msg);
return false;
}
if (!res.ok) {
const msg = res.status === 401 ? 'APIキーが無効です' : `サーバーエラー (${res.status})`;
if (currentIdx === 0 && typeof showToast === 'function') showToast('VOICEVOX: ' + msg);
return false;
}
const buf = await ctx.decodeAudioData(await res.arrayBuffer());
if (!isPlaying) return true;
return new Promise(resolve => {
currentSource = ctx.createBufferSource();
currentSource.buffer = buf;
@ -695,6 +702,8 @@ async function tryVoicevox(text) {
updateVoiceBadge(true);
});
}
return false;
}
function speakBrowser(text, onEnd) {
const utter = new SpeechSynthesisUtterance(preprocessText(text));