分享一下我后来搭的TTS链接,和上面k兄的替换方法一致
解决方案2 :js中搜索speech.platform.bing.com 替换为public.fanqi987.dpdns.org/public/wss/speech.platform.bing.com
分享一下我后来搭的TTS链接,和上面k兄的替换方法一致
解决方案2 :js中搜索speech.platform.bing.com 替换为public.fanqi987.dpdns.org/public/wss/speech.platform.bing.com
感谢分享!能用。
Hip, hip, hooray!
感谢分享,可以用。
实测,深蓝,可用!谢谢!!!
[quote=“fanqi987, post:44, topic:42406”]
public.fanqi987.dpdns.org/public/wss/speech.platform.bing.com
实测了,原来两个灰的小喇叭都可以朗读,感谢 ![]()
dictango(电脑端)为什么可以朗读……是因为用的edge webview还是自己实现了tts..
真好!真强!感谢!
平板上实测可用,感谢![]()
请问修改这个地址需要梯子吗?没有梯子能用吗
不需要梯子啊
Everything’s running smoothly as of 04.09.26! Thanks a lot!
又失效了。顺问对EdgeTTS现在有可靠的websocket fetch方法吗?手机app貌似只支持这种
使用国内引擎让TTS本地化,这个可行性如何
PC上容易解决,手机上较难,有很多限制。
TTS 签名算法升级为 GEC v2,可以叫AI改一下
Deepseek帮改后还是没有效果
适用于 oald.js 中使用 createEdgeTTS()、generateSecMsGecToken(),并携带旧版 Sec-MS-GEC-Version=1-130.0.2849.68 的 OALD10 版本。
本文代码取自已部署脚本。签名算法移植自 edge-tts 7.3.1 的 edge_tts/drm.py。这是对当时接口的兼容修复,不保证微软后续修改协议后仍永久有效。
只修改两处:签名函数、协议版本。不要只改版本号。 公共代理地址、音色、音频解析、界面和 CSS 均无需改动。
先备份词典目录中的 oald.js,例如另存为 oald.before-gecv2.js。用支持 UTF-8 的代码编辑器打开原文件,尽量保留原来的换行符。
若文件已经使用本文的完整 GEC v2 函数及版本号,无须重复修改。
在 createEdgeTTS() 内找到:
function generateSecMsGecToken() {
// 原来的签名代码
}
将整个函数(包括函数体及末尾花括号)替换为下面的完整代码。保留后面原有的 async function ensureSocketReady(),不要一并删除。
此函数继续使用 createEdgeTTS() 内原有的 TRUSTED_CLIENT_TOKEN 常量。无需添加 Python 服务或额外的 JS 文件。
function generateSecMsGecToken() {
// GEC v2; port of edge-tts DRM. All arithmetic is modulo 2^32.
var C = {"initial":[1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225],"state":[4255104674,2435071207,3192492180,3442351626,1811967817,79508907,4272990180,66059218],"schedule":[3945054110,358115615,2696978146,217723578,2787307148,3520959633,3235718560,4192668342,4039316851,1884868174,1904797190,15051571,3529027680,1920361828],"rounds":[1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298],"adjustment":3386640039,"coefficients":[[3895366065,3005677776],[1155409094,2296378653],[1005282748,693190712],[3365689651,3005795295],[2367603217,3557143765],[827258325,652566332],[3911487921,1618477510],[4037392853,2192753147],[101076211,4199654362],[3665427040,545047652],[3201434976,1010595059],[2314848760,3536931681],[905590982,3137643201],[3410617286,3311559015]]};
function rol(v) { return ((v << 16) | (v >>> 16)) >>> 0; }
function rr(v,n) { return (v >>> n) | (v << (32-n)); }
function s0(v) { return (rr(v,7)^rr(v,18)^(v>>>3))>>>0; }
function s1(v) { return (rr(v,17)^rr(v,19)^(v>>>10))>>>0; }
function expand(w,start) {
for(var i=start;i<64;i++) w[i]=(s1(w[i-2])+w[i-7]+s0(w[i-15])+w[i-16])>>>0;
}
function rounds(state,w,first) {
var a=state[0],b=state[1],c=state[2],d=state[3],e=state[4],f=state[5],g=state[6],h=state[7];
for(var i=first;i<64;i++) {
var k=(first===14 && i===14)?C.adjustment:C.rounds[i];
// Explicit unsigned conversions: JS XOR returns a signed int32.
var t1=(h+((rr(e,6)^rr(e,11)^rr(e,25))>>>0)+((g^(e&(f^g)))>>>0)+k+w[i])>>>0;
var t2=(((rr(a,2)^rr(a,13)^rr(a,22))>>>0)+(((a&b)|(c&(a|b)))>>>0))>>>0;
h=g;g=f;f=e;e=(d+t1)>>>0;d=c;c=b;b=a;a=(t1+t2)>>>0;
}
return [a,b,c,d,e,f,g,h];
}
function sum(a,b) { return a.map(function(x,i){return (x+b[i])>>>0;}); }
var seconds=Math.floor((Date.now()/1000+11644473600)/300)*300;
var text=String(BigInt(seconds)*BigInt(10000000))+TRUSTED_CLIENT_TOKEN;
var data=Array.prototype.map.call(text,function(x){return x.charCodeAt(0);});
function be(bytes,p) { return ((bytes[p]<<24)|(bytes[p+1]<<16)|(bytes[p+2]<<8)|bytes[p+3])>>>0; }
function le(bytes,p) { return ((bytes[p]||0)|((bytes[p+1]||0)<<8)|((bytes[p+2]||0)<<16)|((bytes[p+3]||0)<<24))>>>0; }
var w=Array(14).fill(0);
w.push((data[0]<<16)|(data[1]<<8)|data[2]);w.push(be(data,3));
for(var i=16;i<30;i++) {
var v=C.schedule[i-16];
if(i-2>=14) v+=i===16?(s1(w[i-2])^0x000B92C0)>>>0:s1(w[i-2]);
if(i-7>=14) v+=w[i-7];
if(i-15>=14) v+=i===29?(s0(w[i-15])^0x059C0B80)>>>0:s0(w[i-15]);
w.push(v>>>0);
}
w.push((s1(w[28])+w[23]+s0(w[15])+w[14]+C.adjustment-C.rounds[14])>>>0);
expand(w,31);
var state=sum(C.initial,rounds(C.state,w,14));
var length=57+data.length,padded=data.slice(7);padded.push(128);
var zeroes=((55-length)%64+64)%64;
while(zeroes-->0)padded.push(0);
var bits=length*8;
padded.push(0,0,0,0,(bits>>>24)&255,(bits>>>16)&255,(bits>>>8)&255,bits&255);
for(var p=0;p<padded.length;p+=64) {
w=[];for(var j=0;j<16;j++)w.push(be(padded,p+j*4));
expand(w,16);state=sum(state,rounds(state,w,0));
}
var digest=[];
state.forEach(function(v){digest.push(v>>>24,(v>>>16)&255,(v>>>8)&255,v&255);});
var keyA=(le(digest,0)|1)>>>0,keyB=(le(digest,4)|1)>>>0;
var factors=[Math.imul(keyA,0x82893933)>>>0,Math.imul(keyB,0x82893933)>>>0];
var terms=[],offsets=[];
C.coefficients.forEach(function(pair,i){terms.push(Math.imul(pair[0],factors[i%2])>>>0);offsets.push(pair[1]);});
var wordsData=[0].concat(data);
var count=Math.ceil(wordsData.length/4);if(count%2)count++;
for(i=0;i<count;i++) {
var word=le(wordsData,i*4),term=Math.imul(word,i%2?keyB:keyA),offset=Math.imul(rol(word),i%2?0xC31713DB:0xEF0569FB);
if(i===0){term+=Math.imul(0xF2F90B1A,factors[0]);offset+=0x0B1A0000;}
terms.push(term>>>0);offsets.push(offset>>>0);
}
var swapAcc=0,swapLast=0,csState=0,acc=0,t,q;
for(i=0;i<terms.length;i+=2) {
var x=terms[i],y=terms[i+1];
t=rol((offsets[i]+x)>>>0);q=Math.imul(t,0x76F50F34)>>>0;
swapAcc+=(Math.imul(rol(q),0xC3EFEA97)+q)>>>0;
t=rol((offsets[i+1]+y)>>>0);q=Math.imul(t,0x1881CE3C)>>>0;
swapLast=(Math.imul(rol(q),0x35BD1EC9)+q)>>>0;swapAcc=(swapAcc+swapLast)>>>0;
csState=(Math.imul(csState,keyA)+x)>>>0;
[0xCF98B111,0x87085B9F,0x12CEB96D,0x257E1D83].forEach(function(k){csState=Math.imul(rol(csState),k)>>>0;});
acc+=csState;t=(Math.imul(csState,keyB)+y)>>>0;
t=Math.imul(rol(t),0xA27416F5)>>>0;t=Math.imul(rol(t),0xD38396FF)>>>0;
csState=Math.imul(rol(Math.imul(rol(t),0x7C932B89)>>>0),0xBFA49F69)>>>0;
acc=(acc+csState)>>>0;
}
function hex(bytes){return bytes.map(function(v){return ('0'+v.toString(16)).slice(-2);}).join('').toUpperCase();}
var cs=[];
[(swapLast^csState)>>>0,(swapAcc^acc)>>>0].forEach(function(v){cs.push(v&255,(v>>>8)&255,(v>>>16)&255,v>>>24);});
return hex(cs)+'-'+hex(digest);
}
BigInt 保证 Windows 时间刻度计算精确。Math.imul() 和 >>> 0 保证 32 位整数计算与参考实现一致。CryptoJS.SHA256() 直接替代。<16位大写十六进制>-<64位大写十六进制>。在 ensureSocketReady() 中找到:
const Sec_MS_GEC_VERSION = "1-130.0.2849.68";
改为:
const Sec_MS_GEC_VERSION = "2-143.0.3650.75";
修改后的连接部分应保留如下结构:
const Sec_MS_GEC = generateSecMsGecToken();
const Sec_MS_GEC_VERSION = "2-143.0.3650.75";
socket = new WebSocket(
`${SYNTH_URL}&Sec-MS-GEC=${Sec_MS_GEC}&Sec-MS-GEC-Version=${Sec_MS_GEC_VERSION}`
);
本次修复不需要切换代理。原配置保持:
const TRUSTED_CLIENT_TOKEN = "6A5AA1D4EAFF4E9FB37E23D68491D6F4";
const SYNTH_URL = `wss://public.fanqi987.dpdns.org/public/wss/speech.platform.bing.com/consumer/speech/synthesize/readaloud/edge/v1?TrustedClientToken=${TRUSTED_CLIENT_TOKEN}`;
这里的 TRUSTED_CLIENT_TOKEN 是客户端使用的固定协议常量,不是用户私人 API Key。不要把自建代理的私人访问令牌写进公开教程。
原有的 CryptoJS 加载代码可以保留,不要顺带删除其他依赖。
oald.js,确保文件名和词典原来引用的一致。node --check oald.js
无需重新打包 MDX/MDD,也不需要修改 CSS。 本教程针对词典已从外部加载该 oald.js 的部署方式;若使用其他封装方式,需要先确认实际加载的脚本位置。
play() 使用测试替身,验证了真实联网合成和原音频解析,不等同于欧路真机已实际发声。最终播放效果需在欧路内确认。oald.js,而不是同名旧副本。用备份的原脚本恢复 oald.js,完全退出欧路后重开即可。注意:回滚只撤销修改,不保证旧签名仍能被微软接受。
大神,牛!目前可用。
感觉又到折腾没底的模式了,试了这方法,还是没效果。
在原来的js代理换成收费的,其他未变,马上就OK,侧面证明同这个关系不大