webscout 8.2.2__py3-none-any.whl → 8.2.7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of webscout might be problematic. Click here for more details.
- webscout/AIauto.py +112 -22
- webscout/AIbase.py +144 -7
- webscout/AIutel.py +249 -131
- webscout/Bard.py +579 -206
- webscout/DWEBS.py +78 -35
- webscout/__init__.py +0 -1
- webscout/cli.py +256 -0
- webscout/conversation.py +307 -436
- webscout/exceptions.py +23 -0
- webscout/prompt_manager.py +56 -42
- webscout/version.py +1 -1
- webscout/webscout_search.py +65 -47
- webscout/webscout_search_async.py +81 -126
- webscout/yep_search.py +93 -43
- {webscout-8.2.2.dist-info → webscout-8.2.7.dist-info}/METADATA +172 -52
- webscout-8.2.7.dist-info/RECORD +26 -0
- {webscout-8.2.2.dist-info → webscout-8.2.7.dist-info}/WHEEL +1 -1
- webscout-8.2.7.dist-info/entry_points.txt +3 -0
- webscout-8.2.7.dist-info/top_level.txt +1 -0
- inferno/__init__.py +0 -6
- inferno/__main__.py +0 -9
- inferno/cli.py +0 -6
- webscout/Extra/GitToolkit/__init__.py +0 -10
- webscout/Extra/GitToolkit/gitapi/__init__.py +0 -12
- webscout/Extra/GitToolkit/gitapi/repository.py +0 -195
- webscout/Extra/GitToolkit/gitapi/user.py +0 -96
- webscout/Extra/GitToolkit/gitapi/utils.py +0 -62
- webscout/Extra/YTToolkit/YTdownloader.py +0 -957
- webscout/Extra/YTToolkit/__init__.py +0 -3
- webscout/Extra/YTToolkit/transcriber.py +0 -476
- webscout/Extra/YTToolkit/ytapi/__init__.py +0 -6
- webscout/Extra/YTToolkit/ytapi/channel.py +0 -307
- webscout/Extra/YTToolkit/ytapi/errors.py +0 -13
- webscout/Extra/YTToolkit/ytapi/extras.py +0 -45
- webscout/Extra/YTToolkit/ytapi/https.py +0 -88
- webscout/Extra/YTToolkit/ytapi/patterns.py +0 -61
- webscout/Extra/YTToolkit/ytapi/playlist.py +0 -59
- webscout/Extra/YTToolkit/ytapi/pool.py +0 -8
- webscout/Extra/YTToolkit/ytapi/query.py +0 -40
- webscout/Extra/YTToolkit/ytapi/stream.py +0 -63
- webscout/Extra/YTToolkit/ytapi/utils.py +0 -62
- webscout/Extra/YTToolkit/ytapi/video.py +0 -232
- webscout/Extra/__init__.py +0 -7
- webscout/Extra/autocoder/__init__.py +0 -9
- webscout/Extra/autocoder/autocoder.py +0 -849
- webscout/Extra/autocoder/autocoder_utiles.py +0 -332
- webscout/Extra/gguf.py +0 -682
- webscout/Extra/tempmail/__init__.py +0 -28
- webscout/Extra/tempmail/async_utils.py +0 -141
- webscout/Extra/tempmail/base.py +0 -161
- webscout/Extra/tempmail/cli.py +0 -187
- webscout/Extra/tempmail/emailnator.py +0 -84
- webscout/Extra/tempmail/mail_tm.py +0 -361
- webscout/Extra/tempmail/temp_mail_io.py +0 -292
- webscout/Extra/weather.py +0 -194
- webscout/Extra/weather_ascii.py +0 -76
- webscout/LLM.py +0 -442
- webscout/Litlogger/__init__.py +0 -67
- webscout/Litlogger/core/__init__.py +0 -6
- webscout/Litlogger/core/level.py +0 -23
- webscout/Litlogger/core/logger.py +0 -165
- webscout/Litlogger/handlers/__init__.py +0 -12
- webscout/Litlogger/handlers/console.py +0 -33
- webscout/Litlogger/handlers/file.py +0 -143
- webscout/Litlogger/handlers/network.py +0 -173
- webscout/Litlogger/styles/__init__.py +0 -7
- webscout/Litlogger/styles/colors.py +0 -249
- webscout/Litlogger/styles/formats.py +0 -458
- webscout/Litlogger/styles/text.py +0 -87
- webscout/Litlogger/utils/__init__.py +0 -6
- webscout/Litlogger/utils/detectors.py +0 -153
- webscout/Litlogger/utils/formatters.py +0 -200
- webscout/Local/__init__.py +0 -12
- webscout/Local/__main__.py +0 -9
- webscout/Local/api.py +0 -576
- webscout/Local/cli.py +0 -516
- webscout/Local/config.py +0 -75
- webscout/Local/llm.py +0 -287
- webscout/Local/model_manager.py +0 -253
- webscout/Local/server.py +0 -721
- webscout/Local/utils.py +0 -93
- webscout/Provider/AI21.py +0 -177
- webscout/Provider/AISEARCH/DeepFind.py +0 -250
- webscout/Provider/AISEARCH/ISou.py +0 -256
- webscout/Provider/AISEARCH/Perplexity.py +0 -359
- webscout/Provider/AISEARCH/__init__.py +0 -10
- webscout/Provider/AISEARCH/felo_search.py +0 -228
- webscout/Provider/AISEARCH/genspark_search.py +0 -208
- webscout/Provider/AISEARCH/hika_search.py +0 -194
- webscout/Provider/AISEARCH/iask_search.py +0 -436
- webscout/Provider/AISEARCH/monica_search.py +0 -246
- webscout/Provider/AISEARCH/scira_search.py +0 -324
- webscout/Provider/AISEARCH/webpilotai_search.py +0 -281
- webscout/Provider/Aitopia.py +0 -292
- webscout/Provider/AllenAI.py +0 -413
- webscout/Provider/Andi.py +0 -228
- webscout/Provider/Blackboxai.py +0 -229
- webscout/Provider/C4ai.py +0 -432
- webscout/Provider/ChatGPTClone.py +0 -226
- webscout/Provider/ChatGPTES.py +0 -237
- webscout/Provider/ChatGPTGratis.py +0 -194
- webscout/Provider/Chatify.py +0 -175
- webscout/Provider/Cloudflare.py +0 -273
- webscout/Provider/Cohere.py +0 -208
- webscout/Provider/DeepSeek.py +0 -196
- webscout/Provider/Deepinfra.py +0 -297
- webscout/Provider/ElectronHub.py +0 -709
- webscout/Provider/ExaAI.py +0 -261
- webscout/Provider/ExaChat.py +0 -342
- webscout/Provider/Free2GPT.py +0 -241
- webscout/Provider/GPTWeb.py +0 -193
- webscout/Provider/Gemini.py +0 -169
- webscout/Provider/GithubChat.py +0 -367
- webscout/Provider/Glider.py +0 -211
- webscout/Provider/Groq.py +0 -670
- webscout/Provider/HF_space/__init__.py +0 -0
- webscout/Provider/HF_space/qwen_qwen2.py +0 -206
- webscout/Provider/HeckAI.py +0 -233
- webscout/Provider/HuggingFaceChat.py +0 -462
- webscout/Provider/Hunyuan.py +0 -272
- webscout/Provider/Jadve.py +0 -266
- webscout/Provider/Koboldai.py +0 -381
- webscout/Provider/LambdaChat.py +0 -392
- webscout/Provider/Llama.py +0 -200
- webscout/Provider/Llama3.py +0 -204
- webscout/Provider/Marcus.py +0 -148
- webscout/Provider/Netwrck.py +0 -228
- webscout/Provider/OLLAMA.py +0 -396
- webscout/Provider/OPENAI/__init__.py +0 -25
- webscout/Provider/OPENAI/base.py +0 -46
- webscout/Provider/OPENAI/c4ai.py +0 -367
- webscout/Provider/OPENAI/chatgpt.py +0 -549
- webscout/Provider/OPENAI/chatgptclone.py +0 -460
- webscout/Provider/OPENAI/deepinfra.py +0 -272
- webscout/Provider/OPENAI/e2b.py +0 -1350
- webscout/Provider/OPENAI/exaai.py +0 -404
- webscout/Provider/OPENAI/exachat.py +0 -433
- webscout/Provider/OPENAI/freeaichat.py +0 -352
- webscout/Provider/OPENAI/glider.py +0 -316
- webscout/Provider/OPENAI/heckai.py +0 -337
- webscout/Provider/OPENAI/llmchatco.py +0 -327
- webscout/Provider/OPENAI/netwrck.py +0 -348
- webscout/Provider/OPENAI/opkfc.py +0 -488
- webscout/Provider/OPENAI/scirachat.py +0 -463
- webscout/Provider/OPENAI/sonus.py +0 -294
- webscout/Provider/OPENAI/standardinput.py +0 -425
- webscout/Provider/OPENAI/textpollinations.py +0 -285
- webscout/Provider/OPENAI/toolbaz.py +0 -405
- webscout/Provider/OPENAI/typegpt.py +0 -346
- webscout/Provider/OPENAI/uncovrAI.py +0 -455
- webscout/Provider/OPENAI/utils.py +0 -211
- webscout/Provider/OPENAI/venice.py +0 -413
- webscout/Provider/OPENAI/wisecat.py +0 -381
- webscout/Provider/OPENAI/writecream.py +0 -156
- webscout/Provider/OPENAI/x0gpt.py +0 -371
- webscout/Provider/OPENAI/yep.py +0 -327
- webscout/Provider/OpenGPT.py +0 -199
- webscout/Provider/Openai.py +0 -496
- webscout/Provider/PI.py +0 -344
- webscout/Provider/Perplexitylabs.py +0 -415
- webscout/Provider/Phind.py +0 -535
- webscout/Provider/PizzaGPT.py +0 -198
- webscout/Provider/QwenLM.py +0 -254
- webscout/Provider/Reka.py +0 -214
- webscout/Provider/StandardInput.py +0 -278
- webscout/Provider/TTI/AiForce/__init__.py +0 -22
- webscout/Provider/TTI/AiForce/async_aiforce.py +0 -224
- webscout/Provider/TTI/AiForce/sync_aiforce.py +0 -245
- webscout/Provider/TTI/FreeAIPlayground/__init__.py +0 -9
- webscout/Provider/TTI/FreeAIPlayground/async_freeaiplayground.py +0 -181
- webscout/Provider/TTI/FreeAIPlayground/sync_freeaiplayground.py +0 -180
- webscout/Provider/TTI/ImgSys/__init__.py +0 -23
- webscout/Provider/TTI/ImgSys/async_imgsys.py +0 -202
- webscout/Provider/TTI/ImgSys/sync_imgsys.py +0 -195
- webscout/Provider/TTI/MagicStudio/__init__.py +0 -2
- webscout/Provider/TTI/MagicStudio/async_magicstudio.py +0 -111
- webscout/Provider/TTI/MagicStudio/sync_magicstudio.py +0 -109
- webscout/Provider/TTI/Nexra/__init__.py +0 -22
- webscout/Provider/TTI/Nexra/async_nexra.py +0 -286
- webscout/Provider/TTI/Nexra/sync_nexra.py +0 -258
- webscout/Provider/TTI/PollinationsAI/__init__.py +0 -23
- webscout/Provider/TTI/PollinationsAI/async_pollinations.py +0 -311
- webscout/Provider/TTI/PollinationsAI/sync_pollinations.py +0 -265
- webscout/Provider/TTI/__init__.py +0 -12
- webscout/Provider/TTI/aiarta/__init__.py +0 -2
- webscout/Provider/TTI/aiarta/async_aiarta.py +0 -482
- webscout/Provider/TTI/aiarta/sync_aiarta.py +0 -440
- webscout/Provider/TTI/artbit/__init__.py +0 -22
- webscout/Provider/TTI/artbit/async_artbit.py +0 -155
- webscout/Provider/TTI/artbit/sync_artbit.py +0 -148
- webscout/Provider/TTI/fastflux/__init__.py +0 -22
- webscout/Provider/TTI/fastflux/async_fastflux.py +0 -261
- webscout/Provider/TTI/fastflux/sync_fastflux.py +0 -252
- webscout/Provider/TTI/huggingface/__init__.py +0 -22
- webscout/Provider/TTI/huggingface/async_huggingface.py +0 -199
- webscout/Provider/TTI/huggingface/sync_huggingface.py +0 -195
- webscout/Provider/TTI/piclumen/__init__.py +0 -23
- webscout/Provider/TTI/piclumen/async_piclumen.py +0 -268
- webscout/Provider/TTI/piclumen/sync_piclumen.py +0 -233
- webscout/Provider/TTI/pixelmuse/__init__.py +0 -4
- webscout/Provider/TTI/pixelmuse/async_pixelmuse.py +0 -249
- webscout/Provider/TTI/pixelmuse/sync_pixelmuse.py +0 -182
- webscout/Provider/TTI/talkai/__init__.py +0 -4
- webscout/Provider/TTI/talkai/async_talkai.py +0 -229
- webscout/Provider/TTI/talkai/sync_talkai.py +0 -207
- webscout/Provider/TTS/__init__.py +0 -7
- webscout/Provider/TTS/deepgram.py +0 -156
- webscout/Provider/TTS/elevenlabs.py +0 -111
- webscout/Provider/TTS/gesserit.py +0 -127
- webscout/Provider/TTS/murfai.py +0 -113
- webscout/Provider/TTS/parler.py +0 -111
- webscout/Provider/TTS/speechma.py +0 -180
- webscout/Provider/TTS/streamElements.py +0 -333
- webscout/Provider/TTS/utils.py +0 -280
- webscout/Provider/TeachAnything.py +0 -187
- webscout/Provider/TextPollinationsAI.py +0 -231
- webscout/Provider/TwoAI.py +0 -199
- webscout/Provider/Venice.py +0 -219
- webscout/Provider/VercelAI.py +0 -234
- webscout/Provider/WebSim.py +0 -228
- webscout/Provider/WiseCat.py +0 -196
- webscout/Provider/Writecream.py +0 -211
- webscout/Provider/WritingMate.py +0 -197
- webscout/Provider/Youchat.py +0 -330
- webscout/Provider/__init__.py +0 -198
- webscout/Provider/ai4chat.py +0 -202
- webscout/Provider/aimathgpt.py +0 -189
- webscout/Provider/akashgpt.py +0 -342
- webscout/Provider/askmyai.py +0 -158
- webscout/Provider/asksteve.py +0 -203
- webscout/Provider/bagoodex.py +0 -145
- webscout/Provider/cerebras.py +0 -242
- webscout/Provider/chatglm.py +0 -205
- webscout/Provider/cleeai.py +0 -213
- webscout/Provider/copilot.py +0 -428
- webscout/Provider/elmo.py +0 -234
- webscout/Provider/freeaichat.py +0 -271
- webscout/Provider/gaurish.py +0 -244
- webscout/Provider/geminiapi.py +0 -208
- webscout/Provider/geminiprorealtime.py +0 -160
- webscout/Provider/granite.py +0 -187
- webscout/Provider/hermes.py +0 -219
- webscout/Provider/julius.py +0 -223
- webscout/Provider/koala.py +0 -268
- webscout/Provider/labyrinth.py +0 -340
- webscout/Provider/learnfastai.py +0 -266
- webscout/Provider/lepton.py +0 -194
- webscout/Provider/llama3mitril.py +0 -180
- webscout/Provider/llamatutor.py +0 -192
- webscout/Provider/llmchat.py +0 -213
- webscout/Provider/llmchatco.py +0 -311
- webscout/Provider/meta.py +0 -794
- webscout/Provider/multichat.py +0 -325
- webscout/Provider/promptrefine.py +0 -193
- webscout/Provider/scira_chat.py +0 -277
- webscout/Provider/scnet.py +0 -187
- webscout/Provider/searchchat.py +0 -293
- webscout/Provider/sonus.py +0 -208
- webscout/Provider/talkai.py +0 -194
- webscout/Provider/toolbaz.py +0 -320
- webscout/Provider/turboseek.py +0 -219
- webscout/Provider/tutorai.py +0 -252
- webscout/Provider/typefully.py +0 -280
- webscout/Provider/typegpt.py +0 -232
- webscout/Provider/uncovr.py +0 -312
- webscout/Provider/x0gpt.py +0 -256
- webscout/Provider/yep.py +0 -376
- webscout/litagent/__init__.py +0 -29
- webscout/litagent/agent.py +0 -455
- webscout/litagent/constants.py +0 -60
- webscout/litprinter/__init__.py +0 -59
- webscout/scout/__init__.py +0 -8
- webscout/scout/core/__init__.py +0 -7
- webscout/scout/core/crawler.py +0 -140
- webscout/scout/core/scout.py +0 -568
- webscout/scout/core/search_result.py +0 -96
- webscout/scout/core/text_analyzer.py +0 -63
- webscout/scout/core/text_utils.py +0 -277
- webscout/scout/core/web_analyzer.py +0 -52
- webscout/scout/core.py +0 -881
- webscout/scout/element.py +0 -460
- webscout/scout/parsers/__init__.py +0 -69
- webscout/scout/parsers/html5lib_parser.py +0 -172
- webscout/scout/parsers/html_parser.py +0 -236
- webscout/scout/parsers/lxml_parser.py +0 -178
- webscout/scout/utils.py +0 -37
- webscout/swiftcli/__init__.py +0 -809
- webscout/zeroart/__init__.py +0 -55
- webscout/zeroart/base.py +0 -60
- webscout/zeroart/effects.py +0 -99
- webscout/zeroart/fonts.py +0 -816
- webscout-8.2.2.dist-info/RECORD +0 -309
- webscout-8.2.2.dist-info/entry_points.txt +0 -5
- webscout-8.2.2.dist-info/top_level.txt +0 -3
- webstoken/__init__.py +0 -30
- webstoken/classifier.py +0 -189
- webstoken/keywords.py +0 -216
- webstoken/language.py +0 -128
- webstoken/ner.py +0 -164
- webstoken/normalizer.py +0 -35
- webstoken/processor.py +0 -77
- webstoken/sentiment.py +0 -206
- webstoken/stemmer.py +0 -73
- webstoken/tagger.py +0 -60
- webstoken/tokenizer.py +0 -158
- {webscout-8.2.2.dist-info → webscout-8.2.7.dist-info/licenses}/LICENSE.md +0 -0
|
@@ -1,333 +0,0 @@
|
|
|
1
|
-
import time
|
|
2
|
-
import requests
|
|
3
|
-
import pathlib
|
|
4
|
-
import urllib.parse
|
|
5
|
-
import tempfile
|
|
6
|
-
from typing import Union
|
|
7
|
-
from io import BytesIO
|
|
8
|
-
from webscout import exceptions
|
|
9
|
-
from webscout.AIbase import TTSProvider
|
|
10
|
-
from webscout.litagent import LitAgent
|
|
11
|
-
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
12
|
-
from . import utils
|
|
13
|
-
|
|
14
|
-
class StreamElements(TTSProvider):
|
|
15
|
-
"""
|
|
16
|
-
Text-to-speech provider using the StreamElements API.
|
|
17
|
-
"""
|
|
18
|
-
|
|
19
|
-
# Request headers
|
|
20
|
-
headers: dict[str, str] = {
|
|
21
|
-
"User-Agent": LitAgent().random()
|
|
22
|
-
}
|
|
23
|
-
all_voices: list[str] = [
|
|
24
|
-
"Filiz",
|
|
25
|
-
"Astrid",
|
|
26
|
-
"Tatyana",
|
|
27
|
-
"Maxim",
|
|
28
|
-
"Carmen",
|
|
29
|
-
"Ines",
|
|
30
|
-
"Cristiano",
|
|
31
|
-
"Vitoria",
|
|
32
|
-
"Ricardo",
|
|
33
|
-
"Maja",
|
|
34
|
-
"Jan",
|
|
35
|
-
"Jacek",
|
|
36
|
-
"Ewa",
|
|
37
|
-
"Ruben",
|
|
38
|
-
"Lotte",
|
|
39
|
-
"Liv",
|
|
40
|
-
"Seoyeon",
|
|
41
|
-
"Takumi",
|
|
42
|
-
"Mizuki",
|
|
43
|
-
"Giorgio",
|
|
44
|
-
"Carla",
|
|
45
|
-
"Bianca",
|
|
46
|
-
"Karl",
|
|
47
|
-
"Dora",
|
|
48
|
-
"Mathieu",
|
|
49
|
-
"Celine",
|
|
50
|
-
"Chantal",
|
|
51
|
-
"Penelope",
|
|
52
|
-
"Miguel",
|
|
53
|
-
"Mia",
|
|
54
|
-
"Enrique",
|
|
55
|
-
"Conchita",
|
|
56
|
-
"Geraint",
|
|
57
|
-
"Salli",
|
|
58
|
-
"Matthew",
|
|
59
|
-
"Kimberly",
|
|
60
|
-
"Kendra",
|
|
61
|
-
"Justin",
|
|
62
|
-
"Joey",
|
|
63
|
-
"Joanna",
|
|
64
|
-
"Ivy",
|
|
65
|
-
"Raveena",
|
|
66
|
-
"Aditi",
|
|
67
|
-
"Emma",
|
|
68
|
-
"Brian",
|
|
69
|
-
"Amy",
|
|
70
|
-
"Russell",
|
|
71
|
-
"Nicole",
|
|
72
|
-
"Vicki",
|
|
73
|
-
"Marlene",
|
|
74
|
-
"Hans",
|
|
75
|
-
"Naja",
|
|
76
|
-
"Mads",
|
|
77
|
-
"Gwyneth",
|
|
78
|
-
"Zhiyu",
|
|
79
|
-
"es-ES-Standard-A",
|
|
80
|
-
"it-IT-Standard-A",
|
|
81
|
-
"it-IT-Wavenet-A",
|
|
82
|
-
"ja-JP-Standard-A",
|
|
83
|
-
"ja-JP-Wavenet-A",
|
|
84
|
-
"ko-KR-Standard-A",
|
|
85
|
-
"ko-KR-Wavenet-A",
|
|
86
|
-
"pt-BR-Standard-A",
|
|
87
|
-
"tr-TR-Standard-A",
|
|
88
|
-
"sv-SE-Standard-A",
|
|
89
|
-
"nl-NL-Standard-A",
|
|
90
|
-
"nl-NL-Wavenet-A",
|
|
91
|
-
"en-US-Wavenet-A",
|
|
92
|
-
"en-US-Wavenet-B",
|
|
93
|
-
"en-US-Wavenet-C",
|
|
94
|
-
"en-US-Wavenet-D",
|
|
95
|
-
"en-US-Wavenet-E",
|
|
96
|
-
"en-US-Wavenet-F",
|
|
97
|
-
"en-GB-Standard-A",
|
|
98
|
-
"en-GB-Standard-B",
|
|
99
|
-
"en-GB-Standard-C",
|
|
100
|
-
"en-GB-Standard-D",
|
|
101
|
-
"en-GB-Wavenet-A",
|
|
102
|
-
"en-GB-Wavenet-B",
|
|
103
|
-
"en-GB-Wavenet-C",
|
|
104
|
-
"en-GB-Wavenet-D",
|
|
105
|
-
"en-US-Standard-B",
|
|
106
|
-
"en-US-Standard-C",
|
|
107
|
-
"en-US-Standard-D",
|
|
108
|
-
"en-US-Standard-E",
|
|
109
|
-
"de-DE-Standard-A",
|
|
110
|
-
"de-DE-Standard-B",
|
|
111
|
-
"de-DE-Wavenet-A",
|
|
112
|
-
"de-DE-Wavenet-B",
|
|
113
|
-
"de-DE-Wavenet-C",
|
|
114
|
-
"de-DE-Wavenet-D",
|
|
115
|
-
"en-AU-Standard-A",
|
|
116
|
-
"en-AU-Standard-B",
|
|
117
|
-
"en-AU-Wavenet-A",
|
|
118
|
-
"en-AU-Wavenet-B",
|
|
119
|
-
"en-AU-Wavenet-C",
|
|
120
|
-
"en-AU-Wavenet-D",
|
|
121
|
-
"en-AU-Standard-C",
|
|
122
|
-
"en-AU-Standard-D",
|
|
123
|
-
"fr-CA-Standard-A",
|
|
124
|
-
"fr-CA-Standard-B",
|
|
125
|
-
"fr-CA-Standard-C",
|
|
126
|
-
"fr-CA-Standard-D",
|
|
127
|
-
"fr-FR-Standard-C",
|
|
128
|
-
"fr-FR-Standard-D",
|
|
129
|
-
"fr-FR-Wavenet-A",
|
|
130
|
-
"fr-FR-Wavenet-B",
|
|
131
|
-
"fr-FR-Wavenet-C",
|
|
132
|
-
"fr-FR-Wavenet-D",
|
|
133
|
-
"da-DK-Wavenet-A",
|
|
134
|
-
"pl-PL-Wavenet-A",
|
|
135
|
-
"pl-PL-Wavenet-B",
|
|
136
|
-
"pl-PL-Wavenet-C",
|
|
137
|
-
"pl-PL-Wavenet-D",
|
|
138
|
-
"pt-PT-Wavenet-A",
|
|
139
|
-
"pt-PT-Wavenet-B",
|
|
140
|
-
"pt-PT-Wavenet-C",
|
|
141
|
-
"pt-PT-Wavenet-D",
|
|
142
|
-
"ru-RU-Wavenet-A",
|
|
143
|
-
"ru-RU-Wavenet-B",
|
|
144
|
-
"ru-RU-Wavenet-C",
|
|
145
|
-
"ru-RU-Wavenet-D",
|
|
146
|
-
"sk-SK-Wavenet-A",
|
|
147
|
-
"tr-TR-Wavenet-A",
|
|
148
|
-
"tr-TR-Wavenet-B",
|
|
149
|
-
"tr-TR-Wavenet-C",
|
|
150
|
-
"tr-TR-Wavenet-D",
|
|
151
|
-
"tr-TR-Wavenet-E",
|
|
152
|
-
"uk-UA-Wavenet-A",
|
|
153
|
-
"ar-XA-Wavenet-A",
|
|
154
|
-
"ar-XA-Wavenet-B",
|
|
155
|
-
"ar-XA-Wavenet-C",
|
|
156
|
-
"cs-CZ-Wavenet-A",
|
|
157
|
-
"nl-NL-Wavenet-B",
|
|
158
|
-
"nl-NL-Wavenet-C",
|
|
159
|
-
"nl-NL-Wavenet-D",
|
|
160
|
-
"nl-NL-Wavenet-E",
|
|
161
|
-
"en-IN-Wavenet-A",
|
|
162
|
-
"en-IN-Wavenet-B",
|
|
163
|
-
"en-IN-Wavenet-C",
|
|
164
|
-
"fil-PH-Wavenet-A",
|
|
165
|
-
"fi-FI-Wavenet-A",
|
|
166
|
-
"el-GR-Wavenet-A",
|
|
167
|
-
"hi-IN-Wavenet-A",
|
|
168
|
-
"hi-IN-Wavenet-B",
|
|
169
|
-
"hi-IN-Wavenet-C",
|
|
170
|
-
"hu-HU-Wavenet-A",
|
|
171
|
-
"id-ID-Wavenet-A",
|
|
172
|
-
"id-ID-Wavenet-B",
|
|
173
|
-
"id-ID-Wavenet-C",
|
|
174
|
-
"it-IT-Wavenet-B",
|
|
175
|
-
"it-IT-Wavenet-C",
|
|
176
|
-
"it-IT-Wavenet-D",
|
|
177
|
-
"ja-JP-Wavenet-B",
|
|
178
|
-
"ja-JP-Wavenet-C",
|
|
179
|
-
"ja-JP-Wavenet-D",
|
|
180
|
-
"cmn-CN-Wavenet-A",
|
|
181
|
-
"cmn-CN-Wavenet-B",
|
|
182
|
-
"cmn-CN-Wavenet-C",
|
|
183
|
-
"cmn-CN-Wavenet-D",
|
|
184
|
-
"nb-no-Wavenet-E",
|
|
185
|
-
"nb-no-Wavenet-A",
|
|
186
|
-
"nb-no-Wavenet-B",
|
|
187
|
-
"nb-no-Wavenet-C",
|
|
188
|
-
"nb-no-Wavenet-D",
|
|
189
|
-
"vi-VN-Wavenet-A",
|
|
190
|
-
"vi-VN-Wavenet-B",
|
|
191
|
-
"vi-VN-Wavenet-C",
|
|
192
|
-
"vi-VN-Wavenet-D",
|
|
193
|
-
"sr-rs-Standard-A",
|
|
194
|
-
"lv-lv-Standard-A",
|
|
195
|
-
"is-is-Standard-A",
|
|
196
|
-
"bg-bg-Standard-A",
|
|
197
|
-
"af-ZA-Standard-A",
|
|
198
|
-
"Tracy",
|
|
199
|
-
"Danny",
|
|
200
|
-
"Huihui",
|
|
201
|
-
"Yaoyao",
|
|
202
|
-
"Kangkang",
|
|
203
|
-
"HanHan",
|
|
204
|
-
"Zhiwei",
|
|
205
|
-
"Asaf",
|
|
206
|
-
"An",
|
|
207
|
-
"Stefanos",
|
|
208
|
-
"Filip",
|
|
209
|
-
"Ivan",
|
|
210
|
-
"Heidi",
|
|
211
|
-
"Herena",
|
|
212
|
-
"Kalpana",
|
|
213
|
-
"Hemant",
|
|
214
|
-
"Matej",
|
|
215
|
-
"Andika",
|
|
216
|
-
"Rizwan",
|
|
217
|
-
"Lado",
|
|
218
|
-
"Valluvar",
|
|
219
|
-
"Linda",
|
|
220
|
-
"Heather",
|
|
221
|
-
"Sean",
|
|
222
|
-
"Michael",
|
|
223
|
-
"Karsten",
|
|
224
|
-
"Guillaume",
|
|
225
|
-
"Pattara",
|
|
226
|
-
"Jakub",
|
|
227
|
-
"Szabolcs",
|
|
228
|
-
"Hoda",
|
|
229
|
-
"Naayf",
|
|
230
|
-
]
|
|
231
|
-
|
|
232
|
-
def __init__(self, timeout: int = 20, proxies: dict = None):
|
|
233
|
-
"""Initializes the StreamElements TTS client."""
|
|
234
|
-
self.session = requests.Session()
|
|
235
|
-
self.session.headers.update(self.headers)
|
|
236
|
-
if proxies:
|
|
237
|
-
self.session.proxies.update(proxies)
|
|
238
|
-
self.timeout = timeout
|
|
239
|
-
self.temp_dir = tempfile.mkdtemp(prefix="webscout_tts_")
|
|
240
|
-
|
|
241
|
-
def tts(self, text: str, voice: str = "Mathieu", verbose: bool = True) -> str:
|
|
242
|
-
"""
|
|
243
|
-
Converts text to speech using the StreamElements API and saves it to a file.
|
|
244
|
-
|
|
245
|
-
Args:
|
|
246
|
-
text (str): The text to convert to speech
|
|
247
|
-
voice (str): The voice to use for TTS (default: "Mathieu")
|
|
248
|
-
verbose (bool): Whether to print progress messages (default: True)
|
|
249
|
-
|
|
250
|
-
Returns:
|
|
251
|
-
str: Path to the generated audio file
|
|
252
|
-
"""
|
|
253
|
-
assert (
|
|
254
|
-
voice in self.all_voices
|
|
255
|
-
), f"Voice '{voice}' not one of [{', '.join(self.all_voices)}]"
|
|
256
|
-
|
|
257
|
-
filename = pathlib.Path(tempfile.mktemp(suffix=".mp3", dir=self.temp_dir))
|
|
258
|
-
|
|
259
|
-
# Split text into sentences
|
|
260
|
-
sentences = utils.split_sentences(text)
|
|
261
|
-
|
|
262
|
-
# Function to request audio for each chunk
|
|
263
|
-
def generate_audio_for_chunk(part_text: str, part_number: int):
|
|
264
|
-
while True:
|
|
265
|
-
try:
|
|
266
|
-
# URL encode the text and voice
|
|
267
|
-
encoded_text = urllib.parse.quote(part_text)
|
|
268
|
-
encoded_voice = urllib.parse.quote(voice)
|
|
269
|
-
|
|
270
|
-
url = f"https://streamelements.com/tts/{encoded_voice}/{encoded_text}"
|
|
271
|
-
|
|
272
|
-
response = self.session.get(url, headers=self.headers, timeout=self.timeout)
|
|
273
|
-
response.raise_for_status()
|
|
274
|
-
|
|
275
|
-
# Check if the request was successful
|
|
276
|
-
if response.ok and response.status_code == 200:
|
|
277
|
-
if verbose:
|
|
278
|
-
print(f"[debug] Chunk {part_number} processed successfully")
|
|
279
|
-
return part_number, response.content
|
|
280
|
-
else:
|
|
281
|
-
if verbose:
|
|
282
|
-
print(f"[debug] No data received for chunk {part_number}. Retrying...")
|
|
283
|
-
except requests.RequestException as e:
|
|
284
|
-
if verbose:
|
|
285
|
-
print(f"[debug] Error for chunk {part_number}: {e}. Retrying...")
|
|
286
|
-
time.sleep(1)
|
|
287
|
-
try:
|
|
288
|
-
# Using ThreadPoolExecutor to handle requests concurrently
|
|
289
|
-
with ThreadPoolExecutor() as executor:
|
|
290
|
-
futures = {executor.submit(generate_audio_for_chunk, sentence.strip(), chunk_num): chunk_num
|
|
291
|
-
for chunk_num, sentence in enumerate(sentences, start=1)}
|
|
292
|
-
|
|
293
|
-
# Dictionary to store results with order preserved
|
|
294
|
-
audio_chunks = {}
|
|
295
|
-
|
|
296
|
-
for future in as_completed(futures):
|
|
297
|
-
chunk_num = futures[future]
|
|
298
|
-
try:
|
|
299
|
-
part_number, audio_data = future.result()
|
|
300
|
-
audio_chunks[part_number] = audio_data # Store the audio data in correct sequence
|
|
301
|
-
except Exception as e:
|
|
302
|
-
if verbose:
|
|
303
|
-
print(f"[debug] Failed to generate audio for chunk {chunk_num}: {e}")
|
|
304
|
-
|
|
305
|
-
# Combine audio chunks in the correct sequence
|
|
306
|
-
combined_audio = BytesIO()
|
|
307
|
-
for part_number in sorted(audio_chunks.keys()):
|
|
308
|
-
combined_audio.write(audio_chunks[part_number])
|
|
309
|
-
if verbose:
|
|
310
|
-
print(f"[debug] Added chunk {part_number} to the combined file.")
|
|
311
|
-
|
|
312
|
-
# Save the combined audio data to a single file
|
|
313
|
-
with open(filename, 'wb') as f:
|
|
314
|
-
f.write(combined_audio.getvalue())
|
|
315
|
-
if verbose:
|
|
316
|
-
print(f"[debug] Final Audio Saved as {filename}")
|
|
317
|
-
return filename.as_posix()
|
|
318
|
-
|
|
319
|
-
except requests.exceptions.RequestException as e:
|
|
320
|
-
if verbose:
|
|
321
|
-
print(f"[debug] Failed to perform the operation: {e}")
|
|
322
|
-
raise exceptions.FailedToGenerateResponseError(
|
|
323
|
-
f"Failed to perform the operation: {e}"
|
|
324
|
-
)
|
|
325
|
-
|
|
326
|
-
# Example usage
|
|
327
|
-
if __name__ == "__main__":
|
|
328
|
-
streamelements = StreamElements()
|
|
329
|
-
text = "This is a test of the StreamElements text-to-speech API. It supports multiple sentences and advanced logging."
|
|
330
|
-
|
|
331
|
-
print("[debug] Generating audio...")
|
|
332
|
-
audio_file = streamelements.tts(text, voice="Mathieu")
|
|
333
|
-
print(f"Audio saved to: {audio_file}")
|
webscout/Provider/TTS/utils.py
DELETED
|
@@ -1,280 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Text processing utilities for TTS providers.
|
|
3
|
-
"""
|
|
4
|
-
from typing import Union, List, Dict, Tuple, Set, Optional, Pattern
|
|
5
|
-
import re
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class SentenceTokenizer:
|
|
9
|
-
"""Advanced sentence tokenizer with support for complex cases and proper formatting."""
|
|
10
|
-
|
|
11
|
-
def __init__(self) -> None:
|
|
12
|
-
# Common abbreviations by category
|
|
13
|
-
self.TITLES: Set[str] = {
|
|
14
|
-
'mr', 'mrs', 'ms', 'dr', 'prof', 'rev', 'sr', 'jr', 'esq',
|
|
15
|
-
'hon', 'pres', 'gov', 'atty', 'supt', 'det', 'rev', 'col','maj', 'gen', 'capt', 'cmdr',
|
|
16
|
-
'lt', 'sgt', 'cpl', 'pvt'
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
self.ACADEMIC: Set[str] = {
|
|
20
|
-
'ph.d', 'phd', 'm.d', 'md', 'b.a', 'ba', 'm.a', 'ma', 'd.d.s', 'dds',
|
|
21
|
-
'm.b.a', 'mba', 'b.sc', 'bsc', 'm.sc', 'msc', 'llb', 'll.b', 'bl'
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
self.ORGANIZATIONS: Set[str] = {
|
|
25
|
-
'inc', 'ltd', 'co', 'corp', 'llc', 'llp', 'assn', 'bros', 'plc', 'cos',
|
|
26
|
-
'intl', 'dept', 'est', 'dist', 'mfg', 'div'
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
self.MONTHS: Set[str] = {
|
|
30
|
-
'jan', 'feb', 'mar', 'apr', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
self.UNITS: Set[str] = {
|
|
34
|
-
'oz', 'pt', 'qt', 'gal', 'ml', 'cc', 'km', 'cm', 'mm', 'ft', 'in',
|
|
35
|
-
'kg', 'lb', 'lbs', 'hz', 'khz', 'mhz', 'ghz', 'kb', 'mb', 'gb', 'tb'
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
self.TECHNOLOGY: Set[str] = {
|
|
39
|
-
'v', 'ver', 'app', 'sys', 'dir', 'exe', 'lib', 'api', 'sdk', 'url',
|
|
40
|
-
'cpu', 'gpu', 'ram', 'rom', 'hdd', 'ssd', 'lan', 'wan', 'sql', 'html'
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
self.MISC: Set[str] = {
|
|
44
|
-
'vs', 'etc', 'ie', 'eg', 'no', 'al', 'ca', 'cf', 'pp', 'est', 'st',
|
|
45
|
-
'approx', 'appt', 'apt', 'dept', 'depts', 'min', 'max', 'avg'
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
# Combine all abbreviations
|
|
49
|
-
self.all_abbreviations: Set[str] = (
|
|
50
|
-
self.TITLES | self.ACADEMIC | self.ORGANIZATIONS |
|
|
51
|
-
self.MONTHS | self.UNITS | self.TECHNOLOGY | self.MISC
|
|
52
|
-
)
|
|
53
|
-
|
|
54
|
-
# Special patterns
|
|
55
|
-
self.ELLIPSIS: str = r'\.{2,}|…'
|
|
56
|
-
self.URL_PATTERN: str = (
|
|
57
|
-
r'(?:https?:\/\/|www\.)[\w\-\.]+\.[a-zA-Z]{2,}(?:\/[^\s]*)?'
|
|
58
|
-
)
|
|
59
|
-
self.EMAIL_PATTERN: str = r'[\w\.-]+@[\w\.-]+\.\w+'
|
|
60
|
-
self.NUMBER_PATTERN: str = (
|
|
61
|
-
r'\d+(?:\.\d+)?(?:%|°|km|cm|mm|m|kg|g|lb|ft|in|mph|kmh|hz|mhz|ghz)?'
|
|
62
|
-
)
|
|
63
|
-
|
|
64
|
-
# Quote and bracket pairs
|
|
65
|
-
self.QUOTE_PAIRS: Dict[str, str] = {
|
|
66
|
-
'"': '"', "'": "'", '"': '"', "「": "」", "『": "』",
|
|
67
|
-
"«": "»", "‹": "›", "'": "'", "‚": "'"
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
self.BRACKETS: Dict[str, str] = {
|
|
71
|
-
'(': ')', '[': ']', '{': '}', '⟨': '⟩', '「': '」',
|
|
72
|
-
'『': '』', '【': '】', '〖': '〗', '「': '」'
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
# Compile regex patterns
|
|
76
|
-
self._compile_patterns()
|
|
77
|
-
|
|
78
|
-
def _compile_patterns(self) -> None:
|
|
79
|
-
"""Compile regex patterns for better performance."""
|
|
80
|
-
# Pattern for finding potential sentence boundaries
|
|
81
|
-
self.SENTENCE_END: Pattern = re.compile(
|
|
82
|
-
r'''
|
|
83
|
-
# Group for sentence endings
|
|
84
|
-
(?:
|
|
85
|
-
# Standard endings with optional quotes/brackets
|
|
86
|
-
(?<=[.!?])[\"\'\)\]\}»›」』\s]*
|
|
87
|
-
|
|
88
|
-
# Ellipsis
|
|
89
|
-
|(?:\.{2,}|…)
|
|
90
|
-
|
|
91
|
-
# Asian-style endings
|
|
92
|
-
|(?<=[。!?」』】\s])
|
|
93
|
-
)
|
|
94
|
-
|
|
95
|
-
# Must be followed by whitespace and capital letter or number
|
|
96
|
-
(?=\s+(?:[A-Z0-9]|["'({[\[「『《‹〈][A-Z]))
|
|
97
|
-
''',
|
|
98
|
-
re.VERBOSE
|
|
99
|
-
)
|
|
100
|
-
|
|
101
|
-
# Pattern for abbreviations
|
|
102
|
-
abbrev_pattern = '|'.join(re.escape(abbr) for abbr in self.all_abbreviations)
|
|
103
|
-
self.ABBREV_PATTERN: Pattern = re.compile(
|
|
104
|
-
fr'\b(?:{abbrev_pattern})\.?',
|
|
105
|
-
re.IGNORECASE
|
|
106
|
-
)
|
|
107
|
-
|
|
108
|
-
def _protect_special_cases(self, text: str) -> Tuple[str, Dict[str, str]]:
|
|
109
|
-
"""Protect URLs, emails, and other special cases from being split."""
|
|
110
|
-
protected = text
|
|
111
|
-
placeholders: Dict[str, str] = {}
|
|
112
|
-
counter = 0
|
|
113
|
-
|
|
114
|
-
# Protect URLs and emails
|
|
115
|
-
for pattern in [self.URL_PATTERN, self.EMAIL_PATTERN]:
|
|
116
|
-
for match in re.finditer(pattern, protected):
|
|
117
|
-
placeholder = f'__PROTECTED_{counter}__'
|
|
118
|
-
placeholders[placeholder] = match.group()
|
|
119
|
-
protected = protected.replace(match.group(), placeholder)
|
|
120
|
-
counter += 1
|
|
121
|
-
|
|
122
|
-
# Protect quoted content
|
|
123
|
-
stack = []
|
|
124
|
-
protected_chars = list(protected)
|
|
125
|
-
i = 0
|
|
126
|
-
while i < len(protected_chars):
|
|
127
|
-
char = protected_chars[i]
|
|
128
|
-
if char in self.QUOTE_PAIRS:
|
|
129
|
-
stack.append((char, i))
|
|
130
|
-
elif stack and char == self.QUOTE_PAIRS[stack[-1][0]]:
|
|
131
|
-
start_quote, start_idx = stack.pop()
|
|
132
|
-
content = ''.join(protected_chars[start_idx:i + 1])
|
|
133
|
-
placeholder = f'__PROTECTED_{counter}__'
|
|
134
|
-
placeholders[placeholder] = content
|
|
135
|
-
protected_chars[start_idx:i + 1] = list(placeholder)
|
|
136
|
-
counter += 1
|
|
137
|
-
i += 1
|
|
138
|
-
|
|
139
|
-
return ''.join(protected_chars), placeholders
|
|
140
|
-
|
|
141
|
-
def _restore_special_cases(self, text: str, placeholders: Dict[str, str]) -> str:
|
|
142
|
-
"""Restore protected content."""
|
|
143
|
-
restored = text
|
|
144
|
-
for placeholder, original in placeholders.items():
|
|
145
|
-
restored = restored.replace(placeholder, original)
|
|
146
|
-
return restored
|
|
147
|
-
|
|
148
|
-
def _handle_abbreviations(self, text: str) -> str:
|
|
149
|
-
"""Handle abbreviations to prevent incorrect sentence splitting."""
|
|
150
|
-
def replace_abbrev(match: re.Match) -> str:
|
|
151
|
-
abbr = match.group().lower().rstrip('.')
|
|
152
|
-
if abbr in self.all_abbreviations:
|
|
153
|
-
return match.group().replace('.', '__DOT__')
|
|
154
|
-
return match.group()
|
|
155
|
-
|
|
156
|
-
return self.ABBREV_PATTERN.sub(replace_abbrev, text)
|
|
157
|
-
|
|
158
|
-
def _normalize_whitespace(self, text: str) -> str:
|
|
159
|
-
"""Normalize whitespace while preserving paragraph breaks."""
|
|
160
|
-
# Replace multiple newlines with special marker
|
|
161
|
-
text = re.sub(r'\n\s*\n', ' __PARA__ ', text)
|
|
162
|
-
# Normalize remaining whitespace
|
|
163
|
-
text = re.sub(r'\s+', ' ', text)
|
|
164
|
-
return text.strip()
|
|
165
|
-
|
|
166
|
-
def _restore_formatting(self, sentences: List[str]) -> List[str]:
|
|
167
|
-
"""Restore original formatting and clean up sentences."""
|
|
168
|
-
restored = []
|
|
169
|
-
for sentence in sentences:
|
|
170
|
-
# Restore dots in abbreviations
|
|
171
|
-
sentence = sentence.replace('__DOT__', '.')
|
|
172
|
-
|
|
173
|
-
# Restore paragraph breaks
|
|
174
|
-
sentence = sentence.replace('__PARA__', '\n\n')
|
|
175
|
-
|
|
176
|
-
# Clean up whitespace
|
|
177
|
-
sentence = re.sub(r'\s+', ' ', sentence).strip()
|
|
178
|
-
|
|
179
|
-
# Capitalize first letter if it's lowercase and not an abbreviation
|
|
180
|
-
words = sentence.split()
|
|
181
|
-
if words and words[0].lower() not in self.all_abbreviations:
|
|
182
|
-
sentence = sentence[0].upper() + sentence[1:]
|
|
183
|
-
|
|
184
|
-
if sentence:
|
|
185
|
-
restored.append(sentence)
|
|
186
|
-
|
|
187
|
-
return restored
|
|
188
|
-
|
|
189
|
-
def tokenize(self, text: str) -> List[str]:
|
|
190
|
-
"""
|
|
191
|
-
Split text into sentences while handling complex cases.
|
|
192
|
-
|
|
193
|
-
Args:
|
|
194
|
-
text (str): Input text to split into sentences.
|
|
195
|
-
|
|
196
|
-
Returns:
|
|
197
|
-
List[str]: List of properly formatted sentences.
|
|
198
|
-
"""
|
|
199
|
-
if not text or not text.strip():
|
|
200
|
-
return []
|
|
201
|
-
|
|
202
|
-
# Step 1: Protect special cases
|
|
203
|
-
protected_text, placeholders = self._protect_special_cases(text)
|
|
204
|
-
|
|
205
|
-
# Step 2: Normalize whitespace
|
|
206
|
-
protected_text = self._normalize_whitespace(protected_text)
|
|
207
|
-
|
|
208
|
-
# Step 3: Handle abbreviations
|
|
209
|
-
protected_text = self._handle_abbreviations(protected_text)
|
|
210
|
-
|
|
211
|
-
# Step 4: Split into potential sentences
|
|
212
|
-
potential_sentences = self.SENTENCE_END.split(protected_text)
|
|
213
|
-
|
|
214
|
-
# Step 5: Process and restore formatting
|
|
215
|
-
sentences = self._restore_formatting(potential_sentences)
|
|
216
|
-
|
|
217
|
-
# Step 6: Restore special cases
|
|
218
|
-
sentences = [self._restore_special_cases(s, placeholders) for s in sentences]
|
|
219
|
-
|
|
220
|
-
# Step 7: Post-process sentences
|
|
221
|
-
final_sentences = []
|
|
222
|
-
current_sentence = []
|
|
223
|
-
|
|
224
|
-
for sentence in sentences:
|
|
225
|
-
# Skip empty sentences
|
|
226
|
-
if not sentence.strip():
|
|
227
|
-
continue
|
|
228
|
-
|
|
229
|
-
# Check if sentence might be continuation of previous
|
|
230
|
-
if current_sentence and sentence[0].islower():
|
|
231
|
-
current_sentence.append(sentence)
|
|
232
|
-
else:
|
|
233
|
-
if current_sentence:
|
|
234
|
-
final_sentences.append(' '.join(current_sentence))
|
|
235
|
-
current_sentence = [sentence]
|
|
236
|
-
|
|
237
|
-
# Add last sentence if exists
|
|
238
|
-
if current_sentence:
|
|
239
|
-
final_sentences.append(' '.join(current_sentence))
|
|
240
|
-
|
|
241
|
-
return final_sentences
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
def split_sentences(text: str) -> List[str]:
|
|
245
|
-
"""
|
|
246
|
-
Convenience function to split text into sentences using SentenceTokenizer.
|
|
247
|
-
|
|
248
|
-
Args:
|
|
249
|
-
text (str): Input text to split into sentences.
|
|
250
|
-
|
|
251
|
-
Returns:
|
|
252
|
-
List[str]: List of properly formatted sentences.
|
|
253
|
-
"""
|
|
254
|
-
tokenizer = SentenceTokenizer()
|
|
255
|
-
return tokenizer.tokenize(text)
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
if __name__ == "__main__":
|
|
259
|
-
# Test text with various challenging cases
|
|
260
|
-
test_text: str = """
|
|
261
|
-
Dr. Smith (Ph.D., M.D.) visited Washington D.C. on Jan. 20, 2024! He met with Prof. Johnson at 3:30 p.m.
|
|
262
|
-
They discussed A.I. and machine learning... "What about the U.S. market?" asked Dr. Smith.
|
|
263
|
-
The meeting ended at 5 p.m. Later, they went to Mr. Wilson's house (located at 123 Main St.) for dinner.
|
|
264
|
-
|
|
265
|
-
Visit our website at https://www.example.com or email us at test@example.com!
|
|
266
|
-
The temperature was 72.5°F (22.5°C). The company's Q3 2023 revenue was $12.5M USD.
|
|
267
|
-
|
|
268
|
-
「これは日本語の文章です。」This is a mixed-language text! How cool is that?
|
|
269
|
-
|
|
270
|
-
Some technical specs: CPU: 3.5GHz, RAM: 16GB, Storage: 2TB SSD.
|
|
271
|
-
Common abbreviations: etc., i.e., e.g., vs., cf., approx. 100 units.
|
|
272
|
-
"""
|
|
273
|
-
|
|
274
|
-
# Process and print each sentence
|
|
275
|
-
sentences: List[str] = split_sentences(test_text)
|
|
276
|
-
print("Detected sentences:")
|
|
277
|
-
print("-" * 80)
|
|
278
|
-
for i, sentence in enumerate(sentences, 1):
|
|
279
|
-
print(f"{i}. {sentence}")
|
|
280
|
-
print("-" * 80)
|