webscout 7.5__py3-none-any.whl → 7.6__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 (118) 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/autocoder_utiles.py +0 -4
  9. webscout/Extra/autocoder/rawdog.py +13 -41
  10. webscout/Extra/gguf.py +652 -428
  11. webscout/Extra/weather.py +178 -156
  12. webscout/Extra/weather_ascii.py +70 -17
  13. webscout/Litlogger/core/logger.py +1 -2
  14. webscout/Litlogger/handlers/file.py +1 -1
  15. webscout/Litlogger/styles/formats.py +0 -2
  16. webscout/Litlogger/utils/detectors.py +0 -1
  17. webscout/Provider/AISEARCH/DeepFind.py +0 -1
  18. webscout/Provider/AISEARCH/ISou.py +1 -1
  19. webscout/Provider/AISEARCH/felo_search.py +0 -1
  20. webscout/Provider/AllenAI.py +24 -9
  21. webscout/Provider/C4ai.py +29 -11
  22. webscout/Provider/ChatGPTGratis.py +24 -56
  23. webscout/Provider/DeepSeek.py +25 -17
  24. webscout/Provider/Deepinfra.py +115 -48
  25. webscout/Provider/Gemini.py +1 -1
  26. webscout/Provider/Glider.py +25 -8
  27. webscout/Provider/HF_space/qwen_qwen2.py +2 -2
  28. webscout/Provider/HeckAI.py +23 -7
  29. webscout/Provider/Jadve.py +20 -5
  30. webscout/Provider/Netwrck.py +42 -19
  31. webscout/Provider/PI.py +4 -2
  32. webscout/Provider/Perplexitylabs.py +26 -6
  33. webscout/Provider/PizzaGPT.py +10 -51
  34. webscout/Provider/TTI/AiForce/async_aiforce.py +4 -37
  35. webscout/Provider/TTI/AiForce/sync_aiforce.py +41 -38
  36. webscout/Provider/TTI/FreeAIPlayground/__init__.py +9 -9
  37. webscout/Provider/TTI/FreeAIPlayground/async_freeaiplayground.py +206 -206
  38. webscout/Provider/TTI/FreeAIPlayground/sync_freeaiplayground.py +192 -192
  39. webscout/Provider/TTI/MagicStudio/__init__.py +2 -0
  40. webscout/Provider/TTI/MagicStudio/async_magicstudio.py +111 -0
  41. webscout/Provider/TTI/MagicStudio/sync_magicstudio.py +109 -0
  42. webscout/Provider/TTI/PollinationsAI/async_pollinations.py +5 -24
  43. webscout/Provider/TTI/PollinationsAI/sync_pollinations.py +2 -22
  44. webscout/Provider/TTI/__init__.py +2 -3
  45. webscout/Provider/TTI/aiarta/async_aiarta.py +14 -14
  46. webscout/Provider/TTI/aiarta/sync_aiarta.py +52 -21
  47. webscout/Provider/TTI/fastflux/__init__.py +22 -0
  48. webscout/Provider/TTI/fastflux/async_fastflux.py +257 -0
  49. webscout/Provider/TTI/fastflux/sync_fastflux.py +247 -0
  50. webscout/Provider/TTS/__init__.py +2 -2
  51. webscout/Provider/TTS/deepgram.py +12 -39
  52. webscout/Provider/TTS/elevenlabs.py +14 -40
  53. webscout/Provider/TTS/gesserit.py +11 -35
  54. webscout/Provider/TTS/murfai.py +13 -39
  55. webscout/Provider/TTS/parler.py +17 -40
  56. webscout/Provider/TTS/speechma.py +180 -0
  57. webscout/Provider/TTS/streamElements.py +17 -44
  58. webscout/Provider/TextPollinationsAI.py +39 -59
  59. webscout/Provider/Venice.py +25 -8
  60. webscout/Provider/WiseCat.py +27 -5
  61. webscout/Provider/Youchat.py +64 -37
  62. webscout/Provider/__init__.py +0 -6
  63. webscout/Provider/akashgpt.py +20 -5
  64. webscout/Provider/flowith.py +20 -5
  65. webscout/Provider/freeaichat.py +32 -45
  66. webscout/Provider/koala.py +20 -5
  67. webscout/Provider/llamatutor.py +1 -1
  68. webscout/Provider/llmchat.py +30 -8
  69. webscout/Provider/multichat.py +65 -9
  70. webscout/Provider/talkai.py +1 -0
  71. webscout/Provider/turboseek.py +3 -0
  72. webscout/Provider/tutorai.py +2 -0
  73. webscout/Provider/typegpt.py +154 -64
  74. webscout/Provider/x0gpt.py +3 -1
  75. webscout/Provider/yep.py +102 -20
  76. webscout/__init__.py +3 -0
  77. webscout/cli.py +4 -40
  78. webscout/conversation.py +1 -10
  79. webscout/litagent/__init__.py +2 -2
  80. webscout/litagent/agent.py +351 -20
  81. webscout/litagent/constants.py +34 -5
  82. webscout/litprinter/__init__.py +0 -3
  83. webscout/models.py +181 -0
  84. webscout/optimizers.py +1 -1
  85. webscout/prompt_manager.py +2 -8
  86. webscout/scout/core/scout.py +1 -4
  87. webscout/scout/core/search_result.py +1 -1
  88. webscout/scout/core/text_utils.py +1 -1
  89. webscout/scout/core.py +2 -5
  90. webscout/scout/element.py +1 -1
  91. webscout/scout/parsers/html_parser.py +1 -1
  92. webscout/scout/utils.py +0 -1
  93. webscout/swiftcli/__init__.py +1 -3
  94. webscout/tempid.py +1 -1
  95. webscout/update_checker.py +1 -3
  96. webscout/version.py +1 -1
  97. webscout/webscout_search_async.py +1 -2
  98. webscout/yep_search.py +297 -297
  99. {webscout-7.5.dist-info → webscout-7.6.dist-info}/LICENSE.md +4 -4
  100. {webscout-7.5.dist-info → webscout-7.6.dist-info}/METADATA +101 -390
  101. {webscout-7.5.dist-info → webscout-7.6.dist-info}/RECORD +104 -110
  102. webscout/Extra/autollama.py +0 -231
  103. webscout/Provider/Amigo.py +0 -274
  104. webscout/Provider/Bing.py +0 -243
  105. webscout/Provider/DiscordRocks.py +0 -253
  106. webscout/Provider/TTI/blackbox/__init__.py +0 -4
  107. webscout/Provider/TTI/blackbox/async_blackbox.py +0 -212
  108. webscout/Provider/TTI/blackbox/sync_blackbox.py +0 -199
  109. webscout/Provider/TTI/deepinfra/__init__.py +0 -4
  110. webscout/Provider/TTI/deepinfra/async_deepinfra.py +0 -227
  111. webscout/Provider/TTI/deepinfra/sync_deepinfra.py +0 -199
  112. webscout/Provider/TTI/imgninza/__init__.py +0 -4
  113. webscout/Provider/TTI/imgninza/async_ninza.py +0 -214
  114. webscout/Provider/TTI/imgninza/sync_ninza.py +0 -209
  115. webscout/Provider/TTS/voicepod.py +0 -117
  116. {webscout-7.5.dist-info → webscout-7.6.dist-info}/WHEEL +0 -0
  117. {webscout-7.5.dist-info → webscout-7.6.dist-info}/entry_points.txt +0 -0
  118. {webscout-7.5.dist-info → webscout-7.6.dist-info}/top_level.txt +0 -0
@@ -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}")
@@ -2,15 +2,14 @@ import time
2
2
  import requests
3
3
  import pathlib
4
4
  import urllib.parse
5
- from typing import Union, Generator
6
- from playsound import playsound
5
+ import tempfile
6
+ from typing import Union
7
+ from io import BytesIO
7
8
  from webscout import exceptions
8
9
  from webscout.AIbase import TTSProvider
9
- from webscout.Litlogger import Logger, LogFormat
10
10
  from webscout.litagent import LitAgent
11
- from . import utils
12
11
  from concurrent.futures import ThreadPoolExecutor, as_completed
13
- from io import BytesIO
12
+ from . import utils
14
13
 
15
14
  class StreamElements(TTSProvider):
16
15
  """
@@ -21,7 +20,6 @@ class StreamElements(TTSProvider):
21
20
  headers: dict[str, str] = {
22
21
  "User-Agent": LitAgent().random()
23
22
  }
24
- cache_dir = pathlib.Path("./audio_cache")
25
23
  all_voices: list[str] = [
26
24
  "Filiz",
27
25
  "Astrid",
@@ -238,11 +236,7 @@ class StreamElements(TTSProvider):
238
236
  if proxies:
239
237
  self.session.proxies.update(proxies)
240
238
  self.timeout = timeout
241
- self.logger = Logger(
242
- name="StreamElementsTTS",
243
- format=LogFormat.MODERN_EMOJI,
244
-
245
- )
239
+ self.temp_dir = tempfile.mkdtemp(prefix="webscout_tts_")
246
240
 
247
241
  def tts(self, text: str, voice: str = "Mathieu", verbose: bool = True) -> str:
248
242
  """
@@ -260,7 +254,7 @@ class StreamElements(TTSProvider):
260
254
  voice in self.all_voices
261
255
  ), f"Voice '{voice}' not one of [{', '.join(self.all_voices)}]"
262
256
 
263
- filename = self.cache_dir / f"{int(time.time())}.mp3"
257
+ filename = pathlib.Path(tempfile.mktemp(suffix=".mp3", dir=self.temp_dir))
264
258
 
265
259
  # Split text into sentences
266
260
  sentences = utils.split_sentences(text)
@@ -278,20 +272,17 @@ class StreamElements(TTSProvider):
278
272
  response = self.session.get(url, headers=self.headers, timeout=self.timeout)
279
273
  response.raise_for_status()
280
274
 
281
- # Create the audio_cache directory if it doesn't exist
282
- self.cache_dir.mkdir(parents=True, exist_ok=True)
283
-
284
275
  # Check if the request was successful
285
276
  if response.ok and response.status_code == 200:
286
277
  if verbose:
287
- self.logger.success(f"Chunk {part_number} processed successfully 🎉")
278
+ print(f"[debug] Chunk {part_number} processed successfully")
288
279
  return part_number, response.content
289
280
  else:
290
281
  if verbose:
291
- self.logger.warning(f"No data received for chunk {part_number}. Retrying...")
282
+ print(f"[debug] No data received for chunk {part_number}. Retrying...")
292
283
  except requests.RequestException as e:
293
284
  if verbose:
294
- self.logger.error(f"Error for chunk {part_number}: {e}. Retrying... 🔄")
285
+ print(f"[debug] Error for chunk {part_number}: {e}. Retrying...")
295
286
  time.sleep(1)
296
287
  try:
297
288
  # Using ThreadPoolExecutor to handle requests concurrently
@@ -309,52 +300,34 @@ class StreamElements(TTSProvider):
309
300
  audio_chunks[part_number] = audio_data # Store the audio data in correct sequence
310
301
  except Exception as e:
311
302
  if verbose:
312
- self.logger.error(f"Failed to generate audio for chunk {chunk_num}: {e} 🚨")
303
+ print(f"[debug] Failed to generate audio for chunk {chunk_num}: {e}")
313
304
 
314
305
  # Combine audio chunks in the correct sequence
315
306
  combined_audio = BytesIO()
316
307
  for part_number in sorted(audio_chunks.keys()):
317
308
  combined_audio.write(audio_chunks[part_number])
318
309
  if verbose:
319
- self.logger.debug(f"Added chunk {part_number} to the combined file.")
310
+ print(f"[debug] Added chunk {part_number} to the combined file.")
320
311
 
321
312
  # Save the combined audio data to a single file
322
313
  with open(filename, 'wb') as f:
323
314
  f.write(combined_audio.getvalue())
324
315
  if verbose:
325
- self.logger.info(f"Final Audio Saved as {filename} 🔊")
316
+ print(f"[debug] Final Audio Saved as {filename}")
326
317
  return filename.as_posix()
327
318
 
328
319
  except requests.exceptions.RequestException as e:
329
- self.logger.critical(f"Failed to perform the operation: {e} 🚨")
320
+ if verbose:
321
+ print(f"[debug] Failed to perform the operation: {e}")
330
322
  raise exceptions.FailedToGenerateResponseError(
331
323
  f"Failed to perform the operation: {e}"
332
324
  )
333
-
334
- def play_audio(self, filename: str):
335
- """
336
- Plays an audio file using playsound.
337
-
338
- Args:
339
- filename (str): The path to the audio file.
340
-
341
- Raises:
342
- RuntimeError: If there is an error playing the audio.
343
- """
344
- try:
345
- self.logger.info(f"Playing audio: {filename} 🎵")
346
- playsound(filename)
347
- except Exception as e:
348
- self.logger.error(f"Error playing audio: {e} 🔇")
349
- raise RuntimeError(f"Error playing audio: {e}")
350
325
 
351
326
  # Example usage
352
327
  if __name__ == "__main__":
353
328
  streamelements = StreamElements()
354
329
  text = "This is a test of the StreamElements text-to-speech API. It supports multiple sentences and advanced logging."
355
330
 
356
- streamelements.logger.info("Generating audio...")
357
- audio_file = streamelements.tts(text, voice="Mathieu")
358
-
359
- streamelements.logger.info("Playing audio...")
360
- streamelements.play_audio(audio_file)
331
+ print("[debug] Generating audio...")
332
+ audio_file = streamelements.tts(text, voice="Mathieu")
333
+ print(f"Audio saved to: {audio_file}")