diff --git a/server.js b/server.js index 74fce167..84b8e5a1 100644 --- a/server.js +++ b/server.js @@ -1123,6 +1123,7 @@ ${excerpt} const ttsCache = new Map(); // key: "speaker:text" → Buffer const TTS_CACHE_MAX = 60; // 約60文を最大キャッシュ(メモリ節約) let ttsBusy = false; // VOICEVOX は同時1リクエストのみ対応(排他ロック) + let preWarmBusy = false; // プリウォームが合成中(ユーザーリクエストを優先するために分離) // 合成ヘルパー(/tts と /tts/warmup から共用) async function ttsSynthesize(text, speaker) { @@ -1160,6 +1161,13 @@ ${excerpt} return res.send(cached); } + // プリウォーム中なら最大6秒待ってユーザーを優先 + if (preWarmBusy) { + const deadline = Date.now() + 6000; + while (preWarmBusy && Date.now() < deadline) { + await new Promise(r => setTimeout(r, 200)); + } + } if (ttsBusy) return res.status(503).json({ error: 'TTS_BUSY' }); try { @@ -1246,12 +1254,11 @@ ${excerpt} const articles = (data.articles || []).slice(0, 5); if (articles.length === 0) return; - // ブラウザと同じロジックでテキスト生成 + // ブラウザと同じロジックでテキスト生成(Brief の speechQueue と完全一致させる) const texts = []; articles.forEach((a, i) => { const prefix = i === 0 ? '最初のニュースです。' : '続いて。'; - const body = (a.title || '').substring(0, 60); - texts.push(`${prefix}${a.source || '不明なソース'}より。${body}`); + texts.push(`${prefix}${a.source || ''}より。${a.title || ''}`); }); texts.push('本日のブリーフィングは以上です。'); @@ -1262,8 +1269,12 @@ ${excerpt} console.log(`[TTS pre-warm] skip (cached): ${text.substring(0, 25)}`); continue; } - while (ttsBusy) await new Promise(r => setTimeout(r, 500)); - ttsBusy = true; + // ユーザーリクエスト中はスキップ(ユーザー優先) + if (ttsBusy) { + console.log(`[TTS pre-warm] skip (user active): ${text.substring(0, 25)}`); + continue; + } + preWarmBusy = true; try { const buf = await ttsSynthesize(text, speaker); if (ttsCache.size >= TTS_CACHE_MAX) ttsCache.delete(ttsCache.keys().next().value); @@ -1272,7 +1283,7 @@ ${excerpt} } catch (e) { console.error(`[TTS pre-warm] synth failed: ${e.message}`); } finally { - ttsBusy = false; + preWarmBusy = false; } } console.log('[TTS pre-warm] done');