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.

Files changed (154) hide show
  1. webscout/AIauto.py +191 -191
  2. webscout/AIbase.py +122 -122
  3. webscout/AIutel.py +440 -440
  4. webscout/Bard.py +343 -161
  5. webscout/DWEBS.py +489 -492
  6. webscout/Extra/YTToolkit/YTdownloader.py +995 -995
  7. webscout/Extra/YTToolkit/__init__.py +2 -2
  8. webscout/Extra/YTToolkit/transcriber.py +476 -479
  9. webscout/Extra/YTToolkit/ytapi/channel.py +307 -307
  10. webscout/Extra/YTToolkit/ytapi/playlist.py +58 -58
  11. webscout/Extra/YTToolkit/ytapi/pool.py +7 -7
  12. webscout/Extra/YTToolkit/ytapi/utils.py +62 -62
  13. webscout/Extra/YTToolkit/ytapi/video.py +103 -103
  14. webscout/Extra/autocoder/__init__.py +9 -9
  15. webscout/Extra/autocoder/autocoder_utiles.py +199 -199
  16. webscout/Extra/autocoder/rawdog.py +5 -7
  17. webscout/Extra/autollama.py +230 -230
  18. webscout/Extra/gguf.py +3 -3
  19. webscout/Extra/weather.py +171 -171
  20. webscout/LLM.py +442 -442
  21. webscout/Litlogger/__init__.py +67 -681
  22. webscout/Litlogger/core/__init__.py +6 -0
  23. webscout/Litlogger/core/level.py +23 -0
  24. webscout/Litlogger/core/logger.py +166 -0
  25. webscout/Litlogger/handlers/__init__.py +12 -0
  26. webscout/Litlogger/handlers/console.py +33 -0
  27. webscout/Litlogger/handlers/file.py +143 -0
  28. webscout/Litlogger/handlers/network.py +173 -0
  29. webscout/Litlogger/styles/__init__.py +7 -0
  30. webscout/Litlogger/styles/colors.py +249 -0
  31. webscout/Litlogger/styles/formats.py +460 -0
  32. webscout/Litlogger/styles/text.py +87 -0
  33. webscout/Litlogger/utils/__init__.py +6 -0
  34. webscout/Litlogger/utils/detectors.py +154 -0
  35. webscout/Litlogger/utils/formatters.py +200 -0
  36. webscout/Provider/AISEARCH/DeepFind.py +250 -250
  37. webscout/Provider/AISEARCH/ISou.py +277 -0
  38. webscout/Provider/AISEARCH/__init__.py +2 -1
  39. webscout/Provider/Blackboxai.py +3 -3
  40. webscout/Provider/ChatGPTGratis.py +226 -0
  41. webscout/Provider/Cloudflare.py +3 -4
  42. webscout/Provider/DeepSeek.py +218 -0
  43. webscout/Provider/Deepinfra.py +40 -24
  44. webscout/Provider/Free2GPT.py +131 -124
  45. webscout/Provider/Gemini.py +100 -115
  46. webscout/Provider/Glider.py +3 -3
  47. webscout/Provider/Groq.py +5 -1
  48. webscout/Provider/Jadve.py +3 -3
  49. webscout/Provider/Marcus.py +191 -192
  50. webscout/Provider/Netwrck.py +3 -3
  51. webscout/Provider/PI.py +2 -2
  52. webscout/Provider/PizzaGPT.py +2 -3
  53. webscout/Provider/QwenLM.py +311 -0
  54. webscout/Provider/TTI/AiForce/__init__.py +22 -22
  55. webscout/Provider/TTI/AiForce/async_aiforce.py +257 -257
  56. webscout/Provider/TTI/AiForce/sync_aiforce.py +242 -242
  57. webscout/Provider/TTI/FreeAIPlayground/__init__.py +9 -0
  58. webscout/Provider/TTI/FreeAIPlayground/async_freeaiplayground.py +206 -0
  59. webscout/Provider/TTI/FreeAIPlayground/sync_freeaiplayground.py +192 -0
  60. webscout/Provider/TTI/Nexra/__init__.py +22 -22
  61. webscout/Provider/TTI/Nexra/async_nexra.py +286 -286
  62. webscout/Provider/TTI/Nexra/sync_nexra.py +258 -258
  63. webscout/Provider/TTI/PollinationsAI/__init__.py +23 -23
  64. webscout/Provider/TTI/PollinationsAI/async_pollinations.py +330 -330
  65. webscout/Provider/TTI/PollinationsAI/sync_pollinations.py +285 -285
  66. webscout/Provider/TTI/__init__.py +2 -1
  67. webscout/Provider/TTI/artbit/__init__.py +22 -22
  68. webscout/Provider/TTI/artbit/async_artbit.py +184 -184
  69. webscout/Provider/TTI/artbit/sync_artbit.py +176 -176
  70. webscout/Provider/TTI/blackbox/__init__.py +4 -4
  71. webscout/Provider/TTI/blackbox/async_blackbox.py +212 -212
  72. webscout/Provider/TTI/blackbox/sync_blackbox.py +199 -199
  73. webscout/Provider/TTI/deepinfra/__init__.py +4 -4
  74. webscout/Provider/TTI/deepinfra/async_deepinfra.py +227 -227
  75. webscout/Provider/TTI/deepinfra/sync_deepinfra.py +199 -199
  76. webscout/Provider/TTI/huggingface/__init__.py +22 -22
  77. webscout/Provider/TTI/huggingface/async_huggingface.py +199 -199
  78. webscout/Provider/TTI/huggingface/sync_huggingface.py +195 -195
  79. webscout/Provider/TTI/imgninza/__init__.py +4 -4
  80. webscout/Provider/TTI/imgninza/async_ninza.py +214 -214
  81. webscout/Provider/TTI/imgninza/sync_ninza.py +209 -209
  82. webscout/Provider/TTI/talkai/__init__.py +4 -4
  83. webscout/Provider/TTI/talkai/async_talkai.py +229 -229
  84. webscout/Provider/TTI/talkai/sync_talkai.py +207 -207
  85. webscout/Provider/TTS/deepgram.py +182 -182
  86. webscout/Provider/TTS/elevenlabs.py +136 -136
  87. webscout/Provider/TTS/gesserit.py +150 -150
  88. webscout/Provider/TTS/murfai.py +138 -138
  89. webscout/Provider/TTS/parler.py +133 -134
  90. webscout/Provider/TTS/streamElements.py +360 -360
  91. webscout/Provider/TTS/utils.py +280 -280
  92. webscout/Provider/TTS/voicepod.py +116 -116
  93. webscout/Provider/TextPollinationsAI.py +28 -8
  94. webscout/Provider/WiseCat.py +193 -0
  95. webscout/Provider/__init__.py +146 -134
  96. webscout/Provider/cerebras.py +242 -227
  97. webscout/Provider/chatglm.py +204 -204
  98. webscout/Provider/dgaf.py +2 -3
  99. webscout/Provider/freeaichat.py +221 -0
  100. webscout/Provider/gaurish.py +2 -3
  101. webscout/Provider/geminiapi.py +208 -208
  102. webscout/Provider/granite.py +223 -0
  103. webscout/Provider/hermes.py +218 -218
  104. webscout/Provider/llama3mitril.py +179 -179
  105. webscout/Provider/llamatutor.py +3 -3
  106. webscout/Provider/llmchat.py +2 -3
  107. webscout/Provider/meta.py +794 -794
  108. webscout/Provider/multichat.py +331 -331
  109. webscout/Provider/typegpt.py +359 -359
  110. webscout/Provider/yep.py +3 -3
  111. webscout/__init__.py +1 -0
  112. webscout/__main__.py +5 -5
  113. webscout/cli.py +319 -319
  114. webscout/conversation.py +241 -242
  115. webscout/exceptions.py +328 -328
  116. webscout/litagent/__init__.py +28 -28
  117. webscout/litagent/agent.py +2 -3
  118. webscout/litprinter/__init__.py +0 -58
  119. webscout/scout/__init__.py +8 -8
  120. webscout/scout/core.py +884 -884
  121. webscout/scout/element.py +459 -459
  122. webscout/scout/parsers/__init__.py +69 -69
  123. webscout/scout/parsers/html5lib_parser.py +172 -172
  124. webscout/scout/parsers/html_parser.py +236 -236
  125. webscout/scout/parsers/lxml_parser.py +178 -178
  126. webscout/scout/utils.py +38 -38
  127. webscout/swiftcli/__init__.py +811 -811
  128. webscout/update_checker.py +2 -12
  129. webscout/version.py +1 -1
  130. webscout/webscout_search.py +87 -6
  131. webscout/webscout_search_async.py +58 -1
  132. webscout/yep_search.py +297 -0
  133. webscout/zeroart/__init__.py +54 -54
  134. webscout/zeroart/base.py +60 -60
  135. webscout/zeroart/effects.py +99 -99
  136. webscout/zeroart/fonts.py +816 -816
  137. {webscout-7.1.dist-info → webscout-7.3.dist-info}/METADATA +62 -22
  138. webscout-7.3.dist-info/RECORD +223 -0
  139. {webscout-7.1.dist-info → webscout-7.3.dist-info}/WHEEL +1 -1
  140. webstoken/__init__.py +30 -30
  141. webstoken/classifier.py +189 -189
  142. webstoken/keywords.py +216 -216
  143. webstoken/language.py +128 -128
  144. webstoken/ner.py +164 -164
  145. webstoken/normalizer.py +35 -35
  146. webstoken/processor.py +77 -77
  147. webstoken/sentiment.py +206 -206
  148. webstoken/stemmer.py +73 -73
  149. webstoken/tagger.py +60 -60
  150. webstoken/tokenizer.py +158 -158
  151. webscout-7.1.dist-info/RECORD +0 -198
  152. {webscout-7.1.dist-info → webscout-7.3.dist-info}/LICENSE.md +0 -0
  153. {webscout-7.1.dist-info → webscout-7.3.dist-info}/entry_points.txt +0 -0
  154. {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
  #