From df0e1b66ad9cb2b4cad13c294aa455e716f6222c Mon Sep 17 00:00:00 2001 From: posimai Date: Sun, 22 Mar 2026 18:23:29 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20TTS=20pre-warm=20=E2=80=94=20prioritize?= =?UTF-8?q?=20user=20requests,=20fix=20cache=20key=20mismatch?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Split preWarmBusy from ttsBusy so user requests are never blocked by pre-warm - /tts endpoint waits up to 6s for pre-warm synthesis then proceeds - Pre-warm skips articles when user is actively using TTS - Fix text format to match Brief exactly (remove substring(60), fix source fallback) Co-Authored-By: Claude Sonnet 4.6 --- server.js | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) 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');