Skip to content

Commit c78e49d

Browse files
authored
Cleanup SentenceBoundary support (#396)
- Default to SentenceBoundary - Modify boundary argument to lowercase to match other options. - Drop merge_cues support as SentenceBoundary renders it obsolete. Signed-off-by: rany <rany2@riseup.net>
1 parent 645c207 commit c78e49d

File tree

5 files changed

+17
-61
lines changed

5 files changed

+17
-61
lines changed

examples/sync_audio_streaming_with_predefined_voice_subtitles_print2stdout.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424

2525
def main() -> None:
2626
"""Main function"""
27-
communicate = edge_tts.Communicate(TEXT, VOICE, Boundary="SentenceBoundary")
27+
communicate = edge_tts.Communicate(TEXT, VOICE, boundary="SentenceBoundary")
2828
submaker = edge_tts.SubMaker()
2929
stdout = sys.stdout
3030
audio_bytes = []

src/edge_tts/communicate.py

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,12 @@
1919
Literal,
2020
Optional,
2121
Tuple,
22-
TypedDict,
2322
Union,
2423
)
2524
from xml.sax.saxutils import escape, unescape
2625

2726
import aiohttp
2827
import certifi
29-
from typing_extensions import NotRequired, Unpack
3028

3129
from .constants import DEFAULT_VOICE, SEC_MS_GEC_VERSION, WSS_HEADERS, WSS_URL
3230
from .data_classes import TTSConfig
@@ -311,19 +309,12 @@ def ssml_headers_plus_data(request_id: str, timestamp: str, ssml: str) -> str:
311309
)
312310

313311

314-
class CommunicateRequest(TypedDict):
315-
"""
316-
A class to communicate with the service.
317-
"""
318-
319-
Boundary: NotRequired[Literal["WordBoundary", "SentenceBoundary"]]
320-
321-
322312
class Communicate:
323313
"""
324314
Communicate with the service.
325315
"""
326316

317+
# pylint: disable=too-many-arguments
327318
def __init__(
328319
self,
329320
text: str,
@@ -332,24 +323,13 @@ def __init__(
332323
rate: str = "+0%",
333324
volume: str = "+0%",
334325
pitch: str = "+0Hz",
326+
boundary: Literal["WordBoundary", "SentenceBoundary"] = "SentenceBoundary",
335327
connector: Optional[aiohttp.BaseConnector] = None,
336328
proxy: Optional[str] = None,
337329
connect_timeout: Optional[int] = 10,
338330
receive_timeout: Optional[int] = 60,
339-
**kwargs: Unpack[CommunicateRequest],
340331
):
341-
"""
342-
Args:
343-
boundary (str): The boundary to use for the TTS.
344-
Defaults to "WordBoundary".
345-
Valid values are "WordBoundary" and "SentenceBoundary".
346-
If "WordBoundary", the TTS will return a word boundary for each word.
347-
If "SentenceBoundary", the TTS will return a sentence boundary for each sentence.
348-
Which is more friendly to Chinese users.
349-
"""
350-
351332
# Validate TTS settings and store the TTSConfig object.
352-
boundary = kwargs.get("Boundary", "WordBoundary")
353333
self.tts_config = TTSConfig(voice, rate, volume, pitch, boundary)
354334

355335
# Validate the text parameter.

src/edge_tts/data_classes.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import argparse
66
import re
77
from dataclasses import dataclass
8+
from typing import Literal
89

910

1011
@dataclass
@@ -17,7 +18,7 @@ class TTSConfig:
1718
rate: str
1819
volume: str
1920
pitch: str
20-
boundary: str
21+
boundary: Literal["WordBoundary", "SentenceBoundary"]
2122

2223
@staticmethod
2324
def validate_string_param(param_name: str, param_value: str, pattern: str) -> str:

src/edge_tts/submaker.py

Lines changed: 12 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""SubMaker module is used to generate subtitles from WordBoundary and SentenceBoundary events."""
22

33
from datetime import timedelta
4-
from typing import List
4+
from typing import List, Optional
55

66
from .srt_composer import Subtitle, compose
77
from .typing import TTSChunk
@@ -14,6 +14,7 @@ class SubMaker:
1414

1515
def __init__(self) -> None:
1616
self.cues: List[Subtitle] = []
17+
self.type: Optional[str] = None
1718

1819
def feed(self, msg: TTSChunk) -> None:
1920
"""
@@ -26,7 +27,16 @@ def feed(self, msg: TTSChunk) -> None:
2627
None
2728
"""
2829
if msg["type"] not in ("WordBoundary", "SentenceBoundary"):
29-
raise ValueError("Invalid message type, expected 'WordBoundary'")
30+
raise ValueError(
31+
"Invalid message type, expected 'WordBoundary' or 'SentenceBoundary'."
32+
)
33+
34+
if self.type is None:
35+
self.type = msg["type"]
36+
elif self.type != msg["type"]:
37+
raise ValueError(
38+
f"Expected message type '{self.type}', but got '{msg['type']}'."
39+
)
3040

3141
self.cues.append(
3242
Subtitle(
@@ -37,38 +47,6 @@ def feed(self, msg: TTSChunk) -> None:
3747
)
3848
)
3949

40-
def merge_cues(self, words: int) -> None:
41-
"""
42-
Merge cues to reduce the number of cues.
43-
44-
Args:
45-
words (int): The number of words to merge.
46-
47-
Returns:
48-
None
49-
"""
50-
if words <= 0:
51-
raise ValueError("Invalid number of words to merge, expected > 0")
52-
53-
if len(self.cues) == 0:
54-
return
55-
56-
new_cues: List[Subtitle] = []
57-
current_cue: Subtitle = self.cues[0]
58-
for cue in self.cues[1:]:
59-
if len(current_cue.content.split()) < words:
60-
current_cue = Subtitle(
61-
index=current_cue.index,
62-
start=current_cue.start,
63-
end=cue.end,
64-
content=f"{current_cue.content} {cue.content}",
65-
)
66-
else:
67-
new_cues.append(current_cue)
68-
current_cue = cue
69-
new_cues.append(current_cue)
70-
self.cues = new_cues
71-
7250
def get_srt(self) -> str:
7351
"""
7452
Get the SRT formatted subtitles from the SubMaker object.

src/edge_tts/util.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,6 @@ async def _run_tts(args: UtilArgs) -> None:
7575
elif chunk["type"] in ("WordBoundary", "SentenceBoundary"):
7676
submaker.feed(chunk)
7777

78-
if args.words_in_cue > 0:
79-
submaker.merge_cues(args.words_in_cue)
80-
8178
if sub_file is not None:
8279
sub_file.write(submaker.get_srt())
8380
finally:

0 commit comments

Comments
 (0)