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