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:
parent
605b2697d1
commit
1353d455e4
53
index.html
53
index.html
|
|
@ -671,29 +671,38 @@ async function tryVoicevox(text) {
|
||||||
if (!apiKey) return false;
|
if (!apiKey) return false;
|
||||||
|
|
||||||
const ctx = getAudioCtx();
|
const ctx = getAudioCtx();
|
||||||
const res = await fetch(TTS_API, {
|
for (let attempt = 0; attempt < 4; attempt++) {
|
||||||
method: 'POST',
|
if (attempt > 0) await new Promise(r => setTimeout(r, 2000 * attempt));
|
||||||
headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${apiKey}` },
|
const res = await fetch(TTS_API, {
|
||||||
body: JSON.stringify({ text: preprocessText(text), speaker: ttsSpeaker }),
|
method: 'POST',
|
||||||
});
|
headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${apiKey}` },
|
||||||
if (!res.ok) {
|
body: JSON.stringify({ text: preprocessText(text), speaker: ttsSpeaker }),
|
||||||
const msg = res.status === 401 ? 'APIキーが無効です' : res.status === 503 ? 'VOICEVOXが起動していません' : `サーバーエラー (${res.status})`;
|
});
|
||||||
if (currentIdx === 0 && typeof showToast === 'function') showToast('VOICEVOX: ' + msg);
|
if (res.status === 503) {
|
||||||
return false;
|
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;
|
||||||
|
currentSource.playbackRate.value = speechRate;
|
||||||
|
currentSource.connect(ctx.destination);
|
||||||
|
currentSource.onended = () => { currentSource = null; resolve(true); };
|
||||||
|
currentSource.start(0);
|
||||||
|
updateVoiceBadge(true);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
const buf = await ctx.decodeAudioData(await res.arrayBuffer());
|
|
||||||
if (!isPlaying) return true;
|
|
||||||
|
|
||||||
return new Promise(resolve => {
|
|
||||||
currentSource = ctx.createBufferSource();
|
|
||||||
currentSource.buffer = buf;
|
|
||||||
currentSource.playbackRate.value = speechRate;
|
|
||||||
currentSource.connect(ctx.destination);
|
|
||||||
currentSource.onended = () => { currentSource = null; resolve(true); };
|
|
||||||
currentSource.start(0);
|
|
||||||
updateVoiceBadge(true);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function speakBrowser(text, onEnd) {
|
function speakBrowser(text, onEnd) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue