- Sidebar: Settings → nav-tree-head with sub-items (General, Connections, Playback, Payloads, Storage, API Keys, Backup, Logs, About) - nav.js: navSettingsCat() scrolls to section, expands tree on activate - General: theme select synced with applyTheme, surfaces dark/light toggle - Logs: /api/logs endpoint (300-entry circular buffer), refresh/clear/ auto-refresh every 3 s, level filters (All/Error/Warning/Info) - About: backend availability chips from _ttsBackends, tech stack tags - server.py: _BufferHandler attaches to root logger, /api/logs GET+DELETE - Fix duplicate toast on save, guard removed settings-btn reference Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
344 lines
20 KiB
HTML
344 lines
20 KiB
HTML
<div class="section-head">
|
|
<span class="section-icon"><span class="mdi mdi-cog-outline"></span></span>
|
|
<div class="section-title">
|
|
<h2>Settings</h2>
|
|
<p>Configure backend URLs, API keys, voice folders, and playback preferences.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="tab-content" id="tab-settings">
|
|
<div class="card" id="settings-box">
|
|
|
|
<!-- Header -->
|
|
<div class="s-header">
|
|
<div>
|
|
<h2>Settings</h2>
|
|
<p class="s-subtitle">Configure the service URLs you use. Advanced payloads, folders, and keys are below.</p>
|
|
</div>
|
|
<span class="s-stack-badge"><span class="mdi mdi-check-circle-outline"></span> Local stack</span>
|
|
</div>
|
|
|
|
<!-- General -->
|
|
<div class="s-section" id="s-settings-general">
|
|
<div class="s-section-label">General</div>
|
|
<div class="settings-grid settings-behavior-grid">
|
|
<div class="s-field">
|
|
<label>Theme</label>
|
|
<select id="s-theme-select">
|
|
<option value="light">Light</option>
|
|
<option value="dark">Dark</option>
|
|
</select>
|
|
<span class="s-hint">Switch between light and dark interface.</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Core connections -->
|
|
<div class="s-section" id="s-settings-connections">
|
|
<div class="s-section-label">Connections</div>
|
|
<p class="s-section-desc">Service URLs for each backend. Change these first when setting up.</p>
|
|
|
|
<div class="s-group">
|
|
<div class="s-group-head">
|
|
<strong>TTS — Text to Speech</strong>
|
|
<span>Qwen3 engines: clone, design, custom, streaming · Kokoro FastAPI</span>
|
|
</div>
|
|
<div class="settings-grid compact">
|
|
<div class="s-field">
|
|
<label>Voice Clone / Base URL <span class="s-label-note">(WAV voices)</span></label>
|
|
<input type="text" id="s-tts-url" placeholder="http://host.docker.internal:8020">
|
|
<span class="s-hint">Uploaded/cloned WAV voices. Expected: <code>POST /v1/audio/speech</code>.</span>
|
|
</div>
|
|
<div class="s-field">
|
|
<label>Voice Design URL <span class="s-label-note">(instruction voices)</span></label>
|
|
<input type="text" id="s-voice-design-url" placeholder="http://host.docker.internal:8021">
|
|
<span class="s-hint">Prompt-designed voices and <code>vd_...</code> virtual voices.</span>
|
|
</div>
|
|
<div class="s-field">
|
|
<label>CustomVoice URL</label>
|
|
<input type="text" id="s-customvoice-url" placeholder="http://host.docker.internal:8022">
|
|
<span class="s-hint">Style over configured speakers such as Ryan, Vivian, Serena.</span>
|
|
</div>
|
|
<div class="s-field">
|
|
<label>Streaming URL</label>
|
|
<input type="text" id="s-tts-stream-url" placeholder="http://host.docker.internal:8023">
|
|
<span class="s-hint">Progressive low-latency WAV playback.</span>
|
|
</div>
|
|
<div class="s-field">
|
|
<label>Kokoro FastAPI URL <span class="s-label-note">(82M · CPU-friendly)</span></label>
|
|
<input type="text" id="s-kokoro-url" placeholder="http://host.docker.internal:8880/v1">
|
|
<span class="s-hint">OpenAI-compatible TTS. Built-in voices: af_bella, bf_emma, am_adam… ~300 MB RAM.</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="s-group">
|
|
<div class="s-group-head">
|
|
<strong>NVIDIA speech stack</strong>
|
|
<span>router, Magpie TTS, Parakeet ASR, clone NIM</span>
|
|
</div>
|
|
<div class="settings-grid compact">
|
|
<div class="s-field">
|
|
<label>NVIDIA router URL <span class="s-label-note">(TTS + STT)</span></label>
|
|
<input type="text" id="s-nvidia-router-url" placeholder="http://host.docker.internal:8090">
|
|
<span class="s-hint">OpenAI-compatible base URL for the NVIDIA speech router.</span>
|
|
</div>
|
|
<div class="s-field">
|
|
<label>NVIDIA Magpie TTS URL</label>
|
|
<input type="text" id="s-nvidia-tts-url" placeholder="http://host.docker.internal:8091">
|
|
<span class="s-hint">Direct Magpie endpoint. Fixed speakers, not WAV cloning.</span>
|
|
</div>
|
|
<div class="s-field">
|
|
<label>NVIDIA Parakeet ASR URL</label>
|
|
<input type="text" id="s-nvidia-asr-url" placeholder="http://host.docker.internal:8092">
|
|
<span class="s-hint">Direct Parakeet endpoint for transcription.</span>
|
|
</div>
|
|
<div class="s-field">
|
|
<label>NVIDIA Zeroshot NIM URL</label>
|
|
<input type="text" id="s-nvidia-zeroshot-url" placeholder="http://host.docker.internal:8093">
|
|
<span class="s-hint">Magpie Zeroshot clone endpoint with <code>audio_prompt</code>.</span>
|
|
</div>
|
|
<div class="s-field">
|
|
<label>NVIDIA Flow NIM URL</label>
|
|
<input type="text" id="s-nvidia-flow-url" placeholder="http://host.docker.internal:8094">
|
|
<span class="s-hint">Magpie Flow clone endpoint with <code>audio_prompt</code> and transcript.</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="s-group">
|
|
<div class="s-group-head">
|
|
<strong>STT — Speech to Text</strong>
|
|
<span>Whisper, faster-whisper, whisper.cpp, Groq, Parakeet, NVIDIA router</span>
|
|
</div>
|
|
<div class="settings-grid stt-settings-grid">
|
|
<div class="s-field stt-url-field">
|
|
<label>Active STT URL</label>
|
|
<input type="text" id="s-whisper-url" placeholder="http://host.docker.internal:8010">
|
|
<span class="s-hint">Default recognition endpoint. Expected: <code>POST /v1/audio/transcriptions</code>.</span>
|
|
<div class="btn-row settings-mini-actions">
|
|
<button class="btn-secondary" id="s-use-parakeet-asr" type="button">Use Parakeet</button>
|
|
<button class="btn-secondary" id="s-use-nvidia-router" type="button">Use router</button>
|
|
<button class="btn-secondary" id="s-use-faster-whisper" type="button">Use faster-whisper</button>
|
|
<button class="btn-secondary" id="s-use-whisper-cpp" type="button">Use whisper.cpp</button>
|
|
</div>
|
|
</div>
|
|
<div class="s-field">
|
|
<label>faster-whisper-server URL <span class="s-label-note">(CTranslate2 GPU)</span></label>
|
|
<input type="text" id="s-faster-whisper-url" placeholder="http://host.docker.internal:8000">
|
|
<span class="s-hint">GPU-accelerated Whisper via CTranslate2. ~70× RT · 1.5 GB VRAM.</span>
|
|
</div>
|
|
<div class="s-field">
|
|
<label>whisper.cpp URL <span class="s-label-note">(CPU/CUDA)</span></label>
|
|
<input type="text" id="s-whisper-cpp-url" placeholder="http://host.docker.internal:8080">
|
|
<span class="s-hint">Lightweight C++ Whisper server. ~8–15× RT CPU · ~1 GB RAM.</span>
|
|
</div>
|
|
<div class="s-field">
|
|
<label>Groq API key <span class="s-label-note">(Groq Whisper & LLM)</span></label>
|
|
<div class="s-key-row">
|
|
<input type="password" id="s-groq-api-key" placeholder="gsk_…" autocomplete="off">
|
|
<button type="button" class="s-eye-btn" data-target="s-groq-api-key"><span class="mdi mdi-eye-outline"></span></button>
|
|
</div>
|
|
<span class="s-hint">Used for Groq Whisper STT and LLM. Free: 2 000 req/day. Fastest cloud transcription.</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Playback behavior -->
|
|
<div class="s-section" id="s-settings-playback">
|
|
<div class="s-section-label">Playback</div>
|
|
<p class="s-section-desc">Controls how previews play and how OpenAI-compatible requests are shaped.</p>
|
|
<div class="settings-grid settings-behavior-grid">
|
|
<div class="s-field">
|
|
<label>TTS preview playback</label>
|
|
<select id="s-tts-stream-mode">
|
|
<option value="auto">Stream when available, then fall back</option>
|
|
<option value="streaming">Streaming only</option>
|
|
<option value="buffered">Buffered WAV only</option>
|
|
</select>
|
|
<span class="s-hint">Buffered keeps Save WAV available. Streaming starts sooner.</span>
|
|
</div>
|
|
<div class="s-field">
|
|
<label>OpenAI-compatible request style</label>
|
|
<select id="s-tts-backend">
|
|
<option value="openai">Qwen3-TTS / OpenAI</option>
|
|
<option value="localai">LocalAI (/tts)</option>
|
|
<option value="pocket">Pocket-TTS</option>
|
|
<option value="nvidia_magpie">NVIDIA Magpie / OpenAI</option>
|
|
</select>
|
|
<span class="s-hint">Controls payload shape for the normal TTS API URL.</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Save / Reload -->
|
|
<div class="btn-row settings-actions">
|
|
<button class="btn-primary" id="s-save-btn">Save settings</button>
|
|
<button class="btn-secondary" id="s-close-btn">Reload settings</button>
|
|
</div>
|
|
|
|
<!-- Advanced payloads -->
|
|
<details class="s-details" id="s-settings-payloads">
|
|
<summary>
|
|
<span class="mdi mdi-code-braces"></span>
|
|
<span class="s-details-title">Payloads</span>
|
|
<small>JSON extras sent to each backend</small>
|
|
<span class="mdi mdi-chevron-down s-details-chevron"></span>
|
|
</summary>
|
|
<div class="settings-grid three settings-param-grid s-details-body">
|
|
<div class="s-field">
|
|
<label>Voice Clone / Base params</label>
|
|
<textarea id="s-tts-extra-voice-clone" spellcheck="false" placeholder='{"temperature":0.1,"top_p":0.8,"seed":0}'></textarea>
|
|
<span class="s-hint">Extra fields for the 8020 WAV voice clone/base model.</span>
|
|
</div>
|
|
<div class="s-field">
|
|
<label>Streaming params</label>
|
|
<textarea id="s-tts-extra-streaming" spellcheck="false" placeholder='{"temperature":0.1,"top_p":0.8,"seed":0}'></textarea>
|
|
<span class="s-hint">Extra fields for the 8023 streaming model.</span>
|
|
</div>
|
|
<div class="s-field">
|
|
<label>CustomVoice params</label>
|
|
<textarea id="s-tts-extra-customvoice" spellcheck="false" placeholder='{"temperature":0.1,"top_p":0.8,"seed":0}'></textarea>
|
|
<span class="s-hint">Extra fields for the CustomVoice backend.</span>
|
|
</div>
|
|
<div class="s-field">
|
|
<label>Voice Design params</label>
|
|
<textarea id="s-tts-extra-voice-design" spellcheck="false" placeholder='{"temperature":0.1,"top_p":0.8,"seed":0}'></textarea>
|
|
<span class="s-hint">Extra fields for Voice Design and virtual <code>vd_...</code> voices.</span>
|
|
</div>
|
|
<div class="s-field">
|
|
<label>NVIDIA Magpie params</label>
|
|
<textarea id="s-tts-extra-nvidia-magpie" spellcheck="false" placeholder="{}"></textarea>
|
|
<span class="s-hint">Usually empty. Magpie accepts fixed speakers: sofia, aria, jason, leo, john.</span>
|
|
</div>
|
|
<div class="s-field">
|
|
<label>NVIDIA Zeroshot params</label>
|
|
<textarea id="s-tts-extra-nvidia-zeroshot" spellcheck="false" placeholder='{"zero_shot_quality":20}'></textarea>
|
|
<span class="s-hint">Optional multipart fields. The app supplies <code>text</code>, <code>language</code>, <code>audio_prompt</code>.</span>
|
|
</div>
|
|
<div class="s-field">
|
|
<label>NVIDIA Flow params</label>
|
|
<textarea id="s-tts-extra-nvidia-flow" spellcheck="false" placeholder="{}"></textarea>
|
|
<span class="s-hint">Optional multipart fields. The app also sends the saved reference transcript.</span>
|
|
</div>
|
|
<div class="s-field">
|
|
<label>Kokoro FastAPI params</label>
|
|
<textarea id="s-tts-extra-kokoro" spellcheck="false" placeholder="{}"></textarea>
|
|
<span class="s-hint">Extra JSON fields for Kokoro. Usually empty — voice is selected from the dropdown.</span>
|
|
</div>
|
|
</div>
|
|
</details>
|
|
|
|
<!-- Voice folders -->
|
|
<details class="s-details" id="s-settings-storage">
|
|
<summary>
|
|
<span class="mdi mdi-folder-outline"></span>
|
|
<span class="s-details-title">Storage</span>
|
|
<small>container paths and Portainer volume mounts</small>
|
|
<span class="mdi mdi-chevron-down s-details-chevron"></span>
|
|
</summary>
|
|
<div class="settings-grid s-details-body">
|
|
<div class="s-field">
|
|
<label>Voice scan directory</label>
|
|
<input type="text" id="s-voices-scan-dir" placeholder="/voices">
|
|
<span class="s-hint">Contains <code>active_voices</code>, <code>hidden_voices</code>, sounds, and metadata.</span>
|
|
</div>
|
|
<div class="s-field">
|
|
<label>Active voices directory</label>
|
|
<input type="text" id="s-output-dir" placeholder="/voices/active_voices">
|
|
<span class="s-hint">New cloned/exported voices are saved here.</span>
|
|
</div>
|
|
</div>
|
|
</details>
|
|
|
|
<!-- API keys -->
|
|
<details class="s-details" id="s-settings-apikeys">
|
|
<summary>
|
|
<span class="mdi mdi-key-outline"></span>
|
|
<span class="s-details-title">API Keys</span>
|
|
<small>usually empty for local containers</small>
|
|
<span class="mdi mdi-chevron-down s-details-chevron"></span>
|
|
</summary>
|
|
<div class="settings-grid three s-details-body">
|
|
<div class="s-field">
|
|
<label>TTS API key <span class="s-label-note">(optional)</span></label>
|
|
<div class="s-key-row">
|
|
<input type="password" id="s-tts-key" placeholder="dummy, sk-local, or leave empty" autocomplete="off">
|
|
<button type="button" class="s-eye-btn" data-target="s-tts-key"><span class="mdi mdi-eye-outline"></span></button>
|
|
</div>
|
|
</div>
|
|
<div class="s-field">
|
|
<label>Voice Design API key <span class="s-label-note">(optional)</span></label>
|
|
<div class="s-key-row">
|
|
<input type="password" id="s-vd-key" placeholder="dummy, sk-local, or leave empty" autocomplete="off">
|
|
<button type="button" class="s-eye-btn" data-target="s-vd-key"><span class="mdi mdi-eye-outline"></span></button>
|
|
</div>
|
|
</div>
|
|
<div class="s-field">
|
|
<label>Whisper API key <span class="s-label-note">(optional)</span></label>
|
|
<div class="s-key-row">
|
|
<input type="password" id="s-whisper-key" placeholder="dummy, sk-local, or leave empty" autocomplete="off">
|
|
<button type="button" class="s-eye-btn" data-target="s-whisper-key"><span class="mdi mdi-eye-outline"></span></button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</details>
|
|
|
|
<!-- Backup & restore -->
|
|
<div class="s-section" id="s-settings-backup">
|
|
<div class="s-section-label">Backup & restore</div>
|
|
<p class="s-hint">Export all voices and settings as a ZIP for backup or migration. Import to restore. API keys are excluded from exports.</p>
|
|
<div class="btn-row" style="margin-top:8px;gap:10px;flex-wrap:wrap">
|
|
<a class="btn-secondary" id="s-export-voices-btn" href="/api/voices/export" download><span class="mdi mdi-download"></span> Export voices</a>
|
|
<label class="btn-secondary" style="cursor:pointer" title="Import a voices ZIP">
|
|
<span class="mdi mdi-upload"></span> Import voices
|
|
<input type="file" id="s-import-voices-file" accept=".zip" style="display:none">
|
|
</label>
|
|
<span class="note" id="s-import-status"></span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Logs -->
|
|
<div class="s-section" id="s-settings-logs">
|
|
<div class="s-section-label">Logs</div>
|
|
<p class="s-section-desc">Recent server activity. Useful for debugging backend connections and API errors.</p>
|
|
<div class="s-log-toolbar">
|
|
<button class="btn-secondary" id="s-logs-refresh-btn" type="button"><span class="mdi mdi-refresh"></span> Refresh</button>
|
|
<button class="btn-secondary" id="s-logs-clear-btn" type="button"><span class="mdi mdi-trash-can-outline"></span> Clear</button>
|
|
<label class="s-log-live-label">
|
|
<input type="checkbox" id="s-logs-live-toggle">
|
|
<span>Auto-refresh</span>
|
|
</label>
|
|
<span class="s-log-count" id="s-log-count"></span>
|
|
</div>
|
|
<div class="s-log-filters">
|
|
<button class="s-log-filter is-active" data-log-level="">All</button>
|
|
<button class="s-log-filter" data-log-level="ERROR">Error</button>
|
|
<button class="s-log-filter" data-log-level="WARNING">Warning</button>
|
|
<button class="s-log-filter" data-log-level="INFO">Info</button>
|
|
</div>
|
|
<div class="s-log-viewer" id="s-log-viewer">
|
|
<div class="s-log-empty">Click Refresh to load logs</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- About -->
|
|
<div class="s-section s-section-last" id="s-settings-about">
|
|
<div class="s-section-label">About</div>
|
|
<div class="s-about-block">
|
|
<div class="s-about-name"><span class="mdi mdi-microphone-variant"></span> TTS Voice Creator</div>
|
|
<p class="s-about-desc">Clone, design, and deploy custom voices using local AI backends. Compatible with Qwen3-TTS, Kokoro FastAPI, NVIDIA Magpie, and any OpenAI-compatible TTS/STT endpoint.</p>
|
|
<div class="s-about-stack">
|
|
<span class="s-about-chip"><span class="mdi mdi-language-python"></span> FastAPI</span>
|
|
<span class="s-about-chip"><span class="mdi mdi-language-javascript"></span> Vanilla JS</span>
|
|
<span class="s-about-chip"><span class="mdi mdi-music-note"></span> WaveSurfer.js v7</span>
|
|
<span class="s-about-chip"><span class="mdi mdi-vector-square"></span> MDI v7.4.47</span>
|
|
</div>
|
|
<div class="s-about-backends" id="s-about-backends"></div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div><!-- /tab-settings -->
|