webscout 7.5__py3-none-any.whl → 7.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of webscout might be problematic. Click here for more details.

Files changed (132) hide show
  1. webscout/AIauto.py +5 -53
  2. webscout/AIutel.py +8 -318
  3. webscout/DWEBS.py +460 -489
  4. webscout/Extra/YTToolkit/YTdownloader.py +14 -53
  5. webscout/Extra/YTToolkit/transcriber.py +12 -13
  6. webscout/Extra/YTToolkit/ytapi/video.py +0 -1
  7. webscout/Extra/__init__.py +0 -1
  8. webscout/Extra/autocoder/__init__.py +9 -9
  9. webscout/Extra/autocoder/autocoder_utiles.py +193 -199
  10. webscout/Extra/autocoder/rawdog.py +789 -677
  11. webscout/Extra/gguf.py +682 -428
  12. webscout/Extra/weather.py +178 -156
  13. webscout/Extra/weather_ascii.py +70 -17
  14. webscout/Litlogger/core/logger.py +1 -2
  15. webscout/Litlogger/handlers/file.py +1 -1
  16. webscout/Litlogger/styles/formats.py +0 -2
  17. webscout/Litlogger/utils/detectors.py +0 -1
  18. webscout/Provider/AISEARCH/DeepFind.py +0 -1
  19. webscout/Provider/AISEARCH/ISou.py +1 -22
  20. webscout/Provider/AISEARCH/felo_search.py +0 -1
  21. webscout/Provider/AllenAI.py +28 -30
  22. webscout/Provider/C4ai.py +29 -11
  23. webscout/Provider/ChatGPTClone.py +226 -0
  24. webscout/Provider/ChatGPTGratis.py +24 -56
  25. webscout/Provider/DeepSeek.py +25 -17
  26. webscout/Provider/Deepinfra.py +115 -48
  27. webscout/Provider/Gemini.py +1 -1
  28. webscout/Provider/Glider.py +33 -12
  29. webscout/Provider/HF_space/qwen_qwen2.py +2 -2
  30. webscout/Provider/HeckAI.py +23 -7
  31. webscout/Provider/Hunyuan.py +272 -0
  32. webscout/Provider/Jadve.py +20 -5
  33. webscout/Provider/LambdaChat.py +391 -0
  34. webscout/Provider/Netwrck.py +42 -19
  35. webscout/Provider/OLLAMA.py +256 -32
  36. webscout/Provider/PI.py +4 -2
  37. webscout/Provider/Perplexitylabs.py +26 -6
  38. webscout/Provider/PizzaGPT.py +10 -51
  39. webscout/Provider/TTI/AiForce/async_aiforce.py +4 -37
  40. webscout/Provider/TTI/AiForce/sync_aiforce.py +41 -38
  41. webscout/Provider/TTI/FreeAIPlayground/__init__.py +9 -9
  42. webscout/Provider/TTI/FreeAIPlayground/async_freeaiplayground.py +179 -206
  43. webscout/Provider/TTI/FreeAIPlayground/sync_freeaiplayground.py +180 -192
  44. webscout/Provider/TTI/MagicStudio/__init__.py +2 -0
  45. webscout/Provider/TTI/MagicStudio/async_magicstudio.py +111 -0
  46. webscout/Provider/TTI/MagicStudio/sync_magicstudio.py +109 -0
  47. webscout/Provider/TTI/PollinationsAI/async_pollinations.py +5 -24
  48. webscout/Provider/TTI/PollinationsAI/sync_pollinations.py +2 -22
  49. webscout/Provider/TTI/__init__.py +2 -3
  50. webscout/Provider/TTI/aiarta/async_aiarta.py +14 -14
  51. webscout/Provider/TTI/aiarta/sync_aiarta.py +52 -21
  52. webscout/Provider/TTI/artbit/async_artbit.py +3 -32
  53. webscout/Provider/TTI/artbit/sync_artbit.py +3 -31
  54. webscout/Provider/TTI/fastflux/__init__.py +22 -0
  55. webscout/Provider/TTI/fastflux/async_fastflux.py +261 -0
  56. webscout/Provider/TTI/fastflux/sync_fastflux.py +252 -0
  57. webscout/Provider/TTI/piclumen/__init__.py +22 -22
  58. webscout/Provider/TTI/piclumen/sync_piclumen.py +232 -232
  59. webscout/Provider/TTS/__init__.py +2 -2
  60. webscout/Provider/TTS/deepgram.py +12 -39
  61. webscout/Provider/TTS/elevenlabs.py +14 -40
  62. webscout/Provider/TTS/gesserit.py +11 -35
  63. webscout/Provider/TTS/murfai.py +13 -39
  64. webscout/Provider/TTS/parler.py +17 -40
  65. webscout/Provider/TTS/speechma.py +180 -0
  66. webscout/Provider/TTS/streamElements.py +17 -44
  67. webscout/Provider/TextPollinationsAI.py +39 -59
  68. webscout/Provider/Venice.py +25 -8
  69. webscout/Provider/WebSim.py +227 -0
  70. webscout/Provider/WiseCat.py +27 -5
  71. webscout/Provider/Youchat.py +64 -37
  72. webscout/Provider/__init__.py +12 -7
  73. webscout/Provider/akashgpt.py +20 -5
  74. webscout/Provider/flowith.py +33 -7
  75. webscout/Provider/freeaichat.py +32 -45
  76. webscout/Provider/koala.py +20 -5
  77. webscout/Provider/labyrinth.py +239 -0
  78. webscout/Provider/learnfastai.py +28 -15
  79. webscout/Provider/llamatutor.py +1 -1
  80. webscout/Provider/llmchat.py +30 -8
  81. webscout/Provider/multichat.py +65 -9
  82. webscout/Provider/sonus.py +208 -0
  83. webscout/Provider/talkai.py +1 -0
  84. webscout/Provider/turboseek.py +3 -0
  85. webscout/Provider/tutorai.py +2 -0
  86. webscout/Provider/typegpt.py +155 -65
  87. webscout/Provider/uncovr.py +297 -0
  88. webscout/Provider/x0gpt.py +3 -1
  89. webscout/Provider/yep.py +102 -20
  90. webscout/__init__.py +3 -0
  91. webscout/cli.py +53 -40
  92. webscout/conversation.py +1 -10
  93. webscout/litagent/__init__.py +2 -2
  94. webscout/litagent/agent.py +356 -20
  95. webscout/litagent/constants.py +34 -5
  96. webscout/litprinter/__init__.py +0 -3
  97. webscout/models.py +181 -0
  98. webscout/optimizers.py +1 -1
  99. webscout/prompt_manager.py +2 -8
  100. webscout/scout/core/scout.py +1 -4
  101. webscout/scout/core/search_result.py +1 -1
  102. webscout/scout/core/text_utils.py +1 -1
  103. webscout/scout/core.py +2 -5
  104. webscout/scout/element.py +1 -1
  105. webscout/scout/parsers/html_parser.py +1 -1
  106. webscout/scout/utils.py +0 -1
  107. webscout/swiftcli/__init__.py +1 -3
  108. webscout/tempid.py +1 -1
  109. webscout/update_checker.py +1 -3
  110. webscout/version.py +1 -1
  111. webscout/webscout_search_async.py +1 -2
  112. webscout/yep_search.py +297 -297
  113. {webscout-7.5.dist-info → webscout-7.7.dist-info}/LICENSE.md +4 -4
  114. {webscout-7.5.dist-info → webscout-7.7.dist-info}/METADATA +127 -405
  115. {webscout-7.5.dist-info → webscout-7.7.dist-info}/RECORD +118 -117
  116. webscout/Extra/autollama.py +0 -231
  117. webscout/Provider/Amigo.py +0 -274
  118. webscout/Provider/Bing.py +0 -243
  119. webscout/Provider/DiscordRocks.py +0 -253
  120. webscout/Provider/TTI/blackbox/__init__.py +0 -4
  121. webscout/Provider/TTI/blackbox/async_blackbox.py +0 -212
  122. webscout/Provider/TTI/blackbox/sync_blackbox.py +0 -199
  123. webscout/Provider/TTI/deepinfra/__init__.py +0 -4
  124. webscout/Provider/TTI/deepinfra/async_deepinfra.py +0 -227
  125. webscout/Provider/TTI/deepinfra/sync_deepinfra.py +0 -199
  126. webscout/Provider/TTI/imgninza/__init__.py +0 -4
  127. webscout/Provider/TTI/imgninza/async_ninza.py +0 -214
  128. webscout/Provider/TTI/imgninza/sync_ninza.py +0 -209
  129. webscout/Provider/TTS/voicepod.py +0 -117
  130. {webscout-7.5.dist-info → webscout-7.7.dist-info}/WHEEL +0 -0
  131. {webscout-7.5.dist-info → webscout-7.7.dist-info}/entry_points.txt +0 -0
  132. {webscout-7.5.dist-info → webscout-7.7.dist-info}/top_level.txt +0 -0
@@ -1,11 +1,10 @@
1
1
  import time
2
2
  import requests
3
3
  import pathlib
4
+ import tempfile
4
5
  from io import BytesIO
5
- from playsound import playsound
6
6
  from webscout import exceptions
7
7
  from webscout.AIbase import TTSProvider
8
- from webscout.Litlogger import Logger, LogFormat
9
8
  from webscout.litagent import LitAgent
10
9
  from concurrent.futures import ThreadPoolExecutor, as_completed
11
10
  from . import utils
@@ -18,7 +17,6 @@ class ElevenlabsTTS(TTSProvider):
18
17
  headers: dict[str, str] = {
19
18
  "User-Agent": LitAgent().random()
20
19
  }
21
- cache_dir = pathlib.Path("./audio_cache")
22
20
  all_voices: dict[str, str] = {"Brian": "nPczCjzI2devNBz1zQrb", "Alice":"Xb7hH8MSUJpSbSDYk0k2", "Bill":"pqHfZKP75CvOlQylNhV4", "Callum":"N2lVS1w4EtoT3dr4eOWO", "Charlie":"IKne3meq5aSn9XLyUdCD", "Charlotte":"XB0fDUnXU5powFXDhCwa", "Chris":"iP95p4xoKVk53GoZ742B", "Daniel":"onwK4e9ZLuTAKqWW03F9", "Eric":"cjVigY5qzO86Huf0OWal", "George":"JBFqnCBsd6RMkjVDRZzb", "Jessica":"cgSgspJ2msm6clMCkdW9", "Laura":"FGY2WhTYpPnrIDTdsKH5", "Liam":"TX3LPaxmHKxFdv7VOQHJ", "Lily":"pFZP5JQG7iQjIQuC4Bku", "Matilda":"XrExE9yKIg1WjnnlVkGX", "Sarah":"EXAVITQu4vr4xnSDxMaL", "Will":"bIHbv24MWmeRgasZH58o"}
23
21
 
24
22
  def __init__(self, timeout: int = 20, proxies: dict = None):
@@ -29,11 +27,7 @@ class ElevenlabsTTS(TTSProvider):
29
27
  self.session.proxies.update(proxies)
30
28
  self.timeout = timeout
31
29
  self.params = {'allow_unauthenticated': '1'}
32
- self.logger = Logger(
33
- name="ElevenlabsTTS",
34
- format=LogFormat.MODERN_EMOJI,
35
-
36
- )
30
+ self.temp_dir = tempfile.mkdtemp(prefix="webscout_tts_")
37
31
 
38
32
  def tts(self, text: str, voice: str = "Brian", verbose:bool = True) -> str:
39
33
  """
@@ -43,7 +37,7 @@ class ElevenlabsTTS(TTSProvider):
43
37
  voice in self.all_voices
44
38
  ), f"Voice '{voice}' not one of [{', '.join(self.all_voices.keys())}]"
45
39
 
46
- filename = self.cache_dir / f"{int(time.time())}.mp3"
40
+ filename = pathlib.Path(tempfile.mktemp(suffix=".mp3", dir=self.temp_dir))
47
41
 
48
42
  # Split text into sentences
49
43
  sentences = utils.split_sentences(text)
@@ -56,20 +50,17 @@ class ElevenlabsTTS(TTSProvider):
56
50
  response = self.session.post(f'https://api.elevenlabs.io/v1/text-to-speech/{self.all_voices[voice]}',params=self.params, headers=self.headers, json=json_data, timeout=self.timeout)
57
51
  response.raise_for_status()
58
52
 
59
- # Create the audio_cache directory if it doesn't exist
60
- self.cache_dir.mkdir(parents=True, exist_ok=True)
61
-
62
53
  # Check if the request was successful
63
54
  if response.ok and response.status_code == 200:
64
55
  if verbose:
65
- self.logger.success(f"Chunk {part_number} processed successfully 🎉")
56
+ print(f"[debug] Chunk {part_number} processed successfully")
66
57
  return part_number, response.content
67
58
  else:
68
59
  if verbose:
69
- self.logger.warning(f"No data received for chunk {part_number}. Retrying...")
60
+ print(f"[debug] No data received for chunk {part_number}. Retrying...")
70
61
  except requests.RequestException as e:
71
62
  if verbose:
72
- self.logger.error(f"Error for chunk {part_number}: {e}. Retrying... 🔄")
63
+ print(f"[debug] Error for chunk {part_number}: {e}. Retrying...")
73
64
  time.sleep(1)
74
65
  try:
75
66
  # Using ThreadPoolExecutor to handle requests concurrently
@@ -87,51 +78,34 @@ class ElevenlabsTTS(TTSProvider):
87
78
  audio_chunks[part_number] = audio_data # Store the audio data in correct sequence
88
79
  except Exception as e:
89
80
  if verbose:
90
- self.logger.error(f"Failed to generate audio for chunk {chunk_num}: {e} 🚨")
81
+ print(f"[debug] Failed to generate audio for chunk {chunk_num}: {e}")
91
82
 
92
83
  # Combine audio chunks in the correct sequence
93
84
  combined_audio = BytesIO()
94
85
  for part_number in sorted(audio_chunks.keys()):
95
86
  combined_audio.write(audio_chunks[part_number])
96
87
  if verbose:
97
- self.logger.debug(f"Added chunk {part_number} to the combined file.")
88
+ print(f"[debug] Added chunk {part_number} to the combined file.")
98
89
 
99
90
  # Save the combined audio data to a single file
100
91
  with open(filename, 'wb') as f:
101
92
  f.write(combined_audio.getvalue())
102
93
  if verbose:
103
- self.logger.info(f"Final Audio Saved as {filename} 🔊")
94
+ print(f"[debug] Final Audio Saved as {filename}")
104
95
  return filename.as_posix()
105
96
 
106
97
  except requests.exceptions.RequestException as e:
107
- self.logger.critical(f"Failed to perform the operation: {e} 🚨")
98
+ if verbose:
99
+ print(f"[debug] Failed to perform the operation: {e}")
108
100
  raise exceptions.FailedToGenerateResponseError(
109
101
  f"Failed to perform the operation: {e}"
110
102
  )
111
-
112
- def play_audio(self, filename: str):
113
- """
114
- Plays an audio file using playsound.
115
-
116
- Args:
117
- filename (str): The path to the audio file.
118
-
119
- Raises:
120
- RuntimeError: If there is an error playing the audio.
121
- """
122
- try:
123
- playsound(filename)
124
- except Exception as e:
125
- self.logger.error(f"Error playing audio: {e} 🔇")
126
- raise RuntimeError(f"Error playing audio: {e}")
127
103
 
128
104
  # Example usage
129
105
  if __name__ == "__main__":
130
106
  elevenlabs = ElevenlabsTTS()
131
107
  text = "This is a test of the ElevenlabsTTS text-to-speech API. It supports multiple sentences and advanced logging."
132
108
 
133
- elevenlabs.logger.info("Generating audio...")
134
- audio_file = elevenlabs.tts(text, voice="Brian")
135
-
136
- elevenlabs.logger.info("Playing audio...")
137
- elevenlabs.play_audio(audio_file)
109
+ print("[debug] Generating audio...")
110
+ audio_file = elevenlabs.tts(text, voice="Brian")
111
+ print(f"Audio saved to: {audio_file}")
@@ -3,10 +3,8 @@ import requests
3
3
  import pathlib
4
4
  import base64
5
5
  from io import BytesIO
6
- from playsound import playsound
7
6
  from webscout import exceptions
8
7
  from webscout.AIbase import TTSProvider
9
- from webscout.Litlogger import Logger, LogFormat
10
8
  from webscout.litagent import LitAgent
11
9
  from concurrent.futures import ThreadPoolExecutor, as_completed
12
10
  from . import utils
@@ -37,11 +35,6 @@ class GesseritTTS(TTSProvider):
37
35
  if proxies:
38
36
  self.session.proxies.update(proxies)
39
37
  self.timeout = timeout
40
- self.logger = Logger(
41
- name="GesseritTTS",
42
- format=LogFormat.MODERN_EMOJI,
43
-
44
- )
45
38
 
46
39
  def tts(self, text: str, voice: str = "Oliver", verbose:bool = True) -> str:
47
40
  """Converts text to speech using the GesseritTTS API and saves it to a file."""
@@ -76,14 +69,14 @@ class GesseritTTS(TTSProvider):
76
69
  audio_base64 = data["audioUrl"].split(",")[1]
77
70
  audio_data = base64.b64decode(audio_base64)
78
71
  if verbose:
79
- self.logger.success(f"Chunk {part_number} processed successfully 🎉")
72
+ print(f"[debug] Chunk {part_number} processed successfully")
80
73
  return part_number, audio_data
81
74
  else:
82
75
  if verbose:
83
- self.logger.warning(f"No data received for chunk {part_number}. Retrying...")
76
+ print(f"[debug] No data received for chunk {part_number}. Retrying...")
84
77
  except requests.RequestException as e:
85
78
  if verbose:
86
- self.logger.error(f"Error for chunk {part_number}: {e}. Retrying... 🔄")
79
+ print(f"[debug] Error for chunk {part_number}: {e}. Retrying...")
87
80
  time.sleep(1)
88
81
  try:
89
82
  # Using ThreadPoolExecutor to handle requests concurrently
@@ -101,51 +94,34 @@ class GesseritTTS(TTSProvider):
101
94
  audio_chunks[part_number] = audio_data # Store the audio data in correct sequence
102
95
  except Exception as e:
103
96
  if verbose:
104
- self.logger.error(f"Failed to generate audio for chunk {chunk_num}: {e} 🚨")
97
+ print(f"[debug] Failed to generate audio for chunk {chunk_num}: {e}")
105
98
 
106
99
  # Combine audio chunks in the correct sequence
107
100
  combined_audio = BytesIO()
108
101
  for part_number in sorted(audio_chunks.keys()):
109
102
  combined_audio.write(audio_chunks[part_number])
110
103
  if verbose:
111
- self.logger.debug(f"Added chunk {part_number} to the combined file.")
104
+ print(f"[debug] Added chunk {part_number} to the combined file.")
112
105
 
113
106
  # Save the combined audio data to a single file
114
107
  with open(filename, 'wb') as f:
115
108
  f.write(combined_audio.getvalue())
116
109
  if verbose:
117
- self.logger.info(f"Final Audio Saved as {filename} 🔊")
110
+ print(f"[debug] Final Audio Saved as {filename}")
118
111
  return filename.as_posix()
119
112
 
120
113
  except requests.exceptions.RequestException as e:
121
- self.logger.critical(f"Failed to perform the operation: {e} 🚨")
114
+ if verbose:
115
+ print(f"[debug] Failed to perform the operation: {e}")
122
116
  raise exceptions.FailedToGenerateResponseError(
123
117
  f"Failed to perform the operation: {e}"
124
118
  )
125
-
126
- def play_audio(self, filename: str):
127
- """
128
- Plays an audio file using playsound.
129
-
130
- Args:
131
- filename (str): The path to the audio file.
132
-
133
- Raises:
134
- RuntimeError: If there is an error playing the audio.
135
- """
136
- try:
137
- playsound(filename)
138
- except Exception as e:
139
- self.logger.error(f"Error playing audio: {e} 🔇")
140
- raise RuntimeError(f"Error playing audio: {e}")
141
119
 
142
120
  # Example usage
143
121
  if __name__ == "__main__":
144
122
  gesserit = GesseritTTS()
145
123
  text = "This is a test of the GesseritTTS text-to-speech API. It supports multiple sentences and advanced logging."
146
124
 
147
- gesserit.logger.info("Generating audio...")
148
- audio_file = gesserit.tts(text, voice="Oliver")
149
-
150
- gesserit.logger.info("Playing audio...")
151
- gesserit.play_audio(audio_file)
125
+ print("[debug] Generating audio...")
126
+ audio_file = gesserit.tts(text, voice="Oliver")
127
+ print(f"Audio saved to: {audio_file}")
@@ -1,12 +1,11 @@
1
1
  import time
2
2
  import requests
3
3
  import pathlib
4
+ import tempfile
4
5
  from io import BytesIO
5
6
  from urllib.parse import urlencode
6
- from playsound import playsound
7
7
  from webscout import exceptions
8
8
  from webscout.AIbase import TTSProvider
9
- from webscout.Litlogger import Logger, LogFormat
10
9
  from webscout.litagent import LitAgent
11
10
  from concurrent.futures import ThreadPoolExecutor, as_completed
12
11
  from . import utils
@@ -17,7 +16,6 @@ class MurfAITTS(TTSProvider):
17
16
  headers: dict[str, str] = {
18
17
  "User-Agent": LitAgent().random()
19
18
  }
20
- cache_dir = pathlib.Path("./audio_cache")
21
19
  all_voices: dict[str, str] = {"Hazel": "en-UK-hazel"}
22
20
 
23
21
  def __init__(self, timeout: int = 20, proxies: dict = None):
@@ -27,11 +25,7 @@ class MurfAITTS(TTSProvider):
27
25
  if proxies:
28
26
  self.session.proxies.update(proxies)
29
27
  self.timeout = timeout
30
- self.logger = Logger(
31
- name="MurfAITTS",
32
- format=LogFormat.MODERN_EMOJI,
33
-
34
- )
28
+ self.temp_dir = tempfile.mkdtemp(prefix="webscout_tts_")
35
29
 
36
30
  def tts(self, text: str, voice: str = "Hazel", verbose:bool = True) -> str:
37
31
  """Converts text to speech using the MurfAITTS API and saves it to a file."""
@@ -39,7 +33,7 @@ class MurfAITTS(TTSProvider):
39
33
  voice in self.all_voices
40
34
  ), f"Voice '{voice}' not one of [{', '.join(self.all_voices.keys())}]"
41
35
 
42
- filename = self.cache_dir / f"{int(time.time())}.mp3"
36
+ filename = pathlib.Path(tempfile.mktemp(suffix=".mp3", dir=self.temp_dir))
43
37
 
44
38
  voice_id = self.all_voices[voice]
45
39
 
@@ -58,20 +52,17 @@ class MurfAITTS(TTSProvider):
58
52
  response = self.session.get(f"https://murf.ai/Prod/anonymous-tts/audio?{encode_param}", headers=self.headers, timeout=self.timeout)
59
53
  response.raise_for_status()
60
54
 
61
- # Create the audio_cache directory if it doesn't exist
62
- self.cache_dir.mkdir(parents=True, exist_ok=True)
63
-
64
55
  # Check if the request was successful
65
56
  if response.ok and response.status_code == 200:
66
57
  if verbose:
67
- self.logger.success(f"Chunk {part_number} processed successfully 🎉")
58
+ print(f"[debug] Chunk {part_number} processed successfully")
68
59
  return part_number, response.content
69
60
  else:
70
61
  if verbose:
71
- self.logger.warning(f"No data received for chunk {part_number}. Retrying...")
62
+ print(f"[debug] No data received for chunk {part_number}. Retrying...")
72
63
  except requests.RequestException as e:
73
64
  if verbose:
74
- self.logger.error(f"Error for chunk {part_number}: {e}. Retrying... 🔄")
65
+ print(f"[debug] Error for chunk {part_number}: {e}. Retrying...")
75
66
  time.sleep(1)
76
67
  try:
77
68
  # Using ThreadPoolExecutor to handle requests concurrently
@@ -89,51 +80,34 @@ class MurfAITTS(TTSProvider):
89
80
  audio_chunks[part_number] = audio_data # Store the audio data in correct sequence
90
81
  except Exception as e:
91
82
  if verbose:
92
- self.logger.error(f"Failed to generate audio for chunk {chunk_num}: {e} 🚨")
83
+ print(f"[debug] Failed to generate audio for chunk {chunk_num}: {e}")
93
84
 
94
85
  # Combine audio chunks in the correct sequence
95
86
  combined_audio = BytesIO()
96
87
  for part_number in sorted(audio_chunks.keys()):
97
88
  combined_audio.write(audio_chunks[part_number])
98
89
  if verbose:
99
- self.logger.debug(f"Added chunk {part_number} to the combined file.")
90
+ print(f"[debug] Added chunk {part_number} to the combined file.")
100
91
 
101
92
  # Save the combined audio data to a single file
102
93
  with open(filename, 'wb') as f:
103
94
  f.write(combined_audio.getvalue())
104
95
  if verbose:
105
- self.logger.info(f"Final Audio Saved as {filename} 🔊")
96
+ print(f"[debug] Final Audio Saved as {filename}")
106
97
  return filename.as_posix()
107
98
 
108
99
  except requests.exceptions.RequestException as e:
109
- self.logger.critical(f"Failed to perform the operation: {e} 🚨")
100
+ if verbose:
101
+ print(f"[debug] Failed to perform the operation: {e}")
110
102
  raise exceptions.FailedToGenerateResponseError(
111
103
  f"Failed to perform the operation: {e}"
112
104
  )
113
-
114
- def play_audio(self, filename: str):
115
- """
116
- Plays an audio file using playsound.
117
-
118
- Args:
119
- filename (str): The path to the audio file.
120
-
121
- Raises:
122
- RuntimeError: If there is an error playing the audio.
123
- """
124
- try:
125
- playsound(filename)
126
- except Exception as e:
127
- self.logger.error(f"Error playing audio: {e} 🔇")
128
- raise RuntimeError(f"Error playing audio: {e}")
129
105
 
130
106
  # Example usage
131
107
  if __name__ == "__main__":
132
108
  murfai = MurfAITTS()
133
109
  text = "This is a test of the MurfAITTS text-to-speech API. It supports multiple sentences and advanced logging."
134
110
 
135
- murfai.logger.info("Generating audio...")
111
+ print("[debug] Generating audio...")
136
112
  audio_file = murfai.tts(text, voice="Hazel")
137
-
138
- murfai.logger.info("Playing audio...")
139
- murfai.play_audio(audio_file)
113
+ print(f"Audio saved to: {audio_file}")
@@ -1,10 +1,8 @@
1
1
  import time
2
+ import tempfile
2
3
  from pathlib import Path
3
- from typing import Generator
4
- from playsound import playsound
5
4
  from webscout import exceptions
6
5
  from webscout.AIbase import TTSProvider
7
- from webscout.Litlogger import Logger, LogFormat
8
6
  from gradio_client import Client
9
7
  import os
10
8
 
@@ -19,11 +17,7 @@ class ParlerTTS(TTSProvider):
19
17
  self.api_endpoint = "/gen_tts"
20
18
  self.client = Client("parler-tts/parler_tts") # Initialize the Gradio client
21
19
  self.timeout = timeout
22
- self.audio_cache_dir = Path("./audio_cache")
23
- self.logger = Logger(
24
- name="ParlerTTS",
25
- format=LogFormat.MODERN_EMOJI,
26
- )
20
+ self.temp_dir = tempfile.mkdtemp(prefix="webscout_tts_")
27
21
 
28
22
  def tts(self, text: str, description: str = "", use_large: bool = False, verbose: bool = True) -> str:
29
23
  """
@@ -41,11 +35,11 @@ class ParlerTTS(TTSProvider):
41
35
  Raises:
42
36
  exceptions.FailedToGenerateResponseError: If there is an error generating or saving the audio.
43
37
  """
44
- filename = self.audio_cache_dir / f"{int(time.time())}.wav"
38
+ filename = Path(tempfile.mktemp(suffix=".wav", dir=self.temp_dir))
45
39
 
46
40
  try:
47
41
  if verbose:
48
- self.logger.info(f"Generating TTS with description: {description} 🎙️")
42
+ print(f"[debug] Generating TTS with description: {description}")
49
43
 
50
44
  result = self.client.predict(
51
45
  text=text,
@@ -62,58 +56,43 @@ class ParlerTTS(TTSProvider):
62
56
  else:
63
57
  raise ValueError(f"Unexpected response from API: {result}")
64
58
 
65
- self._save_audio(audio_bytes, filename)
59
+ self._save_audio(audio_bytes, filename, verbose)
66
60
 
67
61
  if verbose:
68
- self.logger.success(f"Audio generated successfully: {filename} 🔊")
62
+ print(f"[debug] Audio generated successfully: {filename}")
69
63
 
70
64
  return filename.as_posix()
71
65
 
72
66
  except Exception as e:
73
- self.logger.critical(f"Error generating audio: {e} 🚨")
67
+ if verbose:
68
+ print(f"[debug] Error generating audio: {e}")
74
69
  raise exceptions.FailedToGenerateResponseError(
75
70
  f"Error generating audio after multiple retries: {e}"
76
71
  ) from e
77
72
 
78
- def _save_audio(self, audio_data: bytes, filename: Path):
73
+ def _save_audio(self, audio_data: bytes, filename: Path, verbose: bool = True):
79
74
  """
80
- Saves the audio data to a WAV file in the audio cache directory.
75
+ Saves the audio data to a WAV file.
81
76
 
82
77
  Args:
83
78
  audio_data (bytes): Audio data to save
84
79
  filename (Path): Path to save the audio file
80
+ verbose (bool): Whether to print debug information
85
81
 
86
82
  Raises:
87
83
  exceptions.FailedToGenerateResponseError: If there is an error saving the audio.
88
84
  """
89
85
  try:
90
- self.audio_cache_dir.mkdir(parents=True, exist_ok=True)
91
86
  with open(filename, "wb") as f:
92
87
  f.write(audio_data)
93
- self.logger.debug(f"Audio saved to {filename} 💾")
88
+ if verbose:
89
+ print(f"[debug] Audio saved to {filename}")
94
90
 
95
91
  except Exception as e:
96
- self.logger.error(f"Error saving audio: {e} 🔇")
92
+ if verbose:
93
+ print(f"[debug] Error saving audio: {e}")
97
94
  raise exceptions.FailedToGenerateResponseError(f"Error saving audio: {e}")
98
95
 
99
- def play_audio(self, filename: str):
100
- """
101
- Plays an audio file using playsound.
102
-
103
- Args:
104
- filename (str): The path to the audio file.
105
-
106
- Raises:
107
- RuntimeError: If there is an error playing the audio.
108
- """
109
- try:
110
- self.logger.info(f"Playing audio: {filename} 🎵")
111
- playsound(filename)
112
- except Exception as e:
113
- self.logger.error(f"Error playing audio: {e} 🔇")
114
- raise RuntimeError(f"Error playing audio: {e}")
115
-
116
-
117
96
  # Example usage
118
97
  if __name__ == "__main__":
119
98
  parlertts = ParlerTTS()
@@ -127,8 +106,6 @@ if __name__ == "__main__":
127
106
  "recording that almost has no background noise."
128
107
  )
129
108
 
130
- parlertts.logger.info("Generating audio...")
109
+ print("[debug] Generating audio...")
131
110
  audio_file = parlertts.tts(text, description=voice_description, use_large=False)
132
-
133
- parlertts.logger.info("Playing audio...")
134
- parlertts.play_audio(audio_file)
111
+ print(f"Audio saved to: {audio_file}")
@@ -0,0 +1,180 @@
1
+ ##################################################################################
2
+ ## Modified version of code written by t.me/infip1217 ##
3
+ ##################################################################################
4
+ import time
5
+ import requests
6
+ import pathlib
7
+ import tempfile
8
+ from io import BytesIO
9
+ from webscout import exceptions
10
+ from webscout.AIbase import TTSProvider
11
+ from webscout.litagent import LitAgent
12
+ from concurrent.futures import ThreadPoolExecutor, as_completed
13
+ from . import utils
14
+
15
+ class SpeechMaTTS(TTSProvider):
16
+ """
17
+ Text-to-speech provider using the SpeechMa API.
18
+ """
19
+ # Request headers
20
+ headers = {
21
+ "accept": "*/*",
22
+ "accept-language": "en-IN,en-GB;q=0.9,en-US;q=0.8,en;q=0.7,en-AU;q=0.6",
23
+ "content-type": "application/json",
24
+ "origin": "https://speechma.com",
25
+ "priority": "u=1, i",
26
+ "User-Agent": LitAgent().random()
27
+ }
28
+
29
+ # Available voices with their IDs
30
+ all_voices = {
31
+ "Ava": "voice-110", # Multilingual female voice
32
+ "Emma": "voice-115", # Multilingual female voice
33
+ "Andrew": "voice-107", # Multilingual male voice
34
+ "Brian": "voice-112" # Multilingual male voice
35
+ }
36
+
37
+ def __init__(self, timeout: int = 20, proxies: dict = None):
38
+ """Initializes the SpeechMa TTS client."""
39
+ self.api_url = "https://speechma.com/com.api/tts-api.php"
40
+ self.session = requests.Session()
41
+ self.session.headers.update(self.headers)
42
+ if proxies:
43
+ self.session.proxies.update(proxies)
44
+ self.timeout = timeout
45
+ self.temp_dir = tempfile.mkdtemp(prefix="webscout_tts_")
46
+
47
+ def tts(self, text: str, voice: str = "Emma", pitch: int = 0, rate: int = 0, verbose: bool = False) -> str:
48
+ """
49
+ Converts text to speech using the SpeechMa API and saves it to a file.
50
+
51
+ Args:
52
+ text (str): The text to convert to speech
53
+ voice (str): The voice to use for TTS (default: "Emma")
54
+ pitch (int): Voice pitch adjustment (-10 to 10, default: 0)
55
+ rate (int): Voice rate/speed adjustment (-10 to 10, default: 0)
56
+ verbose (bool): Whether to print progress messages (default: True)
57
+
58
+ Returns:
59
+ str: Path to the generated audio file
60
+
61
+ Raises:
62
+ exceptions.FailedToGenerateResponseError: If there is an error generating or saving the audio.
63
+ """
64
+ assert (
65
+ voice in self.all_voices
66
+ ), f"Voice '{voice}' not one of [{', '.join(self.all_voices.keys())}]"
67
+
68
+ filename = pathlib.Path(tempfile.mktemp(suffix=".mp3", dir=self.temp_dir))
69
+
70
+ # Get the voice ID
71
+ voice_id = self.all_voices[voice]
72
+
73
+ if verbose:
74
+ print(f"[debug] Using voice: {voice} (ID: {voice_id})")
75
+
76
+ # Split text into sentences for better processing
77
+ sentences = utils.split_sentences(text)
78
+
79
+ if verbose:
80
+ print(f"[debug] Text split into {len(sentences)} sentences")
81
+
82
+ # Function to request audio for each chunk
83
+ def generate_audio_for_chunk(part_text: str, part_number: int):
84
+ while True:
85
+ try:
86
+ if verbose:
87
+ print(f"[debug] Processing chunk {part_number}: '{part_text[:30]}...'")
88
+
89
+ # Prepare payload for this sentence
90
+ payload = {
91
+ "text": part_text,
92
+ "voice": voice_id,
93
+ "pitch": pitch,
94
+ "rate": rate
95
+ }
96
+
97
+ response = self.session.post(
98
+ self.api_url,
99
+ headers=self.headers,
100
+ json=payload,
101
+ timeout=self.timeout
102
+ )
103
+ response.raise_for_status()
104
+
105
+ # Check if the request was successful
106
+ if response.ok and response.status_code == 200:
107
+ # Ensure we got audio data
108
+ if len(response.content) > 100: # Basic check for actual content
109
+ if verbose:
110
+ print(f"[debug] Chunk {part_number} processed successfully")
111
+ return part_number, response.content
112
+ else:
113
+ if verbose:
114
+ print(f"[debug] Received too small response for chunk {part_number}. Retrying...")
115
+ else:
116
+ if verbose:
117
+ print(f"[debug] Failed request for chunk {part_number} (status code: {response.status_code}). Retrying...")
118
+
119
+ # If we get here, something went wrong with the request
120
+ time.sleep(1)
121
+ except requests.RequestException as e:
122
+ if verbose:
123
+ print(f"[debug] Error for chunk {part_number}: {e}. Retrying...")
124
+ time.sleep(1)
125
+
126
+ try:
127
+ if verbose:
128
+ print(f"[debug] Starting concurrent audio generation for {len(sentences)} chunks")
129
+
130
+ # Using ThreadPoolExecutor to handle requests concurrently
131
+ with ThreadPoolExecutor() as executor:
132
+ futures = {executor.submit(generate_audio_for_chunk, sentence.strip(), chunk_num): chunk_num
133
+ for chunk_num, sentence in enumerate(sentences, start=1)}
134
+
135
+ # Dictionary to store results with order preserved
136
+ audio_chunks = {}
137
+
138
+ for future in as_completed(futures):
139
+ chunk_num = futures[future]
140
+ try:
141
+ part_number, audio_data = future.result()
142
+ audio_chunks[part_number] = audio_data # Store the audio data in correct sequence
143
+ if verbose:
144
+ print(f"[debug] Received audio data for chunk {part_number}")
145
+ except Exception as e:
146
+ if verbose:
147
+ print(f"[debug] Failed to generate audio for chunk {chunk_num}: {e}")
148
+
149
+ if verbose:
150
+ print(f"[debug] Successfully processed {len(audio_chunks)}/{len(sentences)} chunks")
151
+
152
+ # Combine audio chunks in the correct sequence
153
+ combined_audio = BytesIO()
154
+ for part_number in sorted(audio_chunks.keys()):
155
+ combined_audio.write(audio_chunks[part_number])
156
+ if verbose:
157
+ print(f"[debug] Added chunk {part_number} to the combined file")
158
+
159
+ # Save the combined audio data to a single file
160
+ with open(filename, 'wb') as f:
161
+ f.write(combined_audio.getvalue())
162
+
163
+ if verbose:
164
+ print(f"[debug] Final audio saved as {filename}")
165
+
166
+ return filename.as_posix()
167
+
168
+ except requests.exceptions.RequestException as e:
169
+ if verbose:
170
+ print(f"[debug] Failed to perform the operation: {e}")
171
+ raise exceptions.FailedToGenerateResponseError(
172
+ f"Failed to perform the operation: {e}"
173
+ )
174
+
175
+ # Example usage
176
+ if __name__ == "__main__":
177
+ speechma = SpeechMaTTS()
178
+ text = "This is a test of the SpeechMa text-to-speech API. It supports multiple sentences."
179
+ audio_file = speechma.tts(text, voice="Emma")
180
+ print(f"Audio saved to: {audio_file}")