webscout 7.1__py3-none-any.whl → 7.3__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 +191 -191
- webscout/AIbase.py +122 -122
- webscout/AIutel.py +440 -440
- webscout/Bard.py +343 -161
- webscout/DWEBS.py +489 -492
- webscout/Extra/YTToolkit/YTdownloader.py +995 -995
- webscout/Extra/YTToolkit/__init__.py +2 -2
- webscout/Extra/YTToolkit/transcriber.py +476 -479
- webscout/Extra/YTToolkit/ytapi/channel.py +307 -307
- webscout/Extra/YTToolkit/ytapi/playlist.py +58 -58
- webscout/Extra/YTToolkit/ytapi/pool.py +7 -7
- webscout/Extra/YTToolkit/ytapi/utils.py +62 -62
- webscout/Extra/YTToolkit/ytapi/video.py +103 -103
- webscout/Extra/autocoder/__init__.py +9 -9
- webscout/Extra/autocoder/autocoder_utiles.py +199 -199
- webscout/Extra/autocoder/rawdog.py +5 -7
- webscout/Extra/autollama.py +230 -230
- webscout/Extra/gguf.py +3 -3
- webscout/Extra/weather.py +171 -171
- webscout/LLM.py +442 -442
- webscout/Litlogger/__init__.py +67 -681
- webscout/Litlogger/core/__init__.py +6 -0
- webscout/Litlogger/core/level.py +23 -0
- webscout/Litlogger/core/logger.py +166 -0
- webscout/Litlogger/handlers/__init__.py +12 -0
- webscout/Litlogger/handlers/console.py +33 -0
- webscout/Litlogger/handlers/file.py +143 -0
- webscout/Litlogger/handlers/network.py +173 -0
- webscout/Litlogger/styles/__init__.py +7 -0
- webscout/Litlogger/styles/colors.py +249 -0
- webscout/Litlogger/styles/formats.py +460 -0
- webscout/Litlogger/styles/text.py +87 -0
- webscout/Litlogger/utils/__init__.py +6 -0
- webscout/Litlogger/utils/detectors.py +154 -0
- webscout/Litlogger/utils/formatters.py +200 -0
- webscout/Provider/AISEARCH/DeepFind.py +250 -250
- webscout/Provider/AISEARCH/ISou.py +277 -0
- webscout/Provider/AISEARCH/__init__.py +2 -1
- webscout/Provider/Blackboxai.py +3 -3
- webscout/Provider/ChatGPTGratis.py +226 -0
- webscout/Provider/Cloudflare.py +3 -4
- webscout/Provider/DeepSeek.py +218 -0
- webscout/Provider/Deepinfra.py +40 -24
- webscout/Provider/Free2GPT.py +131 -124
- webscout/Provider/Gemini.py +100 -115
- webscout/Provider/Glider.py +3 -3
- webscout/Provider/Groq.py +5 -1
- webscout/Provider/Jadve.py +3 -3
- webscout/Provider/Marcus.py +191 -192
- webscout/Provider/Netwrck.py +3 -3
- webscout/Provider/PI.py +2 -2
- webscout/Provider/PizzaGPT.py +2 -3
- webscout/Provider/QwenLM.py +311 -0
- webscout/Provider/TTI/AiForce/__init__.py +22 -22
- webscout/Provider/TTI/AiForce/async_aiforce.py +257 -257
- webscout/Provider/TTI/AiForce/sync_aiforce.py +242 -242
- webscout/Provider/TTI/FreeAIPlayground/__init__.py +9 -0
- webscout/Provider/TTI/FreeAIPlayground/async_freeaiplayground.py +206 -0
- webscout/Provider/TTI/FreeAIPlayground/sync_freeaiplayground.py +192 -0
- webscout/Provider/TTI/Nexra/__init__.py +22 -22
- webscout/Provider/TTI/Nexra/async_nexra.py +286 -286
- webscout/Provider/TTI/Nexra/sync_nexra.py +258 -258
- webscout/Provider/TTI/PollinationsAI/__init__.py +23 -23
- webscout/Provider/TTI/PollinationsAI/async_pollinations.py +330 -330
- webscout/Provider/TTI/PollinationsAI/sync_pollinations.py +285 -285
- webscout/Provider/TTI/__init__.py +2 -1
- webscout/Provider/TTI/artbit/__init__.py +22 -22
- webscout/Provider/TTI/artbit/async_artbit.py +184 -184
- webscout/Provider/TTI/artbit/sync_artbit.py +176 -176
- webscout/Provider/TTI/blackbox/__init__.py +4 -4
- webscout/Provider/TTI/blackbox/async_blackbox.py +212 -212
- webscout/Provider/TTI/blackbox/sync_blackbox.py +199 -199
- webscout/Provider/TTI/deepinfra/__init__.py +4 -4
- webscout/Provider/TTI/deepinfra/async_deepinfra.py +227 -227
- webscout/Provider/TTI/deepinfra/sync_deepinfra.py +199 -199
- webscout/Provider/TTI/huggingface/__init__.py +22 -22
- webscout/Provider/TTI/huggingface/async_huggingface.py +199 -199
- webscout/Provider/TTI/huggingface/sync_huggingface.py +195 -195
- webscout/Provider/TTI/imgninza/__init__.py +4 -4
- webscout/Provider/TTI/imgninza/async_ninza.py +214 -214
- webscout/Provider/TTI/imgninza/sync_ninza.py +209 -209
- webscout/Provider/TTI/talkai/__init__.py +4 -4
- webscout/Provider/TTI/talkai/async_talkai.py +229 -229
- webscout/Provider/TTI/talkai/sync_talkai.py +207 -207
- webscout/Provider/TTS/deepgram.py +182 -182
- webscout/Provider/TTS/elevenlabs.py +136 -136
- webscout/Provider/TTS/gesserit.py +150 -150
- webscout/Provider/TTS/murfai.py +138 -138
- webscout/Provider/TTS/parler.py +133 -134
- webscout/Provider/TTS/streamElements.py +360 -360
- webscout/Provider/TTS/utils.py +280 -280
- webscout/Provider/TTS/voicepod.py +116 -116
- webscout/Provider/TextPollinationsAI.py +28 -8
- webscout/Provider/WiseCat.py +193 -0
- webscout/Provider/__init__.py +146 -134
- webscout/Provider/cerebras.py +242 -227
- webscout/Provider/chatglm.py +204 -204
- webscout/Provider/dgaf.py +2 -3
- webscout/Provider/freeaichat.py +221 -0
- webscout/Provider/gaurish.py +2 -3
- webscout/Provider/geminiapi.py +208 -208
- webscout/Provider/granite.py +223 -0
- webscout/Provider/hermes.py +218 -218
- webscout/Provider/llama3mitril.py +179 -179
- webscout/Provider/llamatutor.py +3 -3
- webscout/Provider/llmchat.py +2 -3
- webscout/Provider/meta.py +794 -794
- webscout/Provider/multichat.py +331 -331
- webscout/Provider/typegpt.py +359 -359
- webscout/Provider/yep.py +3 -3
- webscout/__init__.py +1 -0
- webscout/__main__.py +5 -5
- webscout/cli.py +319 -319
- webscout/conversation.py +241 -242
- webscout/exceptions.py +328 -328
- webscout/litagent/__init__.py +28 -28
- webscout/litagent/agent.py +2 -3
- webscout/litprinter/__init__.py +0 -58
- webscout/scout/__init__.py +8 -8
- webscout/scout/core.py +884 -884
- webscout/scout/element.py +459 -459
- webscout/scout/parsers/__init__.py +69 -69
- webscout/scout/parsers/html5lib_parser.py +172 -172
- webscout/scout/parsers/html_parser.py +236 -236
- webscout/scout/parsers/lxml_parser.py +178 -178
- webscout/scout/utils.py +38 -38
- webscout/swiftcli/__init__.py +811 -811
- webscout/update_checker.py +2 -12
- webscout/version.py +1 -1
- webscout/webscout_search.py +87 -6
- webscout/webscout_search_async.py +58 -1
- webscout/yep_search.py +297 -0
- webscout/zeroart/__init__.py +54 -54
- webscout/zeroart/base.py +60 -60
- webscout/zeroart/effects.py +99 -99
- webscout/zeroart/fonts.py +816 -816
- {webscout-7.1.dist-info → webscout-7.3.dist-info}/METADATA +62 -22
- webscout-7.3.dist-info/RECORD +223 -0
- {webscout-7.1.dist-info → webscout-7.3.dist-info}/WHEEL +1 -1
- webstoken/__init__.py +30 -30
- webstoken/classifier.py +189 -189
- webstoken/keywords.py +216 -216
- webstoken/language.py +128 -128
- webstoken/ner.py +164 -164
- webstoken/normalizer.py +35 -35
- webstoken/processor.py +77 -77
- webstoken/sentiment.py +206 -206
- webstoken/stemmer.py +73 -73
- webstoken/tagger.py +60 -60
- webstoken/tokenizer.py +158 -158
- webscout-7.1.dist-info/RECORD +0 -198
- {webscout-7.1.dist-info → webscout-7.3.dist-info}/LICENSE.md +0 -0
- {webscout-7.1.dist-info → webscout-7.3.dist-info}/entry_points.txt +0 -0
- {webscout-7.1.dist-info → webscout-7.3.dist-info}/top_level.txt +0 -0
webscout/AIutel.py
CHANGED
|
@@ -1,441 +1,441 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import json
|
|
3
|
-
import platform
|
|
4
|
-
import subprocess
|
|
5
|
-
import logging
|
|
6
|
-
import threading
|
|
7
|
-
import time
|
|
8
|
-
import datetime
|
|
9
|
-
import re
|
|
10
|
-
import sys
|
|
11
|
-
from rich.markdown import Markdown
|
|
12
|
-
from rich.console import Console
|
|
13
|
-
from typing import List, Tuple, Union
|
|
14
|
-
from typing import NoReturn
|
|
15
|
-
import requests
|
|
16
|
-
from pathlib import Path
|
|
17
|
-
from playsound import playsound
|
|
18
|
-
from time import sleep as wait
|
|
19
|
-
import pathlib
|
|
20
|
-
import urllib.parse
|
|
21
|
-
|
|
22
|
-
default_path = os.path.join(os.path.expanduser("~"), ".cache", "AIWEBS", "webscout")
|
|
23
|
-
|
|
24
|
-
def sanitize_stream(
|
|
25
|
-
chunk: str, intro_value: str = "data:", to_json: bool = True
|
|
26
|
-
) -> str | dict:
|
|
27
|
-
"""Remove streaming flags
|
|
28
|
-
|
|
29
|
-
Args:
|
|
30
|
-
chunk (str): Streamig chunk.
|
|
31
|
-
intro_value (str, optional): streaming flag. Defaults to "data:".
|
|
32
|
-
to_json (bool, optional). Return chunk as dictionary. Defaults to True.
|
|
33
|
-
|
|
34
|
-
Returns:
|
|
35
|
-
str: Sanitized streaming value.
|
|
36
|
-
"""
|
|
37
|
-
|
|
38
|
-
if chunk.startswith(intro_value):
|
|
39
|
-
chunk = chunk[len(intro_value) :]
|
|
40
|
-
|
|
41
|
-
return json.loads(chunk) if to_json else chunk
|
|
42
|
-
def run_system_command(
|
|
43
|
-
command: str,
|
|
44
|
-
exit_on_error: bool = True,
|
|
45
|
-
stdout_error: bool = True,
|
|
46
|
-
help: str = None,
|
|
47
|
-
):
|
|
48
|
-
"""Run commands against system
|
|
49
|
-
Args:
|
|
50
|
-
command (str): shell command
|
|
51
|
-
exit_on_error (bool, optional): Exit on error. Defaults to True.
|
|
52
|
-
stdout_error (bool, optional): Print out the error. Defaults to True
|
|
53
|
-
help (str, optional): Help info incase of exception. Defaults to None.
|
|
54
|
-
Returns:
|
|
55
|
-
tuple : (is_successfull, object[Exception|Subprocess.run])
|
|
56
|
-
"""
|
|
57
|
-
try:
|
|
58
|
-
# Run the command and capture the output
|
|
59
|
-
result = subprocess.run(
|
|
60
|
-
command,
|
|
61
|
-
shell=True,
|
|
62
|
-
check=True,
|
|
63
|
-
text=True,
|
|
64
|
-
stdout=subprocess.PIPE,
|
|
65
|
-
stderr=subprocess.PIPE,
|
|
66
|
-
)
|
|
67
|
-
return (True, result)
|
|
68
|
-
except subprocess.CalledProcessError as e:
|
|
69
|
-
if exit_on_error:
|
|
70
|
-
raise Exception(f"Command failed with exit code {e.returncode}") from e
|
|
71
|
-
else:
|
|
72
|
-
return (False, e)
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
from .conversation import Conversation
|
|
76
|
-
|
|
77
|
-
from .optimizers import Optimizers
|
|
78
|
-
|
|
79
|
-
from .Extra.autocoder import AutoCoder
|
|
80
|
-
|
|
81
|
-
from .prompt_manager import AwesomePrompts
|
|
82
|
-
|
|
83
|
-
class Updates:
|
|
84
|
-
"""Webscout latest release info"""
|
|
85
|
-
|
|
86
|
-
url = "https://api.github.com/repos/OE-LUCIFER/Webscout/releases/latest"
|
|
87
|
-
|
|
88
|
-
@property
|
|
89
|
-
def latest_version(self):
|
|
90
|
-
return self.latest(version=True)
|
|
91
|
-
|
|
92
|
-
def executable(self, system: str = platform.system()) -> str:
|
|
93
|
-
"""Url pointing to executable for particular system
|
|
94
|
-
|
|
95
|
-
Args:
|
|
96
|
-
system (str, optional): system name. Defaults to platform.system().
|
|
97
|
-
|
|
98
|
-
Returns:
|
|
99
|
-
str: url
|
|
100
|
-
"""
|
|
101
|
-
for entry in self.latest()["assets"]:
|
|
102
|
-
if entry.get("target") == system:
|
|
103
|
-
return entry.get("url")
|
|
104
|
-
|
|
105
|
-
def latest(self, whole: bool = False, version: bool = False) -> dict:
|
|
106
|
-
"""Check Webscout latest version info
|
|
107
|
-
|
|
108
|
-
Args:
|
|
109
|
-
whole (bool, optional): Return whole json response. Defaults to False.
|
|
110
|
-
version (bool, optional): return version only. Defaults to False.
|
|
111
|
-
|
|
112
|
-
Returns:
|
|
113
|
-
bool|dict: version str or whole dict info
|
|
114
|
-
"""
|
|
115
|
-
import requests
|
|
116
|
-
|
|
117
|
-
data = requests.get(self.url).json()
|
|
118
|
-
if whole:
|
|
119
|
-
return data
|
|
120
|
-
|
|
121
|
-
elif version:
|
|
122
|
-
return data.get("tag_name")
|
|
123
|
-
|
|
124
|
-
else:
|
|
125
|
-
sorted = dict(
|
|
126
|
-
tag_name=data.get("tag_name"),
|
|
127
|
-
tarball_url=data.get("tarball_url"),
|
|
128
|
-
zipball_url=data.get("zipball_url"),
|
|
129
|
-
html_url=data.get("html_url"),
|
|
130
|
-
body=data.get("body"),
|
|
131
|
-
)
|
|
132
|
-
whole_assets = []
|
|
133
|
-
for entry in data.get("assets"):
|
|
134
|
-
url = entry.get("browser_download_url")
|
|
135
|
-
assets = dict(url=url, size=entry.get("size"))
|
|
136
|
-
if ".deb" in url:
|
|
137
|
-
assets["target"] = "Debian"
|
|
138
|
-
elif ".exe" in url:
|
|
139
|
-
assets["target"] = "Windows"
|
|
140
|
-
elif "macos" in url:
|
|
141
|
-
assets["target"] = "Mac"
|
|
142
|
-
elif "linux" in url:
|
|
143
|
-
assets["target"] = "Linux"
|
|
144
|
-
|
|
145
|
-
whole_assets.append(assets)
|
|
146
|
-
sorted["assets"] = whole_assets
|
|
147
|
-
|
|
148
|
-
return sorted
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
class Audio:
|
|
152
|
-
# Request headers
|
|
153
|
-
headers: dict[str, str] = {
|
|
154
|
-
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36"
|
|
155
|
-
}
|
|
156
|
-
cache_dir = pathlib.Path("./audio_cache")
|
|
157
|
-
all_voices: list[str] = [
|
|
158
|
-
"Filiz",
|
|
159
|
-
"Astrid",
|
|
160
|
-
"Tatyana",
|
|
161
|
-
"Maxim",
|
|
162
|
-
"Carmen",
|
|
163
|
-
"Ines",
|
|
164
|
-
"Cristiano",
|
|
165
|
-
"Vitoria",
|
|
166
|
-
"Ricardo",
|
|
167
|
-
"Maja",
|
|
168
|
-
"Jan",
|
|
169
|
-
"Jacek",
|
|
170
|
-
"Ewa",
|
|
171
|
-
"Ruben",
|
|
172
|
-
"Lotte",
|
|
173
|
-
"Liv",
|
|
174
|
-
"Seoyeon",
|
|
175
|
-
"Takumi",
|
|
176
|
-
"Mizuki",
|
|
177
|
-
"Giorgio",
|
|
178
|
-
"Carla",
|
|
179
|
-
"Bianca",
|
|
180
|
-
"Karl",
|
|
181
|
-
"Dora",
|
|
182
|
-
"Mathieu",
|
|
183
|
-
"Celine",
|
|
184
|
-
"Chantal",
|
|
185
|
-
"Penelope",
|
|
186
|
-
"Miguel",
|
|
187
|
-
"Mia",
|
|
188
|
-
"Enrique",
|
|
189
|
-
"Conchita",
|
|
190
|
-
"Geraint",
|
|
191
|
-
"Salli",
|
|
192
|
-
"Matthew",
|
|
193
|
-
"Kimberly",
|
|
194
|
-
"Kendra",
|
|
195
|
-
"Justin",
|
|
196
|
-
"Joey",
|
|
197
|
-
"Joanna",
|
|
198
|
-
"Ivy",
|
|
199
|
-
"Raveena",
|
|
200
|
-
"Aditi",
|
|
201
|
-
"Emma",
|
|
202
|
-
"Brian",
|
|
203
|
-
"Amy",
|
|
204
|
-
"Russell",
|
|
205
|
-
"Nicole",
|
|
206
|
-
"Vicki",
|
|
207
|
-
"Marlene",
|
|
208
|
-
"Hans",
|
|
209
|
-
"Naja",
|
|
210
|
-
"Mads",
|
|
211
|
-
"Gwyneth",
|
|
212
|
-
"Zhiyu",
|
|
213
|
-
"es-ES-Standard-A",
|
|
214
|
-
"it-IT-Standard-A",
|
|
215
|
-
"it-IT-Wavenet-A",
|
|
216
|
-
"ja-JP-Standard-A",
|
|
217
|
-
"ja-JP-Wavenet-A",
|
|
218
|
-
"ko-KR-Standard-A",
|
|
219
|
-
"ko-KR-Wavenet-A",
|
|
220
|
-
"pt-BR-Standard-A",
|
|
221
|
-
"tr-TR-Standard-A",
|
|
222
|
-
"sv-SE-Standard-A",
|
|
223
|
-
"nl-NL-Standard-A",
|
|
224
|
-
"nl-NL-Wavenet-A",
|
|
225
|
-
"en-US-Wavenet-A",
|
|
226
|
-
"en-US-Wavenet-B",
|
|
227
|
-
"en-US-Wavenet-C",
|
|
228
|
-
"en-US-Wavenet-D",
|
|
229
|
-
"en-US-Wavenet-E",
|
|
230
|
-
"en-US-Wavenet-F",
|
|
231
|
-
"en-GB-Standard-A",
|
|
232
|
-
"en-GB-Standard-B",
|
|
233
|
-
"en-GB-Standard-C",
|
|
234
|
-
"en-GB-Standard-D",
|
|
235
|
-
"en-GB-Wavenet-A",
|
|
236
|
-
"en-GB-Wavenet-B",
|
|
237
|
-
"en-GB-Wavenet-C",
|
|
238
|
-
"en-GB-Wavenet-D",
|
|
239
|
-
"en-US-Standard-B",
|
|
240
|
-
"en-US-Standard-C",
|
|
241
|
-
"en-US-Standard-D",
|
|
242
|
-
"en-US-Standard-E",
|
|
243
|
-
"de-DE-Standard-A",
|
|
244
|
-
"de-DE-Standard-B",
|
|
245
|
-
"de-DE-Wavenet-A",
|
|
246
|
-
"de-DE-Wavenet-B",
|
|
247
|
-
"de-DE-Wavenet-C",
|
|
248
|
-
"de-DE-Wavenet-D",
|
|
249
|
-
"en-AU-Standard-A",
|
|
250
|
-
"en-AU-Standard-B",
|
|
251
|
-
"en-AU-Wavenet-A",
|
|
252
|
-
"en-AU-Wavenet-B",
|
|
253
|
-
"en-AU-Wavenet-C",
|
|
254
|
-
"en-AU-Wavenet-D",
|
|
255
|
-
"en-AU-Standard-C",
|
|
256
|
-
"en-AU-Standard-D",
|
|
257
|
-
"fr-CA-Standard-A",
|
|
258
|
-
"fr-CA-Standard-B",
|
|
259
|
-
"fr-CA-Standard-C",
|
|
260
|
-
"fr-CA-Standard-D",
|
|
261
|
-
"fr-FR-Standard-C",
|
|
262
|
-
"fr-FR-Standard-D",
|
|
263
|
-
"fr-FR-Wavenet-A",
|
|
264
|
-
"fr-FR-Wavenet-B",
|
|
265
|
-
"fr-FR-Wavenet-C",
|
|
266
|
-
"fr-FR-Wavenet-D",
|
|
267
|
-
"da-DK-Wavenet-A",
|
|
268
|
-
"pl-PL-Wavenet-A",
|
|
269
|
-
"pl-PL-Wavenet-B",
|
|
270
|
-
"pl-PL-Wavenet-C",
|
|
271
|
-
"pl-PL-Wavenet-D",
|
|
272
|
-
"pt-PT-Wavenet-A",
|
|
273
|
-
"pt-PT-Wavenet-B",
|
|
274
|
-
"pt-PT-Wavenet-C",
|
|
275
|
-
"pt-PT-Wavenet-D",
|
|
276
|
-
"ru-RU-Wavenet-A",
|
|
277
|
-
"ru-RU-Wavenet-B",
|
|
278
|
-
"ru-RU-Wavenet-C",
|
|
279
|
-
"ru-RU-Wavenet-D",
|
|
280
|
-
"sk-SK-Wavenet-A",
|
|
281
|
-
"tr-TR-Wavenet-A",
|
|
282
|
-
"tr-TR-Wavenet-B",
|
|
283
|
-
"tr-TR-Wavenet-C",
|
|
284
|
-
"tr-TR-Wavenet-D",
|
|
285
|
-
"tr-TR-Wavenet-E",
|
|
286
|
-
"uk-UA-Wavenet-A",
|
|
287
|
-
"ar-XA-Wavenet-A",
|
|
288
|
-
"ar-XA-Wavenet-B",
|
|
289
|
-
"ar-XA-Wavenet-C",
|
|
290
|
-
"cs-CZ-Wavenet-A",
|
|
291
|
-
"nl-NL-Wavenet-B",
|
|
292
|
-
"nl-NL-Wavenet-C",
|
|
293
|
-
"nl-NL-Wavenet-D",
|
|
294
|
-
"nl-NL-Wavenet-E",
|
|
295
|
-
"en-IN-Wavenet-A",
|
|
296
|
-
"en-IN-Wavenet-B",
|
|
297
|
-
"en-IN-Wavenet-C",
|
|
298
|
-
"fil-PH-Wavenet-A",
|
|
299
|
-
"fi-FI-Wavenet-A",
|
|
300
|
-
"el-GR-Wavenet-A",
|
|
301
|
-
"hi-IN-Wavenet-A",
|
|
302
|
-
"hi-IN-Wavenet-B",
|
|
303
|
-
"hi-IN-Wavenet-C",
|
|
304
|
-
"hu-HU-Wavenet-A",
|
|
305
|
-
"id-ID-Wavenet-A",
|
|
306
|
-
"id-ID-Wavenet-B",
|
|
307
|
-
"id-ID-Wavenet-C",
|
|
308
|
-
"it-IT-Wavenet-B",
|
|
309
|
-
"it-IT-Wavenet-C",
|
|
310
|
-
"it-IT-Wavenet-D",
|
|
311
|
-
"ja-JP-Wavenet-B",
|
|
312
|
-
"ja-JP-Wavenet-C",
|
|
313
|
-
"ja-JP-Wavenet-D",
|
|
314
|
-
"cmn-CN-Wavenet-A",
|
|
315
|
-
"cmn-CN-Wavenet-B",
|
|
316
|
-
"cmn-CN-Wavenet-C",
|
|
317
|
-
"cmn-CN-Wavenet-D",
|
|
318
|
-
"nb-no-Wavenet-E",
|
|
319
|
-
"nb-no-Wavenet-A",
|
|
320
|
-
"nb-no-Wavenet-B",
|
|
321
|
-
"nb-no-Wavenet-C",
|
|
322
|
-
"nb-no-Wavenet-D",
|
|
323
|
-
"vi-VN-Wavenet-A",
|
|
324
|
-
"vi-VN-Wavenet-B",
|
|
325
|
-
"vi-VN-Wavenet-C",
|
|
326
|
-
"vi-VN-Wavenet-D",
|
|
327
|
-
"sr-rs-Standard-A",
|
|
328
|
-
"lv-lv-Standard-A",
|
|
329
|
-
"is-is-Standard-A",
|
|
330
|
-
"bg-bg-Standard-A",
|
|
331
|
-
"af-ZA-Standard-A",
|
|
332
|
-
"Tracy",
|
|
333
|
-
"Danny",
|
|
334
|
-
"Huihui",
|
|
335
|
-
"Yaoyao",
|
|
336
|
-
"Kangkang",
|
|
337
|
-
"HanHan",
|
|
338
|
-
"Zhiwei",
|
|
339
|
-
"Asaf",
|
|
340
|
-
"An",
|
|
341
|
-
"Stefanos",
|
|
342
|
-
"Filip",
|
|
343
|
-
"Ivan",
|
|
344
|
-
"Heidi",
|
|
345
|
-
"Herena",
|
|
346
|
-
"Kalpana",
|
|
347
|
-
"Hemant",
|
|
348
|
-
"Matej",
|
|
349
|
-
"Andika",
|
|
350
|
-
"Rizwan",
|
|
351
|
-
"Lado",
|
|
352
|
-
"Valluvar",
|
|
353
|
-
"Linda",
|
|
354
|
-
"Heather",
|
|
355
|
-
"Sean",
|
|
356
|
-
"Michael",
|
|
357
|
-
"Karsten",
|
|
358
|
-
"Guillaume",
|
|
359
|
-
"Pattara",
|
|
360
|
-
"Jakub",
|
|
361
|
-
"Szabolcs",
|
|
362
|
-
"Hoda",
|
|
363
|
-
"Naayf",
|
|
364
|
-
]
|
|
365
|
-
|
|
366
|
-
@classmethod
|
|
367
|
-
def text_to_audio(
|
|
368
|
-
cls,
|
|
369
|
-
message: str,
|
|
370
|
-
voice: str = "Brian",
|
|
371
|
-
save_to: Union[Path, str] = None,
|
|
372
|
-
auto: bool = True,
|
|
373
|
-
) -> Union[str, bytes]:
|
|
374
|
-
"""
|
|
375
|
-
Text to speech using StreamElements API
|
|
376
|
-
|
|
377
|
-
Parameters:
|
|
378
|
-
message (str): The text to convert to speech
|
|
379
|
-
voice (str, optional): The voice to use for speech synthesis. Defaults to "Brian".
|
|
380
|
-
save_to (bool, optional): Path to save the audio file. Defaults to None.
|
|
381
|
-
auto (bool, optional): Generate filename based on `message` and save to `cls.cache_dir`. Defaults to False.
|
|
382
|
-
|
|
383
|
-
Returns:
|
|
384
|
-
result (Union[str, bytes]): Path to saved contents or audio content.
|
|
385
|
-
"""
|
|
386
|
-
assert (
|
|
387
|
-
voice in cls.all_voices
|
|
388
|
-
), f"Voice '{voice}' not one of [{', '.join(cls.all_voices)}]"
|
|
389
|
-
# Base URL for provider API
|
|
390
|
-
url: str = (
|
|
391
|
-
f"https://api.streamelements.com/kappa/v2/speech?voice={voice}&text={{{urllib.parse.quote(message)}}}"
|
|
392
|
-
)
|
|
393
|
-
resp = requests.get(url=url, headers=cls.headers, stream=True)
|
|
394
|
-
if not resp.ok:
|
|
395
|
-
raise Exception(
|
|
396
|
-
f"Failed to perform the operation - ({resp.status_code}, {resp.reason}) - {resp.text}"
|
|
397
|
-
)
|
|
398
|
-
|
|
399
|
-
def sanitize_filename(path):
|
|
400
|
-
trash = [
|
|
401
|
-
"\\",
|
|
402
|
-
"/",
|
|
403
|
-
":",
|
|
404
|
-
"*",
|
|
405
|
-
"?",
|
|
406
|
-
'"',
|
|
407
|
-
"<",
|
|
408
|
-
"|",
|
|
409
|
-
">",
|
|
410
|
-
]
|
|
411
|
-
for val in trash:
|
|
412
|
-
path = path.replace(val, "")
|
|
413
|
-
return path.strip()
|
|
414
|
-
|
|
415
|
-
if auto:
|
|
416
|
-
filename: str = message + "..." if len(message) <= 40 else message[:40]
|
|
417
|
-
save_to = cls.cache_dir / sanitize_filename(filename)
|
|
418
|
-
save_to = save_to.as_posix()
|
|
419
|
-
|
|
420
|
-
# Ensure cache_dir exists
|
|
421
|
-
cls.cache_dir.mkdir(parents=True, exist_ok=True)
|
|
422
|
-
|
|
423
|
-
if save_to:
|
|
424
|
-
if not save_to.endswith("mp3"):
|
|
425
|
-
save_to += ".mp3"
|
|
426
|
-
|
|
427
|
-
with open(save_to, "wb") as fh:
|
|
428
|
-
for chunk in resp.iter_content(chunk_size=512):
|
|
429
|
-
fh.write(chunk)
|
|
430
|
-
else:
|
|
431
|
-
return resp.content
|
|
432
|
-
return save_to
|
|
433
|
-
|
|
434
|
-
@staticmethod
|
|
435
|
-
def play(path_to_audio_file: Union[Path, str]) -> NoReturn:
|
|
436
|
-
"""Play audio (.mp3) using playsound.
|
|
437
|
-
"""
|
|
438
|
-
if not Path(path_to_audio_file).is_file():
|
|
439
|
-
raise FileNotFoundError(f"File does not exist - '{path_to_audio_file}'")
|
|
440
|
-
playsound(path_to_audio_file)
|
|
1
|
+
import os
|
|
2
|
+
import json
|
|
3
|
+
import platform
|
|
4
|
+
import subprocess
|
|
5
|
+
import logging
|
|
6
|
+
import threading
|
|
7
|
+
import time
|
|
8
|
+
import datetime
|
|
9
|
+
import re
|
|
10
|
+
import sys
|
|
11
|
+
from rich.markdown import Markdown
|
|
12
|
+
from rich.console import Console
|
|
13
|
+
from typing import List, Tuple, Union
|
|
14
|
+
from typing import NoReturn
|
|
15
|
+
import requests
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from playsound import playsound
|
|
18
|
+
from time import sleep as wait
|
|
19
|
+
import pathlib
|
|
20
|
+
import urllib.parse
|
|
21
|
+
|
|
22
|
+
default_path = os.path.join(os.path.expanduser("~"), ".cache", "AIWEBS", "webscout")
|
|
23
|
+
|
|
24
|
+
def sanitize_stream(
|
|
25
|
+
chunk: str, intro_value: str = "data:", to_json: bool = True
|
|
26
|
+
) -> str | dict:
|
|
27
|
+
"""Remove streaming flags
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
chunk (str): Streamig chunk.
|
|
31
|
+
intro_value (str, optional): streaming flag. Defaults to "data:".
|
|
32
|
+
to_json (bool, optional). Return chunk as dictionary. Defaults to True.
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
str: Sanitized streaming value.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
if chunk.startswith(intro_value):
|
|
39
|
+
chunk = chunk[len(intro_value) :]
|
|
40
|
+
|
|
41
|
+
return json.loads(chunk) if to_json else chunk
|
|
42
|
+
def run_system_command(
|
|
43
|
+
command: str,
|
|
44
|
+
exit_on_error: bool = True,
|
|
45
|
+
stdout_error: bool = True,
|
|
46
|
+
help: str = None,
|
|
47
|
+
):
|
|
48
|
+
"""Run commands against system
|
|
49
|
+
Args:
|
|
50
|
+
command (str): shell command
|
|
51
|
+
exit_on_error (bool, optional): Exit on error. Defaults to True.
|
|
52
|
+
stdout_error (bool, optional): Print out the error. Defaults to True
|
|
53
|
+
help (str, optional): Help info incase of exception. Defaults to None.
|
|
54
|
+
Returns:
|
|
55
|
+
tuple : (is_successfull, object[Exception|Subprocess.run])
|
|
56
|
+
"""
|
|
57
|
+
try:
|
|
58
|
+
# Run the command and capture the output
|
|
59
|
+
result = subprocess.run(
|
|
60
|
+
command,
|
|
61
|
+
shell=True,
|
|
62
|
+
check=True,
|
|
63
|
+
text=True,
|
|
64
|
+
stdout=subprocess.PIPE,
|
|
65
|
+
stderr=subprocess.PIPE,
|
|
66
|
+
)
|
|
67
|
+
return (True, result)
|
|
68
|
+
except subprocess.CalledProcessError as e:
|
|
69
|
+
if exit_on_error:
|
|
70
|
+
raise Exception(f"Command failed with exit code {e.returncode}") from e
|
|
71
|
+
else:
|
|
72
|
+
return (False, e)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
from .conversation import Conversation
|
|
76
|
+
|
|
77
|
+
from .optimizers import Optimizers
|
|
78
|
+
|
|
79
|
+
from .Extra.autocoder import AutoCoder
|
|
80
|
+
|
|
81
|
+
from .prompt_manager import AwesomePrompts
|
|
82
|
+
|
|
83
|
+
class Updates:
|
|
84
|
+
"""Webscout latest release info"""
|
|
85
|
+
|
|
86
|
+
url = "https://api.github.com/repos/OE-LUCIFER/Webscout/releases/latest"
|
|
87
|
+
|
|
88
|
+
@property
|
|
89
|
+
def latest_version(self):
|
|
90
|
+
return self.latest(version=True)
|
|
91
|
+
|
|
92
|
+
def executable(self, system: str = platform.system()) -> str:
|
|
93
|
+
"""Url pointing to executable for particular system
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
system (str, optional): system name. Defaults to platform.system().
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
str: url
|
|
100
|
+
"""
|
|
101
|
+
for entry in self.latest()["assets"]:
|
|
102
|
+
if entry.get("target") == system:
|
|
103
|
+
return entry.get("url")
|
|
104
|
+
|
|
105
|
+
def latest(self, whole: bool = False, version: bool = False) -> dict:
|
|
106
|
+
"""Check Webscout latest version info
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
whole (bool, optional): Return whole json response. Defaults to False.
|
|
110
|
+
version (bool, optional): return version only. Defaults to False.
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
bool|dict: version str or whole dict info
|
|
114
|
+
"""
|
|
115
|
+
import requests
|
|
116
|
+
|
|
117
|
+
data = requests.get(self.url).json()
|
|
118
|
+
if whole:
|
|
119
|
+
return data
|
|
120
|
+
|
|
121
|
+
elif version:
|
|
122
|
+
return data.get("tag_name")
|
|
123
|
+
|
|
124
|
+
else:
|
|
125
|
+
sorted = dict(
|
|
126
|
+
tag_name=data.get("tag_name"),
|
|
127
|
+
tarball_url=data.get("tarball_url"),
|
|
128
|
+
zipball_url=data.get("zipball_url"),
|
|
129
|
+
html_url=data.get("html_url"),
|
|
130
|
+
body=data.get("body"),
|
|
131
|
+
)
|
|
132
|
+
whole_assets = []
|
|
133
|
+
for entry in data.get("assets"):
|
|
134
|
+
url = entry.get("browser_download_url")
|
|
135
|
+
assets = dict(url=url, size=entry.get("size"))
|
|
136
|
+
if ".deb" in url:
|
|
137
|
+
assets["target"] = "Debian"
|
|
138
|
+
elif ".exe" in url:
|
|
139
|
+
assets["target"] = "Windows"
|
|
140
|
+
elif "macos" in url:
|
|
141
|
+
assets["target"] = "Mac"
|
|
142
|
+
elif "linux" in url:
|
|
143
|
+
assets["target"] = "Linux"
|
|
144
|
+
|
|
145
|
+
whole_assets.append(assets)
|
|
146
|
+
sorted["assets"] = whole_assets
|
|
147
|
+
|
|
148
|
+
return sorted
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
class Audio:
|
|
152
|
+
# Request headers
|
|
153
|
+
headers: dict[str, str] = {
|
|
154
|
+
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36"
|
|
155
|
+
}
|
|
156
|
+
cache_dir = pathlib.Path("./audio_cache")
|
|
157
|
+
all_voices: list[str] = [
|
|
158
|
+
"Filiz",
|
|
159
|
+
"Astrid",
|
|
160
|
+
"Tatyana",
|
|
161
|
+
"Maxim",
|
|
162
|
+
"Carmen",
|
|
163
|
+
"Ines",
|
|
164
|
+
"Cristiano",
|
|
165
|
+
"Vitoria",
|
|
166
|
+
"Ricardo",
|
|
167
|
+
"Maja",
|
|
168
|
+
"Jan",
|
|
169
|
+
"Jacek",
|
|
170
|
+
"Ewa",
|
|
171
|
+
"Ruben",
|
|
172
|
+
"Lotte",
|
|
173
|
+
"Liv",
|
|
174
|
+
"Seoyeon",
|
|
175
|
+
"Takumi",
|
|
176
|
+
"Mizuki",
|
|
177
|
+
"Giorgio",
|
|
178
|
+
"Carla",
|
|
179
|
+
"Bianca",
|
|
180
|
+
"Karl",
|
|
181
|
+
"Dora",
|
|
182
|
+
"Mathieu",
|
|
183
|
+
"Celine",
|
|
184
|
+
"Chantal",
|
|
185
|
+
"Penelope",
|
|
186
|
+
"Miguel",
|
|
187
|
+
"Mia",
|
|
188
|
+
"Enrique",
|
|
189
|
+
"Conchita",
|
|
190
|
+
"Geraint",
|
|
191
|
+
"Salli",
|
|
192
|
+
"Matthew",
|
|
193
|
+
"Kimberly",
|
|
194
|
+
"Kendra",
|
|
195
|
+
"Justin",
|
|
196
|
+
"Joey",
|
|
197
|
+
"Joanna",
|
|
198
|
+
"Ivy",
|
|
199
|
+
"Raveena",
|
|
200
|
+
"Aditi",
|
|
201
|
+
"Emma",
|
|
202
|
+
"Brian",
|
|
203
|
+
"Amy",
|
|
204
|
+
"Russell",
|
|
205
|
+
"Nicole",
|
|
206
|
+
"Vicki",
|
|
207
|
+
"Marlene",
|
|
208
|
+
"Hans",
|
|
209
|
+
"Naja",
|
|
210
|
+
"Mads",
|
|
211
|
+
"Gwyneth",
|
|
212
|
+
"Zhiyu",
|
|
213
|
+
"es-ES-Standard-A",
|
|
214
|
+
"it-IT-Standard-A",
|
|
215
|
+
"it-IT-Wavenet-A",
|
|
216
|
+
"ja-JP-Standard-A",
|
|
217
|
+
"ja-JP-Wavenet-A",
|
|
218
|
+
"ko-KR-Standard-A",
|
|
219
|
+
"ko-KR-Wavenet-A",
|
|
220
|
+
"pt-BR-Standard-A",
|
|
221
|
+
"tr-TR-Standard-A",
|
|
222
|
+
"sv-SE-Standard-A",
|
|
223
|
+
"nl-NL-Standard-A",
|
|
224
|
+
"nl-NL-Wavenet-A",
|
|
225
|
+
"en-US-Wavenet-A",
|
|
226
|
+
"en-US-Wavenet-B",
|
|
227
|
+
"en-US-Wavenet-C",
|
|
228
|
+
"en-US-Wavenet-D",
|
|
229
|
+
"en-US-Wavenet-E",
|
|
230
|
+
"en-US-Wavenet-F",
|
|
231
|
+
"en-GB-Standard-A",
|
|
232
|
+
"en-GB-Standard-B",
|
|
233
|
+
"en-GB-Standard-C",
|
|
234
|
+
"en-GB-Standard-D",
|
|
235
|
+
"en-GB-Wavenet-A",
|
|
236
|
+
"en-GB-Wavenet-B",
|
|
237
|
+
"en-GB-Wavenet-C",
|
|
238
|
+
"en-GB-Wavenet-D",
|
|
239
|
+
"en-US-Standard-B",
|
|
240
|
+
"en-US-Standard-C",
|
|
241
|
+
"en-US-Standard-D",
|
|
242
|
+
"en-US-Standard-E",
|
|
243
|
+
"de-DE-Standard-A",
|
|
244
|
+
"de-DE-Standard-B",
|
|
245
|
+
"de-DE-Wavenet-A",
|
|
246
|
+
"de-DE-Wavenet-B",
|
|
247
|
+
"de-DE-Wavenet-C",
|
|
248
|
+
"de-DE-Wavenet-D",
|
|
249
|
+
"en-AU-Standard-A",
|
|
250
|
+
"en-AU-Standard-B",
|
|
251
|
+
"en-AU-Wavenet-A",
|
|
252
|
+
"en-AU-Wavenet-B",
|
|
253
|
+
"en-AU-Wavenet-C",
|
|
254
|
+
"en-AU-Wavenet-D",
|
|
255
|
+
"en-AU-Standard-C",
|
|
256
|
+
"en-AU-Standard-D",
|
|
257
|
+
"fr-CA-Standard-A",
|
|
258
|
+
"fr-CA-Standard-B",
|
|
259
|
+
"fr-CA-Standard-C",
|
|
260
|
+
"fr-CA-Standard-D",
|
|
261
|
+
"fr-FR-Standard-C",
|
|
262
|
+
"fr-FR-Standard-D",
|
|
263
|
+
"fr-FR-Wavenet-A",
|
|
264
|
+
"fr-FR-Wavenet-B",
|
|
265
|
+
"fr-FR-Wavenet-C",
|
|
266
|
+
"fr-FR-Wavenet-D",
|
|
267
|
+
"da-DK-Wavenet-A",
|
|
268
|
+
"pl-PL-Wavenet-A",
|
|
269
|
+
"pl-PL-Wavenet-B",
|
|
270
|
+
"pl-PL-Wavenet-C",
|
|
271
|
+
"pl-PL-Wavenet-D",
|
|
272
|
+
"pt-PT-Wavenet-A",
|
|
273
|
+
"pt-PT-Wavenet-B",
|
|
274
|
+
"pt-PT-Wavenet-C",
|
|
275
|
+
"pt-PT-Wavenet-D",
|
|
276
|
+
"ru-RU-Wavenet-A",
|
|
277
|
+
"ru-RU-Wavenet-B",
|
|
278
|
+
"ru-RU-Wavenet-C",
|
|
279
|
+
"ru-RU-Wavenet-D",
|
|
280
|
+
"sk-SK-Wavenet-A",
|
|
281
|
+
"tr-TR-Wavenet-A",
|
|
282
|
+
"tr-TR-Wavenet-B",
|
|
283
|
+
"tr-TR-Wavenet-C",
|
|
284
|
+
"tr-TR-Wavenet-D",
|
|
285
|
+
"tr-TR-Wavenet-E",
|
|
286
|
+
"uk-UA-Wavenet-A",
|
|
287
|
+
"ar-XA-Wavenet-A",
|
|
288
|
+
"ar-XA-Wavenet-B",
|
|
289
|
+
"ar-XA-Wavenet-C",
|
|
290
|
+
"cs-CZ-Wavenet-A",
|
|
291
|
+
"nl-NL-Wavenet-B",
|
|
292
|
+
"nl-NL-Wavenet-C",
|
|
293
|
+
"nl-NL-Wavenet-D",
|
|
294
|
+
"nl-NL-Wavenet-E",
|
|
295
|
+
"en-IN-Wavenet-A",
|
|
296
|
+
"en-IN-Wavenet-B",
|
|
297
|
+
"en-IN-Wavenet-C",
|
|
298
|
+
"fil-PH-Wavenet-A",
|
|
299
|
+
"fi-FI-Wavenet-A",
|
|
300
|
+
"el-GR-Wavenet-A",
|
|
301
|
+
"hi-IN-Wavenet-A",
|
|
302
|
+
"hi-IN-Wavenet-B",
|
|
303
|
+
"hi-IN-Wavenet-C",
|
|
304
|
+
"hu-HU-Wavenet-A",
|
|
305
|
+
"id-ID-Wavenet-A",
|
|
306
|
+
"id-ID-Wavenet-B",
|
|
307
|
+
"id-ID-Wavenet-C",
|
|
308
|
+
"it-IT-Wavenet-B",
|
|
309
|
+
"it-IT-Wavenet-C",
|
|
310
|
+
"it-IT-Wavenet-D",
|
|
311
|
+
"ja-JP-Wavenet-B",
|
|
312
|
+
"ja-JP-Wavenet-C",
|
|
313
|
+
"ja-JP-Wavenet-D",
|
|
314
|
+
"cmn-CN-Wavenet-A",
|
|
315
|
+
"cmn-CN-Wavenet-B",
|
|
316
|
+
"cmn-CN-Wavenet-C",
|
|
317
|
+
"cmn-CN-Wavenet-D",
|
|
318
|
+
"nb-no-Wavenet-E",
|
|
319
|
+
"nb-no-Wavenet-A",
|
|
320
|
+
"nb-no-Wavenet-B",
|
|
321
|
+
"nb-no-Wavenet-C",
|
|
322
|
+
"nb-no-Wavenet-D",
|
|
323
|
+
"vi-VN-Wavenet-A",
|
|
324
|
+
"vi-VN-Wavenet-B",
|
|
325
|
+
"vi-VN-Wavenet-C",
|
|
326
|
+
"vi-VN-Wavenet-D",
|
|
327
|
+
"sr-rs-Standard-A",
|
|
328
|
+
"lv-lv-Standard-A",
|
|
329
|
+
"is-is-Standard-A",
|
|
330
|
+
"bg-bg-Standard-A",
|
|
331
|
+
"af-ZA-Standard-A",
|
|
332
|
+
"Tracy",
|
|
333
|
+
"Danny",
|
|
334
|
+
"Huihui",
|
|
335
|
+
"Yaoyao",
|
|
336
|
+
"Kangkang",
|
|
337
|
+
"HanHan",
|
|
338
|
+
"Zhiwei",
|
|
339
|
+
"Asaf",
|
|
340
|
+
"An",
|
|
341
|
+
"Stefanos",
|
|
342
|
+
"Filip",
|
|
343
|
+
"Ivan",
|
|
344
|
+
"Heidi",
|
|
345
|
+
"Herena",
|
|
346
|
+
"Kalpana",
|
|
347
|
+
"Hemant",
|
|
348
|
+
"Matej",
|
|
349
|
+
"Andika",
|
|
350
|
+
"Rizwan",
|
|
351
|
+
"Lado",
|
|
352
|
+
"Valluvar",
|
|
353
|
+
"Linda",
|
|
354
|
+
"Heather",
|
|
355
|
+
"Sean",
|
|
356
|
+
"Michael",
|
|
357
|
+
"Karsten",
|
|
358
|
+
"Guillaume",
|
|
359
|
+
"Pattara",
|
|
360
|
+
"Jakub",
|
|
361
|
+
"Szabolcs",
|
|
362
|
+
"Hoda",
|
|
363
|
+
"Naayf",
|
|
364
|
+
]
|
|
365
|
+
|
|
366
|
+
@classmethod
|
|
367
|
+
def text_to_audio(
|
|
368
|
+
cls,
|
|
369
|
+
message: str,
|
|
370
|
+
voice: str = "Brian",
|
|
371
|
+
save_to: Union[Path, str] = None,
|
|
372
|
+
auto: bool = True,
|
|
373
|
+
) -> Union[str, bytes]:
|
|
374
|
+
"""
|
|
375
|
+
Text to speech using StreamElements API
|
|
376
|
+
|
|
377
|
+
Parameters:
|
|
378
|
+
message (str): The text to convert to speech
|
|
379
|
+
voice (str, optional): The voice to use for speech synthesis. Defaults to "Brian".
|
|
380
|
+
save_to (bool, optional): Path to save the audio file. Defaults to None.
|
|
381
|
+
auto (bool, optional): Generate filename based on `message` and save to `cls.cache_dir`. Defaults to False.
|
|
382
|
+
|
|
383
|
+
Returns:
|
|
384
|
+
result (Union[str, bytes]): Path to saved contents or audio content.
|
|
385
|
+
"""
|
|
386
|
+
assert (
|
|
387
|
+
voice in cls.all_voices
|
|
388
|
+
), f"Voice '{voice}' not one of [{', '.join(cls.all_voices)}]"
|
|
389
|
+
# Base URL for provider API
|
|
390
|
+
url: str = (
|
|
391
|
+
f"https://api.streamelements.com/kappa/v2/speech?voice={voice}&text={{{urllib.parse.quote(message)}}}"
|
|
392
|
+
)
|
|
393
|
+
resp = requests.get(url=url, headers=cls.headers, stream=True)
|
|
394
|
+
if not resp.ok:
|
|
395
|
+
raise Exception(
|
|
396
|
+
f"Failed to perform the operation - ({resp.status_code}, {resp.reason}) - {resp.text}"
|
|
397
|
+
)
|
|
398
|
+
|
|
399
|
+
def sanitize_filename(path):
|
|
400
|
+
trash = [
|
|
401
|
+
"\\",
|
|
402
|
+
"/",
|
|
403
|
+
":",
|
|
404
|
+
"*",
|
|
405
|
+
"?",
|
|
406
|
+
'"',
|
|
407
|
+
"<",
|
|
408
|
+
"|",
|
|
409
|
+
">",
|
|
410
|
+
]
|
|
411
|
+
for val in trash:
|
|
412
|
+
path = path.replace(val, "")
|
|
413
|
+
return path.strip()
|
|
414
|
+
|
|
415
|
+
if auto:
|
|
416
|
+
filename: str = message + "..." if len(message) <= 40 else message[:40]
|
|
417
|
+
save_to = cls.cache_dir / sanitize_filename(filename)
|
|
418
|
+
save_to = save_to.as_posix()
|
|
419
|
+
|
|
420
|
+
# Ensure cache_dir exists
|
|
421
|
+
cls.cache_dir.mkdir(parents=True, exist_ok=True)
|
|
422
|
+
|
|
423
|
+
if save_to:
|
|
424
|
+
if not save_to.endswith("mp3"):
|
|
425
|
+
save_to += ".mp3"
|
|
426
|
+
|
|
427
|
+
with open(save_to, "wb") as fh:
|
|
428
|
+
for chunk in resp.iter_content(chunk_size=512):
|
|
429
|
+
fh.write(chunk)
|
|
430
|
+
else:
|
|
431
|
+
return resp.content
|
|
432
|
+
return save_to
|
|
433
|
+
|
|
434
|
+
@staticmethod
|
|
435
|
+
def play(path_to_audio_file: Union[Path, str]) -> NoReturn:
|
|
436
|
+
"""Play audio (.mp3) using playsound.
|
|
437
|
+
"""
|
|
438
|
+
if not Path(path_to_audio_file).is_file():
|
|
439
|
+
raise FileNotFoundError(f"File does not exist - '{path_to_audio_file}'")
|
|
440
|
+
playsound(path_to_audio_file)
|
|
441
441
|
#
|