Landing page: testimonials slider, how-it-works page, 25 TTS voices

- Add testimonial slider with 8 fake caller reviews
- Add how-it-works page with visual architecture diagram
- Expand voice pools: Inworld 25 voices (14M/11F), ElevenLabs 22 (14M/8F)
- Voice pools auto-switch when TTS provider changes
- Add cover art locally, update cache-busted image refs
- Add "More from Luke" footer links (MMG, prints, YouTube)
- Ad channel configurable in settings UI

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-07 01:34:30 -07:00
parent f654a5cbb1
commit bd6c8ccbab
9 changed files with 926 additions and 23 deletions

View File

@@ -135,6 +135,7 @@ class AudioService:
live_caller_channel: Optional[int] = None,
music_channel: Optional[int] = None,
sfx_channel: Optional[int] = None,
ad_channel: Optional[int] = None,
phone_filter: Optional[bool] = None
):
"""Configure audio devices and channels"""
@@ -152,6 +153,8 @@ class AudioService:
self.music_channel = music_channel
if sfx_channel is not None:
self.sfx_channel = sfx_channel
if ad_channel is not None:
self.ad_channel = ad_channel
if phone_filter is not None:
self.phone_filter = phone_filter
@@ -168,6 +171,7 @@ class AudioService:
"live_caller_channel": self.live_caller_channel,
"music_channel": self.music_channel,
"sfx_channel": self.sfx_channel,
"ad_channel": self.ad_channel,
"phone_filter": self.phone_filter,
}

View File

@@ -577,7 +577,12 @@ async def generate_speech_inworld(text: str, voice_id: str) -> tuple[np.ndarray,
import base64
import librosa
voice = INWORLD_VOICES.get(voice_id, DEFAULT_INWORLD_VOICE)
# voice_id is now the Inworld voice name directly (e.g. "Edward")
# Fall back to legacy mapping if it's an ElevenLabs ID
if voice_id in INWORLD_VOICES:
voice = INWORLD_VOICES[voice_id]
else:
voice = voice_id
api_key = settings.inworld_api_key
if not api_key: