Supersede draft PR #26 with a merge-safe prerequisite: bake and preload the amy, irina, and claude Piper models, route only validated server-side language to fixed voices, and leave the live voice deployment manifest unchanged. Remove the pinned WebUI speaker selector and its persisted preference, omit client voice fields from every outbound TTS path, and keep hands-free Voice Mode and the conversation instrument intact. Hostile or legacy voice fields remain ignored by the Piper server. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
35 lines
1.0 KiB
JavaScript
35 lines
1.0 KiB
JavaScript
function speakWithRegisteredEngine(){
|
|
const _opts={
|
|
voice: localStorage.getItem("hermes-tts-voice")||'',
|
|
rate: parseFloat(localStorage.getItem("hermes-tts-rate")),
|
|
};
|
|
return _opts;
|
|
}
|
|
function speakWithEdge(clean){
|
|
const voice=localStorage.getItem("hermes-tts-voice")||"zh-CN-XiaoxiaoNeural";
|
|
const rate='';
|
|
const pitch='';
|
|
return fetch('/api/tts', {
|
|
body: JSON.stringify({text: clean, voice, rate, pitch})
|
|
});
|
|
}
|
|
function speakWithBrowser(clean){
|
|
const utter=new SpeechSynthesisUtterance(clean);
|
|
const savedVoice=localStorage.getItem('hermes-tts-voice');
|
|
const voices=speechSynthesis.getVoices();
|
|
if(savedVoice&&voices.length){
|
|
const match=voices.find(v=>v.name===savedVoice);
|
|
if(match) utter.voice=match;
|
|
}
|
|
return utter;
|
|
}
|
|
function _mirrorSpeechSettingsFromServer(s){
|
|
const defaults={
|
|
tts_voice:'',
|
|
};
|
|
[
|
|
['tts_voice','hermes-tts-voice'],
|
|
].forEach(([settingKey,storageKey])=>localStorage.setItem(storageKey,s[settingKey]));
|
|
return defaults;
|
|
}
|