fix: harden custom TTS URL handling and availability checks

Validate and normalize custom TTS server URLs before proxying requests.
Gate server TTS availability by provider mode to avoid OpenAI/custom misrouting.
Keep browser and message TTS hooks aligned with the new provider-specific checks.
This commit is contained in:
Bohdan Triapitsyn
2026-04-12 10:38:16 +03:00
parent 055b6f6af0
commit c7f1501ad2
5 changed files with 36 additions and 8 deletions
+1
View File
@@ -166,6 +166,7 @@ export function useBrowserVoice(): UseBrowserVoiceReturn {
// Server TTS for mobile (bypasses Safari audio restrictions)
const { speak: speakServerTTS, stop: stopServerTTS, isAvailable: isServerTTSAvailable, unlockAudio: unlockServerTTSAudio } = useServerTTS({
enabled: shouldCheckOpenAIAvailability,
availabilityMode: voiceProvider === 'openai-compatible' ? 'openai-compatible' : 'openai',
});
// macOS Say TTS
+1
View File
@@ -44,6 +44,7 @@ export function useMessageTTS(): UseMessageTTSReturn {
const { speak: speakServerTTS, stop: stopServerTTS, isAvailable: isServerTTSAvailable } = useServerTTS({
enabled: shouldCheckOpenAIAvailability,
availabilityMode: voiceProvider === 'openai-compatible' ? 'openai-compatible' : 'openai',
});
const { speak: speakSayTTS, stop: stopSayTTS, isAvailable: isSayTTSAvailable } = useSayTTS({
enabled: shouldCheckSayAvailability,
+14 -2
View File
@@ -26,6 +26,7 @@ interface ServerTTSStatusCache {
interface UseServerTTSOptions {
enabled?: boolean;
availabilityMode?: 'auto' | 'openai' | 'openai-compatible';
}
const SERVER_TTS_STATUS_TTL_MS = 30000;
@@ -125,6 +126,7 @@ function getAudioContext(): AudioContext {
export function useServerTTS(options: UseServerTTSOptions = {}): UseServerTTSReturn {
const enabled = options.enabled ?? true;
const availabilityMode = options.availabilityMode ?? 'auto';
const [isPlaying, setIsPlaying] = useState(false);
const [isAvailable, setIsAvailable] = useState(false);
const [error, setError] = useState<string | null>(null);
@@ -150,7 +152,17 @@ export function useServerTTS(options: UseServerTTSOptions = {}): UseServerTTSRet
const hasClientKey = Boolean(openaiApiKey && openaiApiKey.trim().length > 0);
const hasCustomUrl = Boolean(openaiCompatibleUrl && openaiCompatibleUrl.trim().length > 0);
if (hasClientKey || hasCustomUrl) {
if (availabilityMode === 'openai-compatible') {
setIsAvailable(hasCustomUrl);
return hasCustomUrl;
}
if (hasClientKey) {
setIsAvailable(true);
return true;
}
if (availabilityMode === 'auto' && hasCustomUrl) {
setIsAvailable(true);
return true;
}
@@ -163,7 +175,7 @@ export function useServerTTS(options: UseServerTTSOptions = {}): UseServerTTSRet
setIsAvailable(false);
return false;
}
}, [enabled, openaiApiKey, openaiCompatibleUrl]);
}, [availabilityMode, enabled, openaiApiKey, openaiCompatibleUrl]);
// Check availability on mount and when API key changes
useEffect(() => {