From 5bccca24752e6eab7e411ed6886ddc7bbff8928b Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Tue, 3 Mar 2026 11:15:36 +0300 Subject: [PATCH] feat(web-test): add ElevenLabs TTS provider Co-Authored-By: Claude Opus 4.6 --- .claude/skills/web-test/recording.md | 14 +++++++++++- .claude/skills/web-test/scripts/browser.mjs | 24 +++++++++++++++++++++ docs/web-test-recording-guide.md | 18 ++++++++++++++++ 3 files changed, 55 insertions(+), 1 deletion(-) diff --git a/.claude/skills/web-test/recording.md b/.claude/skills/web-test/recording.md index c7232d83..91f16eda 100644 --- a/.claude/skills/web-test/recording.md +++ b/.claude/skills/web-test/recording.md @@ -216,6 +216,18 @@ For OpenAI-compatible provider: } ``` +For ElevenLabs: +```json +{ + "tts": { + "provider": "elevenlabs", + "apiKey": "sk_...", + "voice": "JBFqnCBsd6RMkjVDRZzb" + } +} +``` +Note: `voice` is the ElevenLabs voice ID (not a name). Default model: `eleven_multilingual_v2` (supports Russian and other languages). + ### `showCaption()` speech parameter The `speech` option controls what text is narrated (vs displayed): @@ -236,7 +248,7 @@ Generate TTS and merge audio with video. Call after `stopRecording()`. |-----------|------|-------------| | `videoPath` | `string` | Path to the recorded MP4 file | | `opts.captions` | `Array` | Explicit captions (default: from last recording or `.captions.json`) | -| `opts.provider` | `string` | `'edge'` (default) or `'openai'` | +| `opts.provider` | `string` | `'edge'` (default), `'openai'`, or `'elevenlabs'` | | `opts.voice` | `string` | Voice name (provider-specific) | | `opts.apiKey` | `string` | API key (for openai) | | `opts.apiUrl` | `string` | Endpoint (for openai) | diff --git a/.claude/skills/web-test/scripts/browser.mjs b/.claude/skills/web-test/scripts/browser.mjs index 32f6664f..d6e464c0 100644 --- a/.claude/skills/web-test/scripts/browser.mjs +++ b/.claude/skills/web-test/scripts/browser.mjs @@ -3154,10 +3154,34 @@ async function openaiTtsProvider(text, outputPath, opts = {}) { writeFileSync(outputPath, buf); } +/** + * ElevenLabs TTS provider. Requires apiKey. + * @param {string} text — text to synthesize + * @param {string} outputPath — path for the output mp3 file + * @param {object} opts — { apiKey, apiUrl, voice, model } + */ +async function elevenlabsTtsProvider(text, outputPath, opts = {}) { + const voiceId = opts.voice || 'JBFqnCBsd6RMkjVDRZzb'; // George + const apiUrl = opts.apiUrl || `https://api.elevenlabs.io/v1/text-to-speech/${voiceId}`; + if (!opts.apiKey) throw new Error('ElevenLabs TTS requires apiKey'); + const resp = await fetch(apiUrl, { + method: 'POST', + headers: { 'xi-api-key': opts.apiKey, 'Content-Type': 'application/json' }, + body: JSON.stringify({ + text, + model_id: opts.model || 'eleven_multilingual_v2', + }), + }); + if (!resp.ok) throw new Error(`ElevenLabs TTS error ${resp.status}: ${await resp.text()}`); + const buf = Buffer.from(await resp.arrayBuffer()); + writeFileSync(outputPath, buf); +} + /** Get TTS provider function by name. */ function getTtsProvider(name) { switch (name) { case 'openai': return openaiTtsProvider; + case 'elevenlabs': return elevenlabsTtsProvider; case 'edge': default: return edgeTtsProvider; } } diff --git a/docs/web-test-recording-guide.md b/docs/web-test-recording-guide.md index ddc425ed..95ddebf2 100644 --- a/docs/web-test-recording-guide.md +++ b/docs/web-test-recording-guide.md @@ -225,6 +225,24 @@ await showCaption('Технические детали', { speech: false }); Голоса: `alloy`, `echo`, `fable`, `onyx`, `nova`, `shimmer`. +Поле `apiUrl` позволяет подключить любой OpenAI-совместимый сервис. + +### ElevenLabs (платный) + +Высокое качество синтеза, мультиязычная модель поддерживает русский. + +```json +{ + "tts": { + "provider": "elevenlabs", + "apiKey": "sk_...", + "voice": "JBFqnCBsd6RMkjVDRZzb" + } +} +``` + +`voice` — это ID голоса из библиотеки ElevenLabs (не имя). Список голосов — в личном кабинете ElevenLabs. Требуется платный тариф для использования через API. + ## Полный пример Типовая структура озвученного сценария: