UNPKG

@andresaya/edge-tts

Version:

Edge TTS is a package that allows access to the online text-to-speech service used by Microsoft Edge without the need for Microsoft Edge, Windows, or an API key.

25 lines (24 loc) 8.34 kB
var N=Object.defineProperty;var M=(p,e,s)=>e in p?N(p,e,{enumerable:!0,configurable:!0,writable:!0,value:s}):p[e]=s;var T=(p,e,s)=>M(p,typeof e!="symbol"?e+"":e,s);var a={TRUSTED_CLIENT_TOKEN:"6A5AA1D4EAFF4E9FB37E23D68491D6F4",BASE_URL:"https://speech.platform.bing.com/consumer/speech/synthesize/readaloud",WSS_URL:"wss://speech.platform.bing.com/consumer/speech/synthesize/readaloud/edge/v1",VOICES_URL:"https://speech.platform.bing.com/consumer/speech/synthesize/readaloud/voices/list",CHROMIUM_FULL_VERSION:"143.0.3650.75",CHROMIUM_MAJOR_VERSION:"143",VERSION_MS_GEC:"1-143.0.3650",token32(){let p=new Uint8Array(16);return crypto.getRandomValues(p),Array.from(p).map(e=>e.toString(16).padStart(2,"0")).join("").toUpperCase()},getBaseHeaders(){return{"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36 Edg/143.0.0.0","Accept-Encoding":"gzip, deflate, br, zstd","Accept-Language":"en-US,en;q=0.9",Cookie:"MUID="+this.token32()}},WSS_HEADERS:{Pragma:"no-cache","Cache-Control":"no-cache",Origin:"chrome-extension://jdiccldimpdaibmpdkjnbmckianbfold","Sec-WebSocket-Protocol":"synthesize","Sec-WebSocket-Version":"13","User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.3650.75 Safari/537.36 Edg/143.0.3650.75"},VOICE_HEADERS:{"Sec-CH-UA":'" Not;A Brand";v="99", "Microsoft Edge";v="143", "Chromium";v="143"',"Sec-CH-UA-Mobile":"?0",Accept:"*/*","Sec-Fetch-Site":"none","Sec-Fetch-Mode":"cors","Sec-Fetch-Dest":"empty"},OUTPUT_FORMAT:{AUDIO_24KHZ_48KBITRATE_MONO_MP3:"audio-24khz-48kbitrate-mono-mp3",AUDIO_24KHZ_96KBITRATE_MONO_MP3:"audio-24khz-96kbitrate-mono-mp3",WEBM_24KHZ_16BIT_MONO_OPUS:"webm-24khz-16bit-mono-opus"}};var _=class{constructor(){T(this,"audio_stream",[]);T(this,"ws")}async normalizeVoices(e){let s=[];for(let t of e||[]){let r=t?.ShortName||"",o=t?.Locale||"",l=r.replace(/^[a-z]{2}-[A-Z]{2}-/,"");l=l.replace(/NeuralHD$/,"").replace(/Neural$/,"").trim();let g=`${t?.Name||""} ${r}`,u=t?.VoiceType||(/NeuralHD/i.test(g)?"NeuralHD":"Neural"),d=t?.LocaleName||o||null,i=t?.DisplayName||t?.FriendlyName||l||r;i=i.replace(/^Microsoft\s+/i,""),i=i.split(" - ")[0].trim(),i=i.replace(/\s*Online\s*\(Natural\)\s*/i," "),i=i.replace(/\s*Online\s*/i," "),i=i.replace(/\s+/g," ").trim();let n=t?.VoiceTag&&typeof t.VoiceTag=="object"?t.VoiceTag:{},y=Array.isArray(n.TailoredScenarios)?n.TailoredScenarios:Array.isArray(n.ContentCategories)?n.ContentCategories:[],m=Array.isArray(n.VoicePersonalities)?n.VoicePersonalities:[];s.push({Name:r||t?.Name||"",DisplayName:i,LocalName:i,ShortName:r||t?.Name||"",Gender:t?.Gender??null,Locale:o||null,LocaleName:d,SecondaryLocaleList:Array.isArray(t?.SecondaryLocaleList)?t.SecondaryLocaleList:[],VoiceType:u,VoiceTag:{TailoredScenarios:y,VoicePersonalities:m},FriendlyName:`${i} (${u}) - ${d}`})}return s}async getVoices(){let e=await this.generateSecMsGec(a.TRUSTED_CLIENT_TOKEN),t=await(await fetch(`${a.VOICES_URL}?TrustedClientToken=${a.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${e}&Sec-MS-GEC-Version=${a.VERSION_MS_GEC}`,{headers:a.getBaseHeaders()})).json();return this.normalizeVoices(t.voices||[])}async getVoicesByLanguage(e){return(await this.getVoices()).filter(t=>t.Locale.startsWith(e))}async getVoicesByGender(e){return(await this.getVoices()).filter(t=>t.Gender===e)}generateUUID(){return"xxxxxxxx-xxxx-xxxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(e){let s=Math.random()*16|0;return(e==="x"?s:s&3|8).toString(16)})}validatePitch(e){return typeof e=="number"?e>=0?`+${e}Hz`:`${e}Hz`:e}validateRate(e){let s;return typeof e=="string"?s=parseFloat(e.replace("%","")):s=e,s>=0?`+${s}%`:`${s}%`}validateVolume(e){let s;return typeof e=="string"?s=parseInt(e.replace("%",""),10):s=e,`${s}%`}escapeXML(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&apos;")}getSSML(e,s,t={}){let r=this.validatePitch(t.pitch??0),o=this.validateRate(t.rate??0),l=this.validateVolume(t.volume??0),g=this.escapeXML(e);return`<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xmlns:mstts="https://www.w3.org/2001/mstts" xml:lang="en-US"> <voice name="${s}"> <prosody pitch="${r}" rate="${o}" volume="${l}"> ${g} </prosody> </voice> </speak>`}nowRFC1123(){return new Date().toUTCString()}parseRFC1123(e){return new Date(e)}buildTTSConfigMessage(){return`X-Timestamp:${this.nowRFC1123()}\r Content-Type:application/json; charset=utf-8\r Path:speech.config\r \r {"context":{"synthesis":{"audio":{"metadataoptions":{"sentenceBoundaryEnabled":false,"wordBoundaryEnabled":true},"outputFormat":"audio-24khz-48kbitrate-mono-mp3"}}}}`}async synthesize(e,s="en-US-JennyNeural",t={}){let r=await this.generateSecMsGec(a.TRUSTED_CLIENT_TOKEN);return new Promise((o,l)=>{this.audio_stream=[];let g=this.generateUUID(),u=`${a.WSS_URL}?TrustedClientToken=${a.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${r}&Sec-MS-GEC-Version=${a.VERSION_MS_GEC}&ConnectionId=${g}`;this.ws=new WebSocket(u);let d=this.getSSML(e,s,t),i=!1,n,y=()=>{clearTimeout(n),n=window.setTimeout(()=>{i=!0,this.ws&&this.ws.readyState===WebSocket.OPEN&&this.ws.close(),l(new Error("WebSocket inactivity timeout - no response from server"))},3e4)};this.ws.onopen=()=>{y();let m=this.buildTTSConfigMessage();this.ws.send(m);let h=this.nowRFC1123(),f=`X-RequestId:${g}\r Content-Type:application/ssml+xml\r X-Timestamp:${h}\r Path:ssml\r \r ${d}`;this.ws.send(f)},this.ws.onmessage=m=>{if(y(),typeof m.data=="string")m.data.includes("Path:turn.end")&&this.ws?.close();else{let h=new FileReader;h.onload=()=>{let f=h.result,c=new Uint8Array(f);this.processAudioData(c)},h.readAsArrayBuffer(m.data)}},this.ws.onerror=m=>{clearTimeout(n),this.ws&&this.ws.readyState===WebSocket.OPEN&&this.ws.close(),l(new Error("WebSocket error"))},this.ws.onclose=()=>{clearTimeout(n),i||o()}})}async*synthesizeStream(e,s="en-US-JennyNeural",t={}){this.audio_stream=[];let r=this.generateUUID(),o=await this.generateSecMsGec(a.TRUSTED_CLIENT_TOKEN),l=`${a.WSS_URL}?TrustedClientToken=${a.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${o}&Sec-MS-GEC-Version=${a.VERSION_MS_GEC}&ConnectionId=${r}`;this.ws=new WebSocket(l);let g=this.getSSML(e,s,t),u=[],d=!1,i=null,n=null,y=c=>{u.push(c),n&&(n(),n=null)},m=!1,h,f=()=>{clearTimeout(h),h=window.setTimeout(()=>{m=!0,i=new Error("WebSocket inactivity timeout - no response from server"),d=!0,this.ws&&this.ws.readyState===WebSocket.OPEN&&this.ws.close(),n&&(n(),n=null)},3e4)};for(this.ws.onopen=()=>{f();let c=this.buildTTSConfigMessage();this.ws.send(c);let S=this.nowRFC1123(),w=`X-RequestId:${r}\r Content-Type:application/ssml+xml\r X-Timestamp:${S}\r Path:ssml\r \r ${g}`;this.ws.send(w)},this.ws.onmessage=c=>{if(f(),typeof c.data=="string")c.data.includes("Path:turn.end")&&this.ws?.close();else{let S=new FileReader;S.onload=()=>{let w=S.result,E=new Uint8Array(w),C=new TextEncoder().encode(`Path:audio\r `),x=this.indexOf(E,C);if(x!==-1){let A=E.slice(x+C.length);this.audio_stream.push(A),y(A)}},S.readAsArrayBuffer(c.data)}},this.ws.onerror=c=>{clearTimeout(h),i=new Error("WebSocket error"),d=!0,n&&(n(),n=null)},this.ws.onclose=()=>{clearTimeout(h),d=!0,n&&(n(),n=null)};!d||u.length>0;){if(u.length===0){await new Promise(S=>n=S);continue}let c=u.shift();c&&(yield c)}if(i)throw i}processAudioData(e){let s=new TextEncoder().encode(`Path:audio\r `),t=this.indexOf(e,s);if(t!==-1){let r=e.slice(t+s.length);this.audio_stream.push(r)}}indexOf(e,s){for(let t=0;t<=e.length-s.length;t++){let r=!0;for(let o=0;o<s.length;o++)if(e[t+o]!==s[o]){r=!1;break}if(r)return t}return-1}async generateSecMsGec(e){let s=this.nowRFC1123(),t=this.parseRFC1123(s),r=Math.floor(t.getTime()/1e3)+11644473600,l=(r-r%300)*1e7,u=new TextEncoder().encode(`${l}${e}`),d=await crypto.subtle.digest("SHA-256",u);return Array.from(new Uint8Array(d)).map(i=>i.toString(16).padStart(2,"0")).join("").toUpperCase()}getAudioData(){if(this.audio_stream.length===0)throw new Error("No audio data available");let e=this.audio_stream.reduce((r,o)=>r+o.length,0),s=new Uint8Array(e),t=0;for(let r of this.audio_stream)s.set(r,t),t+=r.length;return s}};export{_ as EdgeTTS}; //# sourceMappingURL=edge-tts.esm.min.js.map