Update API endpoints used by Edge TTS (#412)

Fixes https://github.com/rany2/edge-tts/issues/411

Signed-off-by: rany <rany2@riseup.net>
This commit is contained in:
rany2
2025-08-28 19:26:07 +03:00
committed by GitHub
parent 7c5eb17a14
commit bfc54f4acd
4 changed files with 28 additions and 65 deletions

View File

@@ -433,9 +433,9 @@ class Communicate:
trust_env=True, trust_env=True,
timeout=self.session_timeout, timeout=self.session_timeout,
) as session, session.ws_connect( ) as session, session.ws_connect(
f"{WSS_URL}&Sec-MS-GEC={DRM.generate_sec_ms_gec()}" f"{WSS_URL}&ConnectionId={connect_id()}"
f"&Sec-MS-GEC-Version={SEC_MS_GEC_VERSION}" f"&Sec-MS-GEC={DRM.generate_sec_ms_gec()}"
f"&ConnectionId={connect_id()}", f"&Sec-MS-GEC-Version={SEC_MS_GEC_VERSION}",
compress=15, compress=15,
proxy=self.proxy, proxy=self.proxy,
headers=WSS_HEADERS, headers=WSS_HEADERS,

View File

@@ -1,10 +1,14 @@
"""Constants for the edge_tts package.""" """Constants for the edge_tts package."""
BASE_URL = "speech.platform.bing.com/consumer/speech/synthesize/readaloud" BASE_URL = "api.msedgeservices.com/tts/cognitiveservices"
TRUSTED_CLIENT_TOKEN = "6A5AA1D4EAFF4E9FB37E23D68491D6F4" TRUSTED_CLIENT_TOKEN = "6A5AA1D4EAFF4E9FB37E23D68491D6F4"
WSS_URL = f"wss://{BASE_URL}/edge/v1?TrustedClientToken={TRUSTED_CLIENT_TOKEN}" WSS_URL = (
VOICE_LIST = f"https://{BASE_URL}/voices/list?trustedclienttoken={TRUSTED_CLIENT_TOKEN}" f"wss://{BASE_URL}/websocket/v1?Ocp-Apim-Subscription-Key={TRUSTED_CLIENT_TOKEN}"
)
VOICE_LIST = (
f"https://{BASE_URL}/voices/list?Ocp-Apim-Subscription-Key={TRUSTED_CLIENT_TOKEN}"
)
DEFAULT_VOICE = "en-US-EmmaMultilingualNeural" DEFAULT_VOICE = "en-US-EmmaMultilingualNeural"
@@ -22,6 +26,8 @@ WSS_HEADERS = {
"Pragma": "no-cache", "Pragma": "no-cache",
"Cache-Control": "no-cache", "Cache-Control": "no-cache",
"Origin": "chrome-extension://jdiccldimpdaibmpdkjnbmckianbfold", "Origin": "chrome-extension://jdiccldimpdaibmpdkjnbmckianbfold",
"Sec-WebSocket-Protocol": "synthesize",
"Sec-WebSocket-Version": "13",
} }
WSS_HEADERS.update(BASE_HEADERS) WSS_HEADERS.update(BASE_HEADERS)
VOICE_HEADERS = { VOICE_HEADERS = {

View File

@@ -20,49 +20,8 @@ class TTSChunk(TypedDict):
class VoiceTag(TypedDict): class VoiceTag(TypedDict):
"""VoiceTag data.""" """VoiceTag data."""
ContentCategories: List[ ContentCategories: List[str]
Literal[ VoicePersonalities: List[str]
"Cartoon",
"Conversation",
"Copilot",
"Dialect",
"General",
"News",
"Novel",
"Sports",
]
]
VoicePersonalities: List[
Literal[
"Approachable",
"Authentic",
"Authority",
"Bright",
"Caring",
"Casual",
"Cheerful",
"Clear",
"Comfort",
"Confident",
"Considerate",
"Conversational",
"Cute",
"Expressive",
"Friendly",
"Honest",
"Humorous",
"Lively",
"Passion",
"Pleasant",
"Positive",
"Professional",
"Rational",
"Reliable",
"Sincere",
"Sunshine",
"Warm",
]
]
class Voice(TypedDict): class Voice(TypedDict):
@@ -70,14 +29,15 @@ class Voice(TypedDict):
Name: str Name: str
ShortName: str ShortName: str
Gender: Literal["Female", "Male"] DisplayName: str
LocalName: str
LocaleName: str
Locale: str Locale: str
SuggestedCodec: Literal["audio-24khz-48kbitrate-mono-mp3"] Gender: Literal["Female", "Male"]
FriendlyName: str WordsPerMinute: str
Status: Literal["GA"] Status: Literal["Deprecated", "GA", "Preview"]
VoiceTag: VoiceTag VoiceTag: VoiceTag
class VoicesManagerVoice(Voice): class VoicesManagerVoice(Voice):
"""Voice data for VoicesManager.""" """Voice data for VoicesManager."""

View File

@@ -41,17 +41,14 @@ async def __list_voices(
data: List[Voice] = json.loads(await url.text()) data: List[Voice] = json.loads(await url.text())
for voice in data: for voice in data:
# Remove leading and trailing whitespace from categories and personalities. if "VoiceTag" not in voice:
# This has only happened in one case with the zh-CN-YunjianNeural voice voice["VoiceTag"] = {}
# where there was a leading space in one of the categories.
voice["VoiceTag"]["ContentCategories"] = [ if "ContentCategories" not in voice["VoiceTag"]:
category.strip() # type: ignore voice["VoiceTag"]["ContentCategories"] = []
for category in voice["VoiceTag"]["ContentCategories"]
] if "VoicePersonalities" not in voice["VoiceTag"]:
voice["VoiceTag"]["VoicePersonalities"] = [ voice["VoiceTag"]["VoicePersonalities"] = []
personality.strip() # type: ignore
for personality in voice["VoiceTag"]["VoicePersonalities"]
]
return data return data