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.

26 lines (25 loc) 8.87 kB
"use strict";var EdgeTTSModule=(()=>{var w=Object.defineProperty;var M=Object.getOwnPropertyDescriptor;var U=Object.getOwnPropertyNames;var v=Object.prototype.hasOwnProperty;var b=(o,e,t)=>e in o?w(o,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):o[e]=t;var O=(o,e)=>{for(var t in e)w(o,t,{get:e[t],enumerable:!0})},k=(o,e,t,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of U(e))!v.call(o,r)&&r!==t&&w(o,r,{get:()=>e[r],enumerable:!(s=M(e,r))||s.enumerable});return o};var L=o=>k(w({},"__esModule",{value:!0}),o);var E=(o,e,t)=>b(o,typeof e!="symbol"?e+"":e,t);var R={};O(R,{EdgeTTS:()=>C});var c={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 o=new Uint8Array(16);return crypto.getRandomValues(o),Array.from(o).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 C=class{constructor(){E(this,"audio_stream",[]);E(this,"ws")}async normalizeVoices(e){let t=[];for(let s of e||[]){let r=s?.ShortName||"",a=s?.Locale||"",u=r.replace(/^[a-z]{2}-[A-Z]{2}-/,"");u=u.replace(/NeuralHD$/,"").replace(/Neural$/,"").trim();let g=`${s?.Name||""} ${r}`,d=s?.VoiceType||(/NeuralHD/i.test(g)?"NeuralHD":"Neural"),m=s?.LocaleName||a||null,i=s?.DisplayName||s?.FriendlyName||u||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=s?.VoiceTag&&typeof s.VoiceTag=="object"?s.VoiceTag:{},y=Array.isArray(n.TailoredScenarios)?n.TailoredScenarios:Array.isArray(n.ContentCategories)?n.ContentCategories:[],h=Array.isArray(n.VoicePersonalities)?n.VoicePersonalities:[];t.push({Name:r||s?.Name||"",DisplayName:i,LocalName:i,ShortName:r||s?.Name||"",Gender:s?.Gender??null,Locale:a||null,LocaleName:m,SecondaryLocaleList:Array.isArray(s?.SecondaryLocaleList)?s.SecondaryLocaleList:[],VoiceType:d,VoiceTag:{TailoredScenarios:y,VoicePersonalities:h},FriendlyName:`${i} (${d}) - ${m}`})}return t}async getVoices(){let e=await this.generateSecMsGec(c.TRUSTED_CLIENT_TOKEN),s=await(await fetch(`${c.VOICES_URL}?TrustedClientToken=${c.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${e}&Sec-MS-GEC-Version=${c.VERSION_MS_GEC}`,{headers:c.getBaseHeaders()})).json();return this.normalizeVoices(s.voices||[])}async getVoicesByLanguage(e){return(await this.getVoices()).filter(s=>s.Locale.startsWith(e))}async getVoicesByGender(e){return(await this.getVoices()).filter(s=>s.Gender===e)}generateUUID(){return"xxxxxxxx-xxxx-xxxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(e){let t=Math.random()*16|0;return(e==="x"?t:t&3|8).toString(16)})}validatePitch(e){return typeof e=="number"?e>=0?`+${e}Hz`:`${e}Hz`:e}validateRate(e){let t;return typeof e=="string"?t=parseFloat(e.replace("%","")):t=e,t>=0?`+${t}%`:`${t}%`}validateVolume(e){let t;return typeof e=="string"?t=parseInt(e.replace("%",""),10):t=e,`${t}%`}escapeXML(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&apos;")}getSSML(e,t,s={}){let r=this.validatePitch(s.pitch??0),a=this.validateRate(s.rate??0),u=this.validateVolume(s.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="${t}"> <prosody pitch="${r}" rate="${a}" volume="${u}"> ${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,t="en-US-JennyNeural",s={}){let r=await this.generateSecMsGec(c.TRUSTED_CLIENT_TOKEN);return new Promise((a,u)=>{this.audio_stream=[];let g=this.generateUUID(),d=`${c.WSS_URL}?TrustedClientToken=${c.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${r}&Sec-MS-GEC-Version=${c.VERSION_MS_GEC}&ConnectionId=${g}`;this.ws=new WebSocket(d);let m=this.getSSML(e,t,s),i=!1,n,y=()=>{clearTimeout(n),n=window.setTimeout(()=>{i=!0,this.ws&&this.ws.readyState===WebSocket.OPEN&&this.ws.close(),u(new Error("WebSocket inactivity timeout - no response from server"))},3e4)};this.ws.onopen=()=>{y();let h=this.buildTTSConfigMessage();this.ws.send(h);let p=this.nowRFC1123(),f=`X-RequestId:${g}\r Content-Type:application/ssml+xml\r X-Timestamp:${p}\r Path:ssml\r \r ${m}`;this.ws.send(f)},this.ws.onmessage=h=>{if(y(),typeof h.data=="string")h.data.includes("Path:turn.end")&&this.ws?.close();else{let p=new FileReader;p.onload=()=>{let f=p.result,l=new Uint8Array(f);this.processAudioData(l)},p.readAsArrayBuffer(h.data)}},this.ws.onerror=h=>{clearTimeout(n),this.ws&&this.ws.readyState===WebSocket.OPEN&&this.ws.close(),u(new Error("WebSocket error"))},this.ws.onclose=()=>{clearTimeout(n),i||a()}})}async*synthesizeStream(e,t="en-US-JennyNeural",s={}){this.audio_stream=[];let r=this.generateUUID(),a=await this.generateSecMsGec(c.TRUSTED_CLIENT_TOKEN),u=`${c.WSS_URL}?TrustedClientToken=${c.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${a}&Sec-MS-GEC-Version=${c.VERSION_MS_GEC}&ConnectionId=${r}`;this.ws=new WebSocket(u);let g=this.getSSML(e,t,s),d=[],m=!1,i=null,n=null,y=l=>{d.push(l),n&&(n(),n=null)},h=!1,p,f=()=>{clearTimeout(p),p=window.setTimeout(()=>{h=!0,i=new Error("WebSocket inactivity timeout - no response from server"),m=!0,this.ws&&this.ws.readyState===WebSocket.OPEN&&this.ws.close(),n&&(n(),n=null)},3e4)};for(this.ws.onopen=()=>{f();let l=this.buildTTSConfigMessage();this.ws.send(l);let S=this.nowRFC1123(),T=`X-RequestId:${r}\r Content-Type:application/ssml+xml\r X-Timestamp:${S}\r Path:ssml\r \r ${g}`;this.ws.send(T)},this.ws.onmessage=l=>{if(f(),typeof l.data=="string")l.data.includes("Path:turn.end")&&this.ws?.close();else{let S=new FileReader;S.onload=()=>{let T=S.result,x=new Uint8Array(T),A=new TextEncoder().encode(`Path:audio\r `),_=this.indexOf(x,A);if(_!==-1){let N=x.slice(_+A.length);this.audio_stream.push(N),y(N)}},S.readAsArrayBuffer(l.data)}},this.ws.onerror=l=>{clearTimeout(p),i=new Error("WebSocket error"),m=!0,n&&(n(),n=null)},this.ws.onclose=()=>{clearTimeout(p),m=!0,n&&(n(),n=null)};!m||d.length>0;){if(d.length===0){await new Promise(S=>n=S);continue}let l=d.shift();l&&(yield l)}if(i)throw i}processAudioData(e){let t=new TextEncoder().encode(`Path:audio\r `),s=this.indexOf(e,t);if(s!==-1){let r=e.slice(s+t.length);this.audio_stream.push(r)}}indexOf(e,t){for(let s=0;s<=e.length-t.length;s++){let r=!0;for(let a=0;a<t.length;a++)if(e[s+a]!==t[a]){r=!1;break}if(r)return s}return-1}async generateSecMsGec(e){let t=this.nowRFC1123(),s=this.parseRFC1123(t),r=Math.floor(s.getTime()/1e3)+11644473600,u=(r-r%300)*1e7,d=new TextEncoder().encode(`${u}${e}`),m=await crypto.subtle.digest("SHA-256",d);return Array.from(new Uint8Array(m)).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,a)=>r+a.length,0),t=new Uint8Array(e),s=0;for(let r of this.audio_stream)t.set(r,s),s+=r.length;return t}};return L(R);})(); window.EdgeTTS = EdgeTTSModule.EdgeTTS; window.EdgeTTSVoice = EdgeTTSModule.Voice; //# sourceMappingURL=edge-tts.umd.min.js.map