webscout 8.3.1__py3-none-any.whl → 8.3.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.
- webscout/AIutel.py +46 -53
- webscout/Bing_search.py +418 -0
- webscout/Extra/gguf.py +706 -177
- webscout/Provider/AISEARCH/genspark_search.py +7 -7
- webscout/Provider/GeminiProxy.py +140 -0
- webscout/Provider/MCPCore.py +78 -75
- webscout/Provider/OPENAI/BLACKBOXAI.py +1 -4
- webscout/Provider/OPENAI/GeminiProxy.py +328 -0
- webscout/Provider/OPENAI/README.md +2 -0
- webscout/Provider/OPENAI/README_AUTOPROXY.md +238 -0
- webscout/Provider/OPENAI/__init__.py +15 -1
- webscout/Provider/OPENAI/autoproxy.py +332 -39
- webscout/Provider/OPENAI/base.py +15 -5
- webscout/Provider/OPENAI/e2b.py +0 -1
- webscout/Provider/OPENAI/mcpcore.py +109 -70
- webscout/Provider/OPENAI/scirachat.py +59 -51
- webscout/Provider/OPENAI/toolbaz.py +2 -9
- webscout/Provider/OPENAI/xenai.py +514 -0
- webscout/Provider/OPENAI/yep.py +8 -2
- webscout/Provider/TTI/__init__.py +1 -0
- webscout/Provider/TTI/bing.py +231 -0
- webscout/Provider/TTS/speechma.py +45 -39
- webscout/Provider/TogetherAI.py +366 -0
- webscout/Provider/XenAI.py +324 -0
- webscout/Provider/__init__.py +8 -3
- webscout/Provider/deepseek_assistant.py +378 -0
- webscout/auth/__init__.py +44 -0
- webscout/auth/api_key_manager.py +189 -0
- webscout/auth/auth_system.py +100 -0
- webscout/auth/config.py +76 -0
- webscout/auth/database.py +400 -0
- webscout/auth/exceptions.py +67 -0
- webscout/auth/middleware.py +248 -0
- webscout/auth/models.py +130 -0
- webscout/auth/providers.py +257 -0
- webscout/auth/rate_limiter.py +254 -0
- webscout/auth/request_models.py +127 -0
- webscout/auth/request_processing.py +226 -0
- webscout/auth/routes.py +526 -0
- webscout/auth/schemas.py +103 -0
- webscout/auth/server.py +312 -0
- webscout/auth/static/favicon.svg +11 -0
- webscout/auth/swagger_ui.py +203 -0
- webscout/auth/templates/components/authentication.html +237 -0
- webscout/auth/templates/components/base.html +103 -0
- webscout/auth/templates/components/endpoints.html +750 -0
- webscout/auth/templates/components/examples.html +491 -0
- webscout/auth/templates/components/footer.html +75 -0
- webscout/auth/templates/components/header.html +27 -0
- webscout/auth/templates/components/models.html +286 -0
- webscout/auth/templates/components/navigation.html +70 -0
- webscout/auth/templates/static/api.js +455 -0
- webscout/auth/templates/static/icons.js +168 -0
- webscout/auth/templates/static/main.js +784 -0
- webscout/auth/templates/static/particles.js +201 -0
- webscout/auth/templates/static/styles.css +3353 -0
- webscout/auth/templates/static/ui.js +374 -0
- webscout/auth/templates/swagger_ui.html +170 -0
- webscout/client.py +49 -3
- webscout/scout/core/scout.py +104 -26
- webscout/scout/element.py +139 -18
- webscout/swiftcli/core/cli.py +14 -3
- webscout/swiftcli/decorators/output.py +59 -9
- webscout/update_checker.py +31 -49
- webscout/version.py +1 -1
- webscout/webscout_search.py +4 -12
- webscout/webscout_search_async.py +3 -10
- webscout/yep_search.py +2 -11
- {webscout-8.3.1.dist-info → webscout-8.3.2.dist-info}/METADATA +41 -11
- {webscout-8.3.1.dist-info → webscout-8.3.2.dist-info}/RECORD +74 -36
- {webscout-8.3.1.dist-info → webscout-8.3.2.dist-info}/entry_points.txt +1 -1
- webscout/Provider/HF_space/__init__.py +0 -0
- webscout/Provider/HF_space/qwen_qwen2.py +0 -206
- webscout/Provider/OPENAI/api.py +0 -1320
- {webscout-8.3.1.dist-info → webscout-8.3.2.dist-info}/WHEEL +0 -0
- {webscout-8.3.1.dist-info → webscout-8.3.2.dist-info}/licenses/LICENSE.md +0 -0
- {webscout-8.3.1.dist-info → webscout-8.3.2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
import time
|
|
3
|
+
import tempfile
|
|
4
|
+
import os
|
|
5
|
+
from typing import Optional
|
|
6
|
+
from webscout.Provider.TTI.utils import ImageData, ImageResponse
|
|
7
|
+
from webscout.Provider.TTI.base import TTICompatibleProvider, BaseImages
|
|
8
|
+
from io import BytesIO
|
|
9
|
+
from webscout.litagent import LitAgent
|
|
10
|
+
|
|
11
|
+
try:
|
|
12
|
+
from PIL import Image
|
|
13
|
+
except ImportError:
|
|
14
|
+
Image = None
|
|
15
|
+
|
|
16
|
+
class Images(BaseImages):
|
|
17
|
+
def __init__(self, client):
|
|
18
|
+
self._client = client
|
|
19
|
+
|
|
20
|
+
def create(
|
|
21
|
+
self,
|
|
22
|
+
*,
|
|
23
|
+
model: str = "bing",
|
|
24
|
+
prompt: str,
|
|
25
|
+
n: int = 1,
|
|
26
|
+
size: str = "1024x1024",
|
|
27
|
+
response_format: str = "url",
|
|
28
|
+
user: Optional[str] = None,
|
|
29
|
+
style: str = "none",
|
|
30
|
+
aspect_ratio: str = "1:1",
|
|
31
|
+
timeout: int = 60,
|
|
32
|
+
image_format: str = "png",
|
|
33
|
+
seed: Optional[int] = None,
|
|
34
|
+
**kwargs
|
|
35
|
+
) -> ImageResponse:
|
|
36
|
+
if not prompt:
|
|
37
|
+
raise ValueError("Parameter 'prompt' is required")
|
|
38
|
+
if Image is None:
|
|
39
|
+
raise ImportError("Pillow (PIL) is required for image format conversion.")
|
|
40
|
+
agent = LitAgent()
|
|
41
|
+
session = self._client.session
|
|
42
|
+
headers = self._client.headers
|
|
43
|
+
images = []
|
|
44
|
+
urls = []
|
|
45
|
+
for _ in range(n):
|
|
46
|
+
data = {
|
|
47
|
+
"q": prompt,
|
|
48
|
+
"rt": "4",
|
|
49
|
+
"FORM": "GENCRE"
|
|
50
|
+
}
|
|
51
|
+
response = session.post(
|
|
52
|
+
"https://www.bing.com/images/create",
|
|
53
|
+
data=data,
|
|
54
|
+
headers=headers,
|
|
55
|
+
allow_redirects=False,
|
|
56
|
+
timeout=timeout
|
|
57
|
+
)
|
|
58
|
+
redirect_url = response.headers.get("Location")
|
|
59
|
+
if not redirect_url:
|
|
60
|
+
raise Exception("Failed to get redirect URL")
|
|
61
|
+
from urllib.parse import urlparse, parse_qs
|
|
62
|
+
query = urlparse(redirect_url).query
|
|
63
|
+
request_id = parse_qs(query).get("id", [None])[0]
|
|
64
|
+
if not request_id:
|
|
65
|
+
raise Exception("ID not found in URL")
|
|
66
|
+
polling_url = f"https://www.bing.com/images/create/async/results/{request_id}?q={requests.utils.quote(prompt)}"
|
|
67
|
+
attempts = 0
|
|
68
|
+
img_url = None
|
|
69
|
+
while attempts < 10:
|
|
70
|
+
time.sleep(3)
|
|
71
|
+
try:
|
|
72
|
+
poll_resp = session.get(polling_url, headers=headers, timeout=timeout)
|
|
73
|
+
from bs4 import BeautifulSoup
|
|
74
|
+
soup = BeautifulSoup(poll_resp.text, "html.parser")
|
|
75
|
+
imgs = [img["src"].split("?")[0] for img in soup.select(".img_cont .mimg") if img.get("src")]
|
|
76
|
+
if imgs:
|
|
77
|
+
img_url = imgs[0]
|
|
78
|
+
break
|
|
79
|
+
except Exception:
|
|
80
|
+
pass
|
|
81
|
+
attempts += 1
|
|
82
|
+
if not img_url:
|
|
83
|
+
raise Exception("Failed to get images after polling.")
|
|
84
|
+
img_bytes = session.get(img_url, headers=headers, timeout=timeout).content
|
|
85
|
+
# Convert to png or jpeg in memory
|
|
86
|
+
with BytesIO(img_bytes) as input_io:
|
|
87
|
+
with Image.open(input_io) as im:
|
|
88
|
+
out_io = BytesIO()
|
|
89
|
+
if image_format.lower() == "jpeg":
|
|
90
|
+
im = im.convert("RGB")
|
|
91
|
+
im.save(out_io, format="JPEG")
|
|
92
|
+
else:
|
|
93
|
+
im.save(out_io, format="PNG")
|
|
94
|
+
img_bytes = out_io.getvalue()
|
|
95
|
+
images.append(img_bytes)
|
|
96
|
+
if response_format == "url":
|
|
97
|
+
def upload_file_with_retry(img_bytes, image_format, max_retries=3):
|
|
98
|
+
ext = "jpg" if image_format.lower() == "jpeg" else "png"
|
|
99
|
+
for attempt in range(max_retries):
|
|
100
|
+
tmp_path = None
|
|
101
|
+
try:
|
|
102
|
+
with tempfile.NamedTemporaryFile(suffix=f".{ext}", delete=False) as tmp:
|
|
103
|
+
tmp.write(img_bytes)
|
|
104
|
+
tmp.flush()
|
|
105
|
+
tmp_path = tmp.name
|
|
106
|
+
with open(tmp_path, "rb") as f:
|
|
107
|
+
files = {"fileToUpload": (f"image.{ext}", f, f"image/{ext}")}
|
|
108
|
+
data = {"reqtype": "fileupload", "json": "true"}
|
|
109
|
+
headers2 = {"User-Agent": agent.random()}
|
|
110
|
+
if attempt > 0:
|
|
111
|
+
headers2["Connection"] = "close"
|
|
112
|
+
resp2 = requests.post(
|
|
113
|
+
"https://catbox.moe/user/api.php",
|
|
114
|
+
files=files,
|
|
115
|
+
data=data,
|
|
116
|
+
headers=headers2,
|
|
117
|
+
timeout=timeout,
|
|
118
|
+
)
|
|
119
|
+
if resp2.status_code == 200 and resp2.text.strip():
|
|
120
|
+
text = resp2.text.strip()
|
|
121
|
+
if text.startswith("http"):
|
|
122
|
+
return text
|
|
123
|
+
try:
|
|
124
|
+
result = resp2.json()
|
|
125
|
+
if "url" in result:
|
|
126
|
+
return result["url"]
|
|
127
|
+
except Exception:
|
|
128
|
+
if "http" in text:
|
|
129
|
+
return text
|
|
130
|
+
except Exception:
|
|
131
|
+
if attempt < max_retries - 1:
|
|
132
|
+
time.sleep(1 * (attempt + 1))
|
|
133
|
+
finally:
|
|
134
|
+
if tmp_path and os.path.isfile(tmp_path):
|
|
135
|
+
try:
|
|
136
|
+
os.remove(tmp_path)
|
|
137
|
+
except Exception:
|
|
138
|
+
pass
|
|
139
|
+
return None
|
|
140
|
+
def upload_file_alternative(img_bytes, image_format):
|
|
141
|
+
try:
|
|
142
|
+
ext = "jpg" if image_format.lower() == "jpeg" else "png"
|
|
143
|
+
with tempfile.NamedTemporaryFile(suffix=f".{ext}", delete=False) as tmp:
|
|
144
|
+
tmp.write(img_bytes)
|
|
145
|
+
tmp.flush()
|
|
146
|
+
tmp_path = tmp.name
|
|
147
|
+
try:
|
|
148
|
+
if not os.path.isfile(tmp_path):
|
|
149
|
+
return None
|
|
150
|
+
with open(tmp_path, "rb") as img_file:
|
|
151
|
+
files = {"file": img_file}
|
|
152
|
+
alt_resp = requests.post("https://0x0.st", files=files)
|
|
153
|
+
alt_resp.raise_for_status()
|
|
154
|
+
image_url = alt_resp.text.strip()
|
|
155
|
+
if not image_url.startswith("http"):
|
|
156
|
+
return None
|
|
157
|
+
return image_url
|
|
158
|
+
except Exception:
|
|
159
|
+
return None
|
|
160
|
+
finally:
|
|
161
|
+
try:
|
|
162
|
+
os.remove(tmp_path)
|
|
163
|
+
except Exception:
|
|
164
|
+
pass
|
|
165
|
+
except Exception:
|
|
166
|
+
return None
|
|
167
|
+
uploaded_url = upload_file_with_retry(img_bytes, image_format)
|
|
168
|
+
if not uploaded_url:
|
|
169
|
+
uploaded_url = upload_file_alternative(img_bytes, image_format)
|
|
170
|
+
if uploaded_url:
|
|
171
|
+
urls.append(uploaded_url)
|
|
172
|
+
else:
|
|
173
|
+
raise RuntimeError(
|
|
174
|
+
"Failed to upload image to catbox.moe using all available methods"
|
|
175
|
+
)
|
|
176
|
+
result_data = []
|
|
177
|
+
if response_format == "url":
|
|
178
|
+
for url in urls:
|
|
179
|
+
result_data.append(ImageData(url=url))
|
|
180
|
+
elif response_format == "b64_json":
|
|
181
|
+
import base64
|
|
182
|
+
for img in images:
|
|
183
|
+
b64 = base64.b64encode(img).decode("utf-8")
|
|
184
|
+
result_data.append(ImageData(b64_json=b64))
|
|
185
|
+
else:
|
|
186
|
+
raise ValueError("response_format must be 'url' or 'b64_json'")
|
|
187
|
+
from time import time as _time
|
|
188
|
+
return ImageResponse(created=int(_time()), data=result_data)
|
|
189
|
+
|
|
190
|
+
class BingImageAI(TTICompatibleProvider):
|
|
191
|
+
AVAILABLE_MODELS = ["bing"]
|
|
192
|
+
def __init__(self, cookie: Optional[str] = None):
|
|
193
|
+
self.session = requests.Session()
|
|
194
|
+
self.user_agent = LitAgent().random()
|
|
195
|
+
self.headers = {
|
|
196
|
+
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
|
|
197
|
+
"accept-language": "id-ID,id;q=0.9",
|
|
198
|
+
"cache-control": "max-age=0",
|
|
199
|
+
"content-type": "application/x-www-form-urlencoded",
|
|
200
|
+
"origin": "https://www.bing.com",
|
|
201
|
+
"referer": "https://www.bing.com/images/create?&wlexpsignin=1",
|
|
202
|
+
"sec-ch-ua": '"Chromium";v="131", "Not_A Brand";v="24", "Microsoft Edge Simulate";v="131", "Lemur";v="131"',
|
|
203
|
+
"sec-ch-ua-mobile": "?1",
|
|
204
|
+
"sec-fetch-site": "same-origin",
|
|
205
|
+
"sec-fetch-mode": "navigate",
|
|
206
|
+
"sec-fetch-dest": "document",
|
|
207
|
+
"upgrade-insecure-requests": "1",
|
|
208
|
+
"user-agent": self.user_agent,
|
|
209
|
+
}
|
|
210
|
+
self.session.headers.update(self.headers)
|
|
211
|
+
self.cookie = cookie
|
|
212
|
+
if cookie:
|
|
213
|
+
self.session.cookies.set("_U", cookie, domain="bing.com")
|
|
214
|
+
self.images = Images(self)
|
|
215
|
+
@property
|
|
216
|
+
def models(self):
|
|
217
|
+
class _ModelList:
|
|
218
|
+
def list(inner_self):
|
|
219
|
+
return type(self).AVAILABLE_MODELS
|
|
220
|
+
return _ModelList()
|
|
221
|
+
|
|
222
|
+
if __name__ == "__main__":
|
|
223
|
+
from rich import print
|
|
224
|
+
client = BingImageAI(cookie="1pkdvumH1SEjFkDjFymRYKouIRoXZlh_p5RTfAttx4DaaNOSDyz8qFP2M7LbZ93fbl4f6Xm8fTGwXHNDB648Gom5jfnTU_Iz-VH47l0HTYJDS1sItbBBS-sqSISFgXR62SoqnW5eX5MFht-j2uB1gZ4uDnpR_60fLRTCdW1SIRegDvnBm1TGhRiZsi6wUPyzwFg7-PsXAs3Fq9iV9m-0FEw")
|
|
225
|
+
response = client.images.create(
|
|
226
|
+
prompt="A cat riding a bicycle",
|
|
227
|
+
response_format="url",
|
|
228
|
+
n=4,
|
|
229
|
+
timeout=30
|
|
230
|
+
)
|
|
231
|
+
print(response)
|
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
##################################################################################
|
|
2
2
|
## Modified version of code written by t.me/infip1217 ##
|
|
3
3
|
##################################################################################
|
|
4
|
-
import time
|
|
5
4
|
import requests
|
|
6
5
|
import pathlib
|
|
7
6
|
import tempfile
|
|
8
|
-
from io import BytesIO
|
|
9
7
|
from webscout import exceptions
|
|
10
8
|
from webscout.litagent import LitAgent
|
|
11
|
-
from
|
|
12
|
-
from webscout.Provider.TTS import utils
|
|
9
|
+
from webscout.Litlogger import Logger, LogLevel
|
|
13
10
|
from webscout.Provider.TTS.base import BaseTTSProvider
|
|
14
11
|
|
|
15
12
|
class SpeechMaTTS(BaseTTSProvider):
|
|
@@ -18,12 +15,11 @@ class SpeechMaTTS(BaseTTSProvider):
|
|
|
18
15
|
"""
|
|
19
16
|
# Request headers
|
|
20
17
|
headers = {
|
|
21
|
-
"
|
|
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",
|
|
18
|
+
"authority": "speechma.com",
|
|
24
19
|
"origin": "https://speechma.com",
|
|
25
|
-
"
|
|
26
|
-
"
|
|
20
|
+
"referer": "https://speechma.com/",
|
|
21
|
+
"content-type": "application/json",
|
|
22
|
+
**LitAgent().generate_fingerprint()
|
|
27
23
|
}
|
|
28
24
|
|
|
29
25
|
# Available voices with their IDs
|
|
@@ -496,8 +492,9 @@ class SpeechMaTTS(BaseTTSProvider):
|
|
|
496
492
|
if proxies:
|
|
497
493
|
self.session.proxies.update(proxies)
|
|
498
494
|
self.timeout = timeout
|
|
495
|
+
self.logger = Logger(name="SpeechMaTTS", level=LogLevel.INFO)
|
|
499
496
|
|
|
500
|
-
def tts(self, text: str, voice: str = "Emma", pitch: int = 0, rate: int = 0) -> str:
|
|
497
|
+
def tts(self, text: str, voice: str = "Emma", pitch: int = 0, rate: int = 0, verbose: bool = False) -> str:
|
|
501
498
|
"""
|
|
502
499
|
Converts text to speech using the SpeechMa API and saves it to a file.
|
|
503
500
|
|
|
@@ -506,6 +503,7 @@ class SpeechMaTTS(BaseTTSProvider):
|
|
|
506
503
|
voice (str): The voice to use for TTS (default: "Emma")
|
|
507
504
|
pitch (int): Voice pitch adjustment (-10 to 10, default: 0)
|
|
508
505
|
rate (int): Voice rate/speed adjustment (-10 to 10, default: 0)
|
|
506
|
+
verbose (bool): Whether to print debug information (default: False)
|
|
509
507
|
|
|
510
508
|
Returns:
|
|
511
509
|
str: Path to the generated audio file
|
|
@@ -517,10 +515,13 @@ class SpeechMaTTS(BaseTTSProvider):
|
|
|
517
515
|
voice in self.all_voices
|
|
518
516
|
), f"Voice '{voice}' not one of [{', '.join(self.all_voices.keys())}]"
|
|
519
517
|
|
|
518
|
+
if not text or text.strip() == '':
|
|
519
|
+
raise exceptions.FailedToGenerateResponseError("Text is empty")
|
|
520
|
+
|
|
520
521
|
filename = pathlib.Path(tempfile.mktemp(suffix=".mp3", dir=self.temp_dir))
|
|
521
522
|
voice_id = self.all_voices[voice]
|
|
522
523
|
|
|
523
|
-
# Prepare payload for the
|
|
524
|
+
# Prepare payload for the API
|
|
524
525
|
payload = {
|
|
525
526
|
"text": text,
|
|
526
527
|
"voice": voice_id,
|
|
@@ -530,44 +531,49 @@ class SpeechMaTTS(BaseTTSProvider):
|
|
|
530
531
|
}
|
|
531
532
|
|
|
532
533
|
try:
|
|
534
|
+
# Set logger level based on verbose flag
|
|
535
|
+
if verbose:
|
|
536
|
+
self.logger.level = LogLevel.DEBUG
|
|
537
|
+
self.logger.debug(f"Generating audio for voice: {voice} ({voice_id})")
|
|
538
|
+
self.logger.debug(f"Text length: {len(text)} characters")
|
|
539
|
+
else:
|
|
540
|
+
self.logger.level = LogLevel.INFO
|
|
541
|
+
|
|
542
|
+
# Make the request to the SpeechMa API
|
|
533
543
|
response = self.session.post(
|
|
534
544
|
self.api_url,
|
|
535
545
|
headers=self.headers,
|
|
536
546
|
json=payload,
|
|
537
547
|
timeout=self.timeout
|
|
538
548
|
)
|
|
539
|
-
response.raise_for_status()
|
|
540
|
-
resp_json = response.json()
|
|
541
|
-
if not resp_json.get("success") or "data" not in resp_json or "job_id" not in resp_json["data"]:
|
|
542
|
-
raise exceptions.FailedToGenerateResponseError(f"SpeechMa API error: {resp_json}")
|
|
543
|
-
job_id = resp_json["data"]["job_id"]
|
|
544
549
|
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
status_url,
|
|
550
|
-
headers=self.headers,
|
|
551
|
-
timeout=self.timeout
|
|
552
|
-
)
|
|
553
|
-
status_resp.raise_for_status()
|
|
554
|
-
status_json = status_resp.json()
|
|
555
|
-
if status_json.get("success") and status_json.get("data", {}).get("status") == "completed":
|
|
556
|
-
break
|
|
557
|
-
time.sleep(1)
|
|
558
|
-
else:
|
|
559
|
-
raise exceptions.FailedToGenerateResponseError("TTS job did not complete in time.")
|
|
550
|
+
if response.status_code != 200:
|
|
551
|
+
if verbose:
|
|
552
|
+
self.logger.error(f"API error: Status {response.status_code}")
|
|
553
|
+
raise exceptions.FailedToGenerateResponseError(f"API returned status {response.status_code}: {response.text[:500]}")
|
|
560
554
|
|
|
561
|
-
#
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
555
|
+
# Check if response is audio data (content-type should be audio/mpeg)
|
|
556
|
+
content_type = response.headers.get('content-type', '').lower()
|
|
557
|
+
if verbose:
|
|
558
|
+
self.logger.debug(f"Response content type: {content_type}")
|
|
559
|
+
self.logger.debug(f"Response size: {len(response.content)} bytes")
|
|
560
|
+
|
|
561
|
+
if 'audio' in content_type or response.content.startswith(b'\xff\xfb') or response.content.startswith(b'ID3') or b'LAME' in response.content[:100]:
|
|
562
|
+
# This is audio data, save it directly
|
|
563
|
+
with open(filename, 'wb') as f:
|
|
564
|
+
f.write(response.content)
|
|
565
|
+
if verbose:
|
|
566
|
+
self.logger.debug(f"Audio saved to: {filename}")
|
|
567
|
+
return filename.as_posix()
|
|
568
|
+
else:
|
|
569
|
+
# Unexpected response format
|
|
570
|
+
if verbose:
|
|
571
|
+
self.logger.error(f"Unexpected response format: {content_type}")
|
|
572
|
+
raise exceptions.FailedToGenerateResponseError(f"Unexpected response format. Content-Type: {content_type}, Content: {response.text[:200]}")
|
|
569
573
|
|
|
570
574
|
except requests.exceptions.RequestException as e:
|
|
575
|
+
if verbose:
|
|
576
|
+
self.logger.error(f"Request failed: {e}")
|
|
571
577
|
raise exceptions.FailedToGenerateResponseError(
|
|
572
578
|
f"Failed to perform the operation: {e}"
|
|
573
579
|
)
|