webscout 7.0__py3-none-any.whl → 7.2__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 (147) 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 +20 -0
  24. webscout/Litlogger/core/logger.py +123 -0
  25. webscout/Litlogger/handlers/__init__.py +12 -0
  26. webscout/Litlogger/handlers/console.py +50 -0
  27. webscout/Litlogger/handlers/file.py +143 -0
  28. webscout/Litlogger/handlers/network.py +174 -0
  29. webscout/Litlogger/styles/__init__.py +7 -0
  30. webscout/Litlogger/styles/colors.py +231 -0
  31. webscout/Litlogger/styles/formats.py +377 -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/Blackboxai.py +136 -137
  38. webscout/Provider/ChatGPTGratis.py +226 -0
  39. webscout/Provider/Cloudflare.py +91 -78
  40. webscout/Provider/DeepSeek.py +218 -0
  41. webscout/Provider/Deepinfra.py +59 -35
  42. webscout/Provider/Free2GPT.py +131 -124
  43. webscout/Provider/Gemini.py +100 -115
  44. webscout/Provider/Glider.py +74 -59
  45. webscout/Provider/Groq.py +30 -18
  46. webscout/Provider/Jadve.py +108 -77
  47. webscout/Provider/Llama3.py +117 -94
  48. webscout/Provider/Marcus.py +191 -137
  49. webscout/Provider/Netwrck.py +62 -50
  50. webscout/Provider/PI.py +79 -124
  51. webscout/Provider/PizzaGPT.py +129 -83
  52. webscout/Provider/QwenLM.py +311 -0
  53. webscout/Provider/TTI/AiForce/__init__.py +22 -22
  54. webscout/Provider/TTI/AiForce/async_aiforce.py +257 -257
  55. webscout/Provider/TTI/AiForce/sync_aiforce.py +242 -242
  56. webscout/Provider/TTI/Nexra/__init__.py +22 -22
  57. webscout/Provider/TTI/Nexra/async_nexra.py +286 -286
  58. webscout/Provider/TTI/Nexra/sync_nexra.py +258 -258
  59. webscout/Provider/TTI/PollinationsAI/__init__.py +23 -23
  60. webscout/Provider/TTI/PollinationsAI/async_pollinations.py +330 -330
  61. webscout/Provider/TTI/PollinationsAI/sync_pollinations.py +285 -285
  62. webscout/Provider/TTI/artbit/__init__.py +22 -22
  63. webscout/Provider/TTI/artbit/async_artbit.py +184 -184
  64. webscout/Provider/TTI/artbit/sync_artbit.py +176 -176
  65. webscout/Provider/TTI/blackbox/__init__.py +4 -4
  66. webscout/Provider/TTI/blackbox/async_blackbox.py +212 -212
  67. webscout/Provider/TTI/blackbox/sync_blackbox.py +199 -199
  68. webscout/Provider/TTI/deepinfra/__init__.py +4 -4
  69. webscout/Provider/TTI/deepinfra/async_deepinfra.py +227 -227
  70. webscout/Provider/TTI/deepinfra/sync_deepinfra.py +199 -199
  71. webscout/Provider/TTI/huggingface/__init__.py +22 -22
  72. webscout/Provider/TTI/huggingface/async_huggingface.py +199 -199
  73. webscout/Provider/TTI/huggingface/sync_huggingface.py +195 -195
  74. webscout/Provider/TTI/imgninza/__init__.py +4 -4
  75. webscout/Provider/TTI/imgninza/async_ninza.py +214 -214
  76. webscout/Provider/TTI/imgninza/sync_ninza.py +209 -209
  77. webscout/Provider/TTI/talkai/__init__.py +4 -4
  78. webscout/Provider/TTI/talkai/async_talkai.py +229 -229
  79. webscout/Provider/TTI/talkai/sync_talkai.py +207 -207
  80. webscout/Provider/TTS/deepgram.py +182 -182
  81. webscout/Provider/TTS/elevenlabs.py +136 -136
  82. webscout/Provider/TTS/gesserit.py +150 -150
  83. webscout/Provider/TTS/murfai.py +138 -138
  84. webscout/Provider/TTS/parler.py +133 -134
  85. webscout/Provider/TTS/streamElements.py +360 -360
  86. webscout/Provider/TTS/utils.py +280 -280
  87. webscout/Provider/TTS/voicepod.py +116 -116
  88. webscout/Provider/TextPollinationsAI.py +74 -47
  89. webscout/Provider/WiseCat.py +193 -0
  90. webscout/Provider/__init__.py +144 -136
  91. webscout/Provider/cerebras.py +242 -227
  92. webscout/Provider/chatglm.py +204 -204
  93. webscout/Provider/dgaf.py +67 -39
  94. webscout/Provider/gaurish.py +105 -66
  95. webscout/Provider/geminiapi.py +208 -208
  96. webscout/Provider/granite.py +223 -0
  97. webscout/Provider/hermes.py +218 -218
  98. webscout/Provider/llama3mitril.py +179 -179
  99. webscout/Provider/llamatutor.py +72 -62
  100. webscout/Provider/llmchat.py +60 -35
  101. webscout/Provider/meta.py +794 -794
  102. webscout/Provider/multichat.py +331 -230
  103. webscout/Provider/typegpt.py +359 -356
  104. webscout/Provider/yep.py +5 -5
  105. webscout/__main__.py +5 -5
  106. webscout/cli.py +319 -319
  107. webscout/conversation.py +241 -242
  108. webscout/exceptions.py +328 -328
  109. webscout/litagent/__init__.py +28 -28
  110. webscout/litagent/agent.py +2 -3
  111. webscout/litprinter/__init__.py +0 -58
  112. webscout/scout/__init__.py +8 -8
  113. webscout/scout/core.py +884 -884
  114. webscout/scout/element.py +459 -459
  115. webscout/scout/parsers/__init__.py +69 -69
  116. webscout/scout/parsers/html5lib_parser.py +172 -172
  117. webscout/scout/parsers/html_parser.py +236 -236
  118. webscout/scout/parsers/lxml_parser.py +178 -178
  119. webscout/scout/utils.py +38 -38
  120. webscout/swiftcli/__init__.py +811 -811
  121. webscout/update_checker.py +2 -12
  122. webscout/version.py +1 -1
  123. webscout/webscout_search.py +1142 -1140
  124. webscout/webscout_search_async.py +635 -635
  125. webscout/zeroart/__init__.py +54 -54
  126. webscout/zeroart/base.py +60 -60
  127. webscout/zeroart/effects.py +99 -99
  128. webscout/zeroart/fonts.py +816 -816
  129. {webscout-7.0.dist-info → webscout-7.2.dist-info}/METADATA +21 -28
  130. webscout-7.2.dist-info/RECORD +217 -0
  131. webstoken/__init__.py +30 -30
  132. webstoken/classifier.py +189 -189
  133. webstoken/keywords.py +216 -216
  134. webstoken/language.py +128 -128
  135. webstoken/ner.py +164 -164
  136. webstoken/normalizer.py +35 -35
  137. webstoken/processor.py +77 -77
  138. webstoken/sentiment.py +206 -206
  139. webstoken/stemmer.py +73 -73
  140. webstoken/tagger.py +60 -60
  141. webstoken/tokenizer.py +158 -158
  142. webscout/Provider/RUBIKSAI.py +0 -272
  143. webscout-7.0.dist-info/RECORD +0 -199
  144. {webscout-7.0.dist-info → webscout-7.2.dist-info}/LICENSE.md +0 -0
  145. {webscout-7.0.dist-info → webscout-7.2.dist-info}/WHEEL +0 -0
  146. {webscout-7.0.dist-info → webscout-7.2.dist-info}/entry_points.txt +0 -0
  147. {webscout-7.0.dist-info → webscout-7.2.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
  #