webscout 8.2.7__py3-none-any.whl → 8.2.9__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.
- webscout/AIauto.py +33 -15
- webscout/AIbase.py +96 -37
- webscout/AIutel.py +703 -250
- webscout/Bard.py +441 -323
- webscout/Extra/Act.md +309 -0
- webscout/Extra/GitToolkit/__init__.py +10 -0
- webscout/Extra/GitToolkit/gitapi/README.md +110 -0
- webscout/Extra/GitToolkit/gitapi/__init__.py +12 -0
- webscout/Extra/GitToolkit/gitapi/repository.py +195 -0
- webscout/Extra/GitToolkit/gitapi/user.py +96 -0
- webscout/Extra/GitToolkit/gitapi/utils.py +62 -0
- webscout/Extra/YTToolkit/README.md +375 -0
- webscout/Extra/YTToolkit/YTdownloader.py +957 -0
- webscout/Extra/YTToolkit/__init__.py +3 -0
- webscout/Extra/YTToolkit/transcriber.py +476 -0
- webscout/Extra/YTToolkit/ytapi/README.md +44 -0
- webscout/Extra/YTToolkit/ytapi/__init__.py +6 -0
- webscout/Extra/YTToolkit/ytapi/channel.py +307 -0
- webscout/Extra/YTToolkit/ytapi/errors.py +13 -0
- webscout/Extra/YTToolkit/ytapi/extras.py +118 -0
- webscout/Extra/YTToolkit/ytapi/https.py +88 -0
- webscout/Extra/YTToolkit/ytapi/patterns.py +61 -0
- webscout/Extra/YTToolkit/ytapi/playlist.py +59 -0
- webscout/Extra/YTToolkit/ytapi/pool.py +8 -0
- webscout/Extra/YTToolkit/ytapi/query.py +40 -0
- webscout/Extra/YTToolkit/ytapi/stream.py +63 -0
- webscout/Extra/YTToolkit/ytapi/utils.py +62 -0
- webscout/Extra/YTToolkit/ytapi/video.py +232 -0
- webscout/Extra/__init__.py +7 -0
- webscout/Extra/autocoder/__init__.py +9 -0
- webscout/Extra/autocoder/autocoder.py +1105 -0
- webscout/Extra/autocoder/autocoder_utiles.py +332 -0
- webscout/Extra/gguf.md +430 -0
- webscout/Extra/gguf.py +684 -0
- webscout/Extra/tempmail/README.md +488 -0
- webscout/Extra/tempmail/__init__.py +28 -0
- webscout/Extra/tempmail/async_utils.py +141 -0
- webscout/Extra/tempmail/base.py +161 -0
- webscout/Extra/tempmail/cli.py +187 -0
- webscout/Extra/tempmail/emailnator.py +84 -0
- webscout/Extra/tempmail/mail_tm.py +361 -0
- webscout/Extra/tempmail/temp_mail_io.py +292 -0
- webscout/Extra/weather.md +281 -0
- webscout/Extra/weather.py +194 -0
- webscout/Extra/weather_ascii.py +76 -0
- webscout/Litlogger/README.md +10 -0
- webscout/Litlogger/__init__.py +15 -0
- webscout/Litlogger/formats.py +4 -0
- webscout/Litlogger/handlers.py +103 -0
- webscout/Litlogger/levels.py +13 -0
- webscout/Litlogger/logger.py +92 -0
- webscout/Provider/AI21.py +177 -0
- webscout/Provider/AISEARCH/DeepFind.py +254 -0
- webscout/Provider/AISEARCH/Perplexity.py +333 -0
- webscout/Provider/AISEARCH/README.md +279 -0
- webscout/Provider/AISEARCH/__init__.py +9 -0
- webscout/Provider/AISEARCH/felo_search.py +202 -0
- webscout/Provider/AISEARCH/genspark_search.py +324 -0
- webscout/Provider/AISEARCH/hika_search.py +186 -0
- webscout/Provider/AISEARCH/iask_search.py +410 -0
- webscout/Provider/AISEARCH/monica_search.py +220 -0
- webscout/Provider/AISEARCH/scira_search.py +298 -0
- webscout/Provider/AISEARCH/webpilotai_search.py +255 -0
- webscout/Provider/Aitopia.py +316 -0
- webscout/Provider/AllenAI.py +440 -0
- webscout/Provider/Andi.py +228 -0
- webscout/Provider/Blackboxai.py +791 -0
- webscout/Provider/ChatGPTClone.py +237 -0
- webscout/Provider/ChatGPTGratis.py +194 -0
- webscout/Provider/ChatSandbox.py +342 -0
- webscout/Provider/Cloudflare.py +324 -0
- webscout/Provider/Cohere.py +208 -0
- webscout/Provider/Deepinfra.py +340 -0
- webscout/Provider/ExaAI.py +261 -0
- webscout/Provider/ExaChat.py +358 -0
- webscout/Provider/Flowith.py +217 -0
- webscout/Provider/FreeGemini.py +250 -0
- webscout/Provider/Gemini.py +169 -0
- webscout/Provider/GithubChat.py +369 -0
- webscout/Provider/GizAI.py +295 -0
- webscout/Provider/Glider.py +225 -0
- webscout/Provider/Groq.py +801 -0
- webscout/Provider/HF_space/__init__.py +0 -0
- webscout/Provider/HF_space/qwen_qwen2.py +206 -0
- webscout/Provider/HeckAI.py +375 -0
- webscout/Provider/HuggingFaceChat.py +469 -0
- webscout/Provider/Hunyuan.py +283 -0
- webscout/Provider/Jadve.py +291 -0
- webscout/Provider/Koboldai.py +384 -0
- webscout/Provider/LambdaChat.py +411 -0
- webscout/Provider/Llama3.py +259 -0
- webscout/Provider/MCPCore.py +315 -0
- webscout/Provider/Marcus.py +198 -0
- webscout/Provider/Nemotron.py +218 -0
- webscout/Provider/Netwrck.py +270 -0
- webscout/Provider/OLLAMA.py +396 -0
- webscout/Provider/OPENAI/BLACKBOXAI.py +766 -0
- webscout/Provider/OPENAI/Cloudflare.py +378 -0
- webscout/Provider/OPENAI/FreeGemini.py +283 -0
- webscout/Provider/OPENAI/NEMOTRON.py +232 -0
- webscout/Provider/OPENAI/Qwen3.py +283 -0
- webscout/Provider/OPENAI/README.md +952 -0
- webscout/Provider/OPENAI/TwoAI.py +357 -0
- webscout/Provider/OPENAI/__init__.py +40 -0
- webscout/Provider/OPENAI/ai4chat.py +293 -0
- webscout/Provider/OPENAI/api.py +969 -0
- webscout/Provider/OPENAI/base.py +249 -0
- webscout/Provider/OPENAI/c4ai.py +373 -0
- webscout/Provider/OPENAI/chatgpt.py +556 -0
- webscout/Provider/OPENAI/chatgptclone.py +494 -0
- webscout/Provider/OPENAI/chatsandbox.py +173 -0
- webscout/Provider/OPENAI/copilot.py +242 -0
- webscout/Provider/OPENAI/deepinfra.py +322 -0
- webscout/Provider/OPENAI/e2b.py +1414 -0
- webscout/Provider/OPENAI/exaai.py +417 -0
- webscout/Provider/OPENAI/exachat.py +444 -0
- webscout/Provider/OPENAI/flowith.py +162 -0
- webscout/Provider/OPENAI/freeaichat.py +359 -0
- webscout/Provider/OPENAI/glider.py +326 -0
- webscout/Provider/OPENAI/groq.py +364 -0
- webscout/Provider/OPENAI/heckai.py +308 -0
- webscout/Provider/OPENAI/llmchatco.py +335 -0
- webscout/Provider/OPENAI/mcpcore.py +389 -0
- webscout/Provider/OPENAI/multichat.py +376 -0
- webscout/Provider/OPENAI/netwrck.py +357 -0
- webscout/Provider/OPENAI/oivscode.py +287 -0
- webscout/Provider/OPENAI/opkfc.py +496 -0
- webscout/Provider/OPENAI/pydantic_imports.py +172 -0
- webscout/Provider/OPENAI/scirachat.py +477 -0
- webscout/Provider/OPENAI/sonus.py +304 -0
- webscout/Provider/OPENAI/standardinput.py +433 -0
- webscout/Provider/OPENAI/textpollinations.py +339 -0
- webscout/Provider/OPENAI/toolbaz.py +413 -0
- webscout/Provider/OPENAI/typefully.py +355 -0
- webscout/Provider/OPENAI/typegpt.py +364 -0
- webscout/Provider/OPENAI/uncovrAI.py +463 -0
- webscout/Provider/OPENAI/utils.py +318 -0
- webscout/Provider/OPENAI/venice.py +431 -0
- webscout/Provider/OPENAI/wisecat.py +387 -0
- webscout/Provider/OPENAI/writecream.py +163 -0
- webscout/Provider/OPENAI/x0gpt.py +365 -0
- webscout/Provider/OPENAI/yep.py +382 -0
- webscout/Provider/OpenGPT.py +209 -0
- webscout/Provider/Openai.py +496 -0
- webscout/Provider/PI.py +429 -0
- webscout/Provider/Perplexitylabs.py +415 -0
- webscout/Provider/QwenLM.py +254 -0
- webscout/Provider/Reka.py +214 -0
- webscout/Provider/StandardInput.py +290 -0
- webscout/Provider/TTI/README.md +82 -0
- webscout/Provider/TTI/__init__.py +7 -0
- webscout/Provider/TTI/aiarta.py +365 -0
- webscout/Provider/TTI/artbit.py +0 -0
- webscout/Provider/TTI/base.py +64 -0
- webscout/Provider/TTI/fastflux.py +200 -0
- webscout/Provider/TTI/magicstudio.py +201 -0
- webscout/Provider/TTI/piclumen.py +203 -0
- webscout/Provider/TTI/pixelmuse.py +225 -0
- webscout/Provider/TTI/pollinations.py +221 -0
- webscout/Provider/TTI/utils.py +11 -0
- webscout/Provider/TTS/README.md +192 -0
- webscout/Provider/TTS/__init__.py +10 -0
- webscout/Provider/TTS/base.py +159 -0
- webscout/Provider/TTS/deepgram.py +156 -0
- webscout/Provider/TTS/elevenlabs.py +111 -0
- webscout/Provider/TTS/gesserit.py +128 -0
- webscout/Provider/TTS/murfai.py +113 -0
- webscout/Provider/TTS/openai_fm.py +129 -0
- webscout/Provider/TTS/parler.py +111 -0
- webscout/Provider/TTS/speechma.py +580 -0
- webscout/Provider/TTS/sthir.py +94 -0
- webscout/Provider/TTS/streamElements.py +333 -0
- webscout/Provider/TTS/utils.py +280 -0
- webscout/Provider/TeachAnything.py +229 -0
- webscout/Provider/TextPollinationsAI.py +308 -0
- webscout/Provider/TwoAI.py +475 -0
- webscout/Provider/TypliAI.py +305 -0
- webscout/Provider/UNFINISHED/ChatHub.py +209 -0
- webscout/Provider/UNFINISHED/Youchat.py +330 -0
- webscout/Provider/UNFINISHED/liner_api_request.py +263 -0
- webscout/Provider/UNFINISHED/puterjs.py +635 -0
- webscout/Provider/UNFINISHED/test_lmarena.py +119 -0
- webscout/Provider/Venice.py +258 -0
- webscout/Provider/VercelAI.py +253 -0
- webscout/Provider/WiseCat.py +233 -0
- webscout/Provider/WrDoChat.py +370 -0
- webscout/Provider/Writecream.py +246 -0
- webscout/Provider/WritingMate.py +269 -0
- webscout/Provider/__init__.py +174 -0
- webscout/Provider/ai4chat.py +174 -0
- webscout/Provider/akashgpt.py +335 -0
- webscout/Provider/asksteve.py +220 -0
- webscout/Provider/cerebras.py +290 -0
- webscout/Provider/chatglm.py +215 -0
- webscout/Provider/cleeai.py +213 -0
- webscout/Provider/copilot.py +425 -0
- webscout/Provider/elmo.py +283 -0
- webscout/Provider/freeaichat.py +285 -0
- webscout/Provider/geminiapi.py +208 -0
- webscout/Provider/granite.py +235 -0
- webscout/Provider/hermes.py +266 -0
- webscout/Provider/julius.py +223 -0
- webscout/Provider/koala.py +170 -0
- webscout/Provider/learnfastai.py +325 -0
- webscout/Provider/llama3mitril.py +215 -0
- webscout/Provider/llmchat.py +258 -0
- webscout/Provider/llmchatco.py +306 -0
- webscout/Provider/lmarena.py +198 -0
- webscout/Provider/meta.py +801 -0
- webscout/Provider/multichat.py +364 -0
- webscout/Provider/oivscode.py +309 -0
- webscout/Provider/samurai.py +224 -0
- webscout/Provider/scira_chat.py +299 -0
- webscout/Provider/scnet.py +243 -0
- webscout/Provider/searchchat.py +292 -0
- webscout/Provider/sonus.py +258 -0
- webscout/Provider/talkai.py +194 -0
- webscout/Provider/toolbaz.py +353 -0
- webscout/Provider/turboseek.py +266 -0
- webscout/Provider/typefully.py +202 -0
- webscout/Provider/typegpt.py +289 -0
- webscout/Provider/uncovr.py +368 -0
- webscout/Provider/x0gpt.py +299 -0
- webscout/Provider/yep.py +389 -0
- webscout/__init__.py +4 -2
- webscout/cli.py +3 -28
- webscout/client.py +70 -0
- webscout/conversation.py +35 -35
- webscout/litagent/Readme.md +276 -0
- webscout/litagent/__init__.py +29 -0
- webscout/litagent/agent.py +455 -0
- webscout/litagent/constants.py +60 -0
- webscout/litprinter/__init__.py +59 -0
- webscout/optimizers.py +419 -419
- webscout/scout/README.md +404 -0
- webscout/scout/__init__.py +8 -0
- webscout/scout/core/__init__.py +7 -0
- webscout/scout/core/crawler.py +210 -0
- webscout/scout/core/scout.py +607 -0
- webscout/scout/core/search_result.py +96 -0
- webscout/scout/core/text_analyzer.py +63 -0
- webscout/scout/core/text_utils.py +277 -0
- webscout/scout/core/web_analyzer.py +52 -0
- webscout/scout/element.py +478 -0
- webscout/scout/parsers/__init__.py +69 -0
- webscout/scout/parsers/html5lib_parser.py +172 -0
- webscout/scout/parsers/html_parser.py +236 -0
- webscout/scout/parsers/lxml_parser.py +178 -0
- webscout/scout/utils.py +37 -0
- webscout/swiftcli/Readme.md +323 -0
- webscout/swiftcli/__init__.py +95 -0
- webscout/swiftcli/core/__init__.py +7 -0
- webscout/swiftcli/core/cli.py +297 -0
- webscout/swiftcli/core/context.py +104 -0
- webscout/swiftcli/core/group.py +241 -0
- webscout/swiftcli/decorators/__init__.py +28 -0
- webscout/swiftcli/decorators/command.py +221 -0
- webscout/swiftcli/decorators/options.py +220 -0
- webscout/swiftcli/decorators/output.py +252 -0
- webscout/swiftcli/exceptions.py +21 -0
- webscout/swiftcli/plugins/__init__.py +9 -0
- webscout/swiftcli/plugins/base.py +135 -0
- webscout/swiftcli/plugins/manager.py +269 -0
- webscout/swiftcli/utils/__init__.py +59 -0
- webscout/swiftcli/utils/formatting.py +252 -0
- webscout/swiftcli/utils/parsing.py +267 -0
- webscout/version.py +1 -1
- webscout/webscout_search.py +2 -182
- webscout/webscout_search_async.py +1 -179
- webscout/zeroart/README.md +89 -0
- webscout/zeroart/__init__.py +135 -0
- webscout/zeroart/base.py +66 -0
- webscout/zeroart/effects.py +101 -0
- webscout/zeroart/fonts.py +1239 -0
- {webscout-8.2.7.dist-info → webscout-8.2.9.dist-info}/METADATA +262 -83
- webscout-8.2.9.dist-info/RECORD +289 -0
- {webscout-8.2.7.dist-info → webscout-8.2.9.dist-info}/WHEEL +1 -1
- {webscout-8.2.7.dist-info → webscout-8.2.9.dist-info}/entry_points.txt +1 -0
- webscout-8.2.7.dist-info/RECORD +0 -26
- {webscout-8.2.7.dist-info → webscout-8.2.9.dist-info}/licenses/LICENSE.md +0 -0
- {webscout-8.2.7.dist-info → webscout-8.2.9.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
from typing import Optional, List, Dict, Any, Union
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from requests.exceptions import RequestException
|
|
5
|
+
from webscout.Provider.TTI.utils import ImageData, ImageResponse
|
|
6
|
+
from webscout.Provider.TTI.base import TTICompatibleProvider, BaseImages
|
|
7
|
+
from io import BytesIO
|
|
8
|
+
import os
|
|
9
|
+
import tempfile
|
|
10
|
+
from webscout.litagent import LitAgent
|
|
11
|
+
import time
|
|
12
|
+
import json
|
|
13
|
+
import random
|
|
14
|
+
|
|
15
|
+
try:
|
|
16
|
+
from PIL import Image
|
|
17
|
+
except ImportError:
|
|
18
|
+
Image = None
|
|
19
|
+
|
|
20
|
+
class Images(BaseImages):
|
|
21
|
+
def __init__(self, client):
|
|
22
|
+
self._client = client
|
|
23
|
+
|
|
24
|
+
def create(
|
|
25
|
+
self,
|
|
26
|
+
*,
|
|
27
|
+
model: str,
|
|
28
|
+
prompt: str,
|
|
29
|
+
n: int = 1,
|
|
30
|
+
size: str = "1024x1024",
|
|
31
|
+
response_format: str = "url",
|
|
32
|
+
user: Optional[str] = None,
|
|
33
|
+
style: str = "none",
|
|
34
|
+
aspect_ratio: str = "1:1",
|
|
35
|
+
timeout: int = 60,
|
|
36
|
+
image_format: str = "png",
|
|
37
|
+
seed: Optional[int] = None,
|
|
38
|
+
**kwargs
|
|
39
|
+
) -> ImageResponse:
|
|
40
|
+
"""
|
|
41
|
+
image_format: "png" or "jpeg"
|
|
42
|
+
seed: Optional random seed for reproducibility. If not provided, a random seed is used.
|
|
43
|
+
"""
|
|
44
|
+
if Image is None:
|
|
45
|
+
raise ImportError("Pillow (PIL) is required for image format conversion.")
|
|
46
|
+
|
|
47
|
+
images = []
|
|
48
|
+
urls = []
|
|
49
|
+
|
|
50
|
+
def upload_file_with_retry(img_bytes, image_format, max_retries=3):
|
|
51
|
+
ext = "jpg" if image_format.lower() == "jpeg" else "png"
|
|
52
|
+
for attempt in range(max_retries):
|
|
53
|
+
tmp_path = None
|
|
54
|
+
try:
|
|
55
|
+
with tempfile.NamedTemporaryFile(suffix=f".{ext}", delete=False) as tmp:
|
|
56
|
+
tmp.write(img_bytes)
|
|
57
|
+
tmp.flush()
|
|
58
|
+
tmp_path = tmp.name
|
|
59
|
+
with open(tmp_path, 'rb') as f:
|
|
60
|
+
files = {
|
|
61
|
+
'fileToUpload': (f'image.{ext}', f, f'image/{ext}')
|
|
62
|
+
}
|
|
63
|
+
data = {
|
|
64
|
+
'reqtype': 'fileupload',
|
|
65
|
+
'json': 'true'
|
|
66
|
+
}
|
|
67
|
+
headers = {'User-Agent': LitAgent().random()}
|
|
68
|
+
if attempt > 0:
|
|
69
|
+
headers['Connection'] = 'close'
|
|
70
|
+
resp = requests.post("https://catbox.moe/user/api.php", files=files, data=data, headers=headers, timeout=timeout)
|
|
71
|
+
if resp.status_code == 200 and resp.text.strip():
|
|
72
|
+
text = resp.text.strip()
|
|
73
|
+
if text.startswith('http'):
|
|
74
|
+
return text
|
|
75
|
+
try:
|
|
76
|
+
result = resp.json()
|
|
77
|
+
if "url" in result:
|
|
78
|
+
return result["url"]
|
|
79
|
+
except json.JSONDecodeError:
|
|
80
|
+
if 'http' in text:
|
|
81
|
+
return text
|
|
82
|
+
except Exception:
|
|
83
|
+
if attempt < max_retries - 1:
|
|
84
|
+
time.sleep(1 * (attempt + 1))
|
|
85
|
+
finally:
|
|
86
|
+
if tmp_path and os.path.isfile(tmp_path):
|
|
87
|
+
try:
|
|
88
|
+
os.remove(tmp_path)
|
|
89
|
+
except Exception:
|
|
90
|
+
pass
|
|
91
|
+
return None
|
|
92
|
+
|
|
93
|
+
def upload_file_alternative(img_bytes, image_format):
|
|
94
|
+
try:
|
|
95
|
+
ext = "jpg" if image_format.lower() == "jpeg" else "png"
|
|
96
|
+
with tempfile.NamedTemporaryFile(suffix=f".{ext}", delete=False) as tmp:
|
|
97
|
+
tmp.write(img_bytes)
|
|
98
|
+
tmp.flush()
|
|
99
|
+
tmp_path = tmp.name
|
|
100
|
+
try:
|
|
101
|
+
if not os.path.isfile(tmp_path):
|
|
102
|
+
return None
|
|
103
|
+
with open(tmp_path, 'rb') as img_file:
|
|
104
|
+
files = {'file': img_file}
|
|
105
|
+
response = requests.post('https://0x0.st', files=files)
|
|
106
|
+
response.raise_for_status()
|
|
107
|
+
image_url = response.text.strip()
|
|
108
|
+
if not image_url.startswith('http'):
|
|
109
|
+
return None
|
|
110
|
+
return image_url
|
|
111
|
+
except Exception:
|
|
112
|
+
return None
|
|
113
|
+
finally:
|
|
114
|
+
try:
|
|
115
|
+
os.remove(tmp_path)
|
|
116
|
+
except Exception:
|
|
117
|
+
pass
|
|
118
|
+
except Exception:
|
|
119
|
+
return None
|
|
120
|
+
|
|
121
|
+
for i in range(n):
|
|
122
|
+
# Prepare parameters for Pollinations API
|
|
123
|
+
params = {
|
|
124
|
+
"model": model,
|
|
125
|
+
"width": int(size.split("x")[0]) if "x" in size else 1024,
|
|
126
|
+
"height": int(size.split("x")[1]) if "x" in size else 1024,
|
|
127
|
+
"seed": seed if seed is not None else random.randint(0, 2**32 - 1),
|
|
128
|
+
}
|
|
129
|
+
# Build the API URL
|
|
130
|
+
base_url = f"https://image.pollinations.ai/prompt/{prompt}"
|
|
131
|
+
# Compose query string
|
|
132
|
+
query = "&".join(f"{k}={v}" for k, v in params.items())
|
|
133
|
+
url = f"{base_url}?{query}"
|
|
134
|
+
try:
|
|
135
|
+
resp = self._client.session.get(url, timeout=timeout)
|
|
136
|
+
resp.raise_for_status()
|
|
137
|
+
img_bytes = resp.content
|
|
138
|
+
except RequestException as e:
|
|
139
|
+
raise RuntimeError(f"Failed to fetch image from Pollinations API: {e}")
|
|
140
|
+
|
|
141
|
+
# Convert to png or jpeg in memory
|
|
142
|
+
with BytesIO(img_bytes) as input_io:
|
|
143
|
+
with Image.open(input_io) as im:
|
|
144
|
+
out_io = BytesIO()
|
|
145
|
+
if image_format.lower() == "jpeg":
|
|
146
|
+
im = im.convert("RGB")
|
|
147
|
+
im.save(out_io, format="JPEG")
|
|
148
|
+
else:
|
|
149
|
+
im.save(out_io, format="PNG")
|
|
150
|
+
img_bytes = out_io.getvalue()
|
|
151
|
+
|
|
152
|
+
images.append(img_bytes)
|
|
153
|
+
|
|
154
|
+
if response_format == "url":
|
|
155
|
+
uploaded_url = upload_file_with_retry(img_bytes, image_format)
|
|
156
|
+
if not uploaded_url:
|
|
157
|
+
uploaded_url = upload_file_alternative(img_bytes, image_format)
|
|
158
|
+
if uploaded_url:
|
|
159
|
+
urls.append(uploaded_url)
|
|
160
|
+
else:
|
|
161
|
+
raise RuntimeError("Failed to upload image to catbox.moe using all available methods")
|
|
162
|
+
|
|
163
|
+
result_data = []
|
|
164
|
+
if response_format == "url":
|
|
165
|
+
for url in urls:
|
|
166
|
+
result_data.append(ImageData(url=url))
|
|
167
|
+
elif response_format == "b64_json":
|
|
168
|
+
import base64
|
|
169
|
+
for img in images:
|
|
170
|
+
b64 = base64.b64encode(img).decode("utf-8")
|
|
171
|
+
result_data.append(ImageData(b64_json=b64))
|
|
172
|
+
else:
|
|
173
|
+
raise ValueError("response_format must be 'url' or 'b64_json'")
|
|
174
|
+
|
|
175
|
+
from time import time as _time
|
|
176
|
+
return ImageResponse(
|
|
177
|
+
created=int(_time()),
|
|
178
|
+
data=result_data
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
class PollinationsAI(TTICompatibleProvider):
|
|
182
|
+
AVAILABLE_MODELS = [
|
|
183
|
+
"flux",
|
|
184
|
+
"turbo",
|
|
185
|
+
"gptimage"
|
|
186
|
+
]
|
|
187
|
+
|
|
188
|
+
def __init__(self):
|
|
189
|
+
self.api_endpoint = "https://image.pollinations.ai/prompt"
|
|
190
|
+
self.session = requests.Session()
|
|
191
|
+
self.user_agent = LitAgent().random()
|
|
192
|
+
self.headers = {
|
|
193
|
+
"accept": "*/*",
|
|
194
|
+
"accept-language": "en-US,en;q=0.9",
|
|
195
|
+
"content-type": "application/json",
|
|
196
|
+
"origin": "https://image.pollinations.ai",
|
|
197
|
+
"referer": "https://image.pollinations.ai/",
|
|
198
|
+
"user-agent": self.user_agent,
|
|
199
|
+
}
|
|
200
|
+
self.session.headers.update(self.headers)
|
|
201
|
+
self.images = Images(self)
|
|
202
|
+
|
|
203
|
+
@property
|
|
204
|
+
def models(self):
|
|
205
|
+
class _ModelList:
|
|
206
|
+
def list(inner_self):
|
|
207
|
+
return type(self).AVAILABLE_MODELS
|
|
208
|
+
return _ModelList()
|
|
209
|
+
|
|
210
|
+
if __name__ == "__main__":
|
|
211
|
+
from rich import print
|
|
212
|
+
client = PollinationsAI()
|
|
213
|
+
response = client.images.create(
|
|
214
|
+
model="flux",
|
|
215
|
+
prompt="a japanese waifu in short kimono clothes",
|
|
216
|
+
response_format="url",
|
|
217
|
+
n=4,
|
|
218
|
+
timeout=30,
|
|
219
|
+
seed=None # You can set a specific seed for reproducibility
|
|
220
|
+
)
|
|
221
|
+
print(response)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from typing import List, Optional
|
|
2
|
+
from pydantic import BaseModel, Field
|
|
3
|
+
import time
|
|
4
|
+
|
|
5
|
+
class ImageData(BaseModel):
|
|
6
|
+
url: Optional[str] = None
|
|
7
|
+
b64_json: Optional[str] = None
|
|
8
|
+
|
|
9
|
+
class ImageResponse(BaseModel):
|
|
10
|
+
created: int = Field(default_factory=lambda: int(time.time()))
|
|
11
|
+
data: List[ImageData]
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# 🎙️ Webscout Text-to-Speech (TTS) Providers
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
Webscout's TTS Providers offer a versatile and powerful text-to-speech conversion library with support for multiple providers and advanced features.
|
|
6
|
+
|
|
7
|
+
## 🌟 Features
|
|
8
|
+
|
|
9
|
+
- **Multiple TTS Providers**: Support for various text-to-speech services
|
|
10
|
+
- **Concurrent Audio Generation**: Efficiently process long texts
|
|
11
|
+
- **Flexible Voice Selection**: Choose from a wide range of voices
|
|
12
|
+
- **Robust Error Handling**: Comprehensive logging and error management
|
|
13
|
+
- **Temporary File Management**: Automatically manages temporary audio files
|
|
14
|
+
- **Cross-Platform Compatibility**: Works seamlessly across different environments
|
|
15
|
+
- **Custom Save Locations**: Save audio files to specific destinations
|
|
16
|
+
- **Audio Streaming**: Stream audio data in chunks for real-time applications
|
|
17
|
+
|
|
18
|
+
## 📦 Supported TTS Providers
|
|
19
|
+
|
|
20
|
+
1. **ElevenlabsTTS**
|
|
21
|
+
2. **GesseritTTS**
|
|
22
|
+
3. **MurfAITTS**
|
|
23
|
+
4. **ParlerTTS**
|
|
24
|
+
5. **DeepgramTTS**
|
|
25
|
+
6. **StreamElementsTTS**
|
|
26
|
+
7. **SpeechMaTTS**
|
|
27
|
+
8. **SthirTTS**
|
|
28
|
+
## 🚀 Installation
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install webscout
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## 💻 Basic Usage
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
from webscout.Provider.TTS import ElevenlabsTTS
|
|
38
|
+
|
|
39
|
+
# Initialize the TTS provider
|
|
40
|
+
tts = ElevenlabsTTS()
|
|
41
|
+
|
|
42
|
+
# Generate speech from text
|
|
43
|
+
text = "Hello, this is a test of text-to-speech conversion."
|
|
44
|
+
audio_file = tts.tts(text, voice="Brian")
|
|
45
|
+
|
|
46
|
+
# Save the audio to a specific location
|
|
47
|
+
saved_path = tts.save_audio(audio_file, destination="my_speech.mp3")
|
|
48
|
+
print(f"Audio saved to: {saved_path}")
|
|
49
|
+
|
|
50
|
+
# Stream audio in chunks
|
|
51
|
+
for chunk in tts.stream_audio(text, voice="Brian"):
|
|
52
|
+
# Process each chunk (e.g., send to a websocket, write to a stream, etc.)
|
|
53
|
+
process_audio_chunk(chunk)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## 🎛️ Advanced Configuration
|
|
57
|
+
|
|
58
|
+
### Voice Selection
|
|
59
|
+
|
|
60
|
+
Each TTS provider offers multiple voices:
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
# List available voices
|
|
64
|
+
print(tts.all_voices.keys())
|
|
65
|
+
|
|
66
|
+
# Select a specific voice
|
|
67
|
+
audio_file = tts.tts(text, voice="Alice")
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Verbose Logging
|
|
71
|
+
|
|
72
|
+
Enable detailed logging for debugging:
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
audio_file = tts.tts(text, verbose=True)
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## 🔧 Provider-Specific Details
|
|
79
|
+
|
|
80
|
+
### ElevenlabsTTS
|
|
81
|
+
|
|
82
|
+
- Supports multiple English voices
|
|
83
|
+
- Multilingual text-to-speech
|
|
84
|
+
|
|
85
|
+
### GesseritTTS
|
|
86
|
+
|
|
87
|
+
- Offers unique voice characteristics
|
|
88
|
+
- Supports voice description customization
|
|
89
|
+
|
|
90
|
+
### MurfAITTS
|
|
91
|
+
|
|
92
|
+
- Provides specific voice models
|
|
93
|
+
- Supports custom voice descriptions
|
|
94
|
+
|
|
95
|
+
### ParlerTTS
|
|
96
|
+
|
|
97
|
+
- Uses Gradio Client for TTS generation
|
|
98
|
+
- Supports large and small model variants
|
|
99
|
+
|
|
100
|
+
### DeepgramTTS
|
|
101
|
+
|
|
102
|
+
- Multiple voice options
|
|
103
|
+
- Advanced voice selection
|
|
104
|
+
|
|
105
|
+
### StreamElementsTTS
|
|
106
|
+
|
|
107
|
+
- Wide range of international voices
|
|
108
|
+
|
|
109
|
+
### SpeechMaTTS
|
|
110
|
+
|
|
111
|
+
- Multilingual voices (Ava, Emma, Andrew, Brian)
|
|
112
|
+
- Adjustable pitch and speech rate
|
|
113
|
+
- Fast audio generation
|
|
114
|
+
|
|
115
|
+
## 🛡️ Error Handling
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
try:
|
|
119
|
+
audio_file = tts.tts(text)
|
|
120
|
+
except exceptions.FailedToGenerateResponseError as e:
|
|
121
|
+
print(f"TTS generation failed: {e}")
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## 🌐 Proxy Support
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
# Use with proxy
|
|
128
|
+
tts = ElevenlabsTTS(proxies={
|
|
129
|
+
'http': 'http://proxy.example.com:8080',
|
|
130
|
+
'https': 'https://proxy.example.com:8080'
|
|
131
|
+
})
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## 💾 Custom Audio Saving
|
|
135
|
+
|
|
136
|
+
Save generated audio to a specific location:
|
|
137
|
+
|
|
138
|
+
```python
|
|
139
|
+
# Generate speech
|
|
140
|
+
audio_file = tts.tts(text, voice="Brian")
|
|
141
|
+
|
|
142
|
+
# Save to a specific file
|
|
143
|
+
tts.save_audio(audio_file, destination="path/to/output.mp3")
|
|
144
|
+
|
|
145
|
+
# Save to a specific directory with default filename
|
|
146
|
+
tts.save_audio(audio_file, destination="path/to/directory/")
|
|
147
|
+
|
|
148
|
+
# Save with default location (current directory with timestamp)
|
|
149
|
+
saved_path = tts.save_audio(audio_file)
|
|
150
|
+
print(f"Saved to: {saved_path}")
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## 📼 Audio Streaming
|
|
154
|
+
|
|
155
|
+
Stream audio data in chunks for real-time applications:
|
|
156
|
+
|
|
157
|
+
```python
|
|
158
|
+
# Stream audio in chunks
|
|
159
|
+
for chunk in tts.stream_audio(text, voice="Brian", chunk_size=2048):
|
|
160
|
+
# Example: Send to a websocket
|
|
161
|
+
websocket.send(chunk)
|
|
162
|
+
|
|
163
|
+
# Example: Write to an audio stream
|
|
164
|
+
audio_stream.write(chunk)
|
|
165
|
+
|
|
166
|
+
# Example: Process in real-time
|
|
167
|
+
process_audio_data(chunk)
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## ⏱️ Async Support
|
|
171
|
+
|
|
172
|
+
Use the async versions for non-blocking operations:
|
|
173
|
+
|
|
174
|
+
```python
|
|
175
|
+
from webscout.Provider.TTS import AsyncElevenlabsTTS
|
|
176
|
+
import asyncio
|
|
177
|
+
|
|
178
|
+
async def main():
|
|
179
|
+
tts = AsyncElevenlabsTTS()
|
|
180
|
+
|
|
181
|
+
# Generate speech
|
|
182
|
+
audio_file = await tts.tts(text, voice="Brian")
|
|
183
|
+
|
|
184
|
+
# Save to a specific location
|
|
185
|
+
saved_path = await tts.save_audio(audio_file, destination="output.mp3")
|
|
186
|
+
|
|
187
|
+
# Stream audio
|
|
188
|
+
async for chunk in tts.stream_audio(text, voice="Brian"):
|
|
189
|
+
await process_chunk(chunk)
|
|
190
|
+
|
|
191
|
+
asyncio.run(main())
|
|
192
|
+
```
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from .base import BaseTTSProvider, AsyncBaseTTSProvider
|
|
2
|
+
from .streamElements import *
|
|
3
|
+
from .parler import *
|
|
4
|
+
from .deepgram import *
|
|
5
|
+
from .elevenlabs import *
|
|
6
|
+
from .murfai import *
|
|
7
|
+
from .gesserit import *
|
|
8
|
+
from .speechma import *
|
|
9
|
+
from .sthir import *
|
|
10
|
+
from .openai_fm import *
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Base class for TTS providers with common functionality.
|
|
3
|
+
"""
|
|
4
|
+
import os
|
|
5
|
+
import tempfile
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Generator, Optional
|
|
8
|
+
from webscout.AIbase import TTSProvider
|
|
9
|
+
|
|
10
|
+
class BaseTTSProvider(TTSProvider):
|
|
11
|
+
"""
|
|
12
|
+
Base class for TTS providers with common functionality.
|
|
13
|
+
|
|
14
|
+
This class implements common methods like save_audio and stream_audio
|
|
15
|
+
that can be used by all TTS providers.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(self):
|
|
19
|
+
"""Initialize the base TTS provider."""
|
|
20
|
+
self.temp_dir = tempfile.mkdtemp(prefix="webscout_tts_")
|
|
21
|
+
|
|
22
|
+
def save_audio(self, audio_file: str, destination: str = None, verbose: bool = False) -> str:
|
|
23
|
+
"""
|
|
24
|
+
Save audio to a specific destination.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
audio_file (str): Path to the source audio file
|
|
28
|
+
destination (str, optional): Destination path. Defaults to current directory with timestamp.
|
|
29
|
+
verbose (bool, optional): Whether to print debug information. Defaults to False.
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
str: Path to the saved audio file
|
|
33
|
+
|
|
34
|
+
Raises:
|
|
35
|
+
FileNotFoundError: If the audio file doesn't exist
|
|
36
|
+
"""
|
|
37
|
+
import shutil
|
|
38
|
+
import time
|
|
39
|
+
|
|
40
|
+
source_path = Path(audio_file)
|
|
41
|
+
|
|
42
|
+
if not source_path.exists():
|
|
43
|
+
raise FileNotFoundError(f"Audio file not found: {audio_file}")
|
|
44
|
+
|
|
45
|
+
if destination is None:
|
|
46
|
+
# Create a default destination with timestamp in current directory
|
|
47
|
+
timestamp = int(time.time())
|
|
48
|
+
destination = os.path.join(os.getcwd(), f"tts_audio_{timestamp}{source_path.suffix}")
|
|
49
|
+
|
|
50
|
+
# Ensure the destination directory exists
|
|
51
|
+
os.makedirs(os.path.dirname(os.path.abspath(destination)), exist_ok=True)
|
|
52
|
+
|
|
53
|
+
# Copy the file
|
|
54
|
+
shutil.copy2(source_path, destination)
|
|
55
|
+
|
|
56
|
+
if verbose:
|
|
57
|
+
print(f"[debug] Audio saved to {destination}")
|
|
58
|
+
|
|
59
|
+
return destination
|
|
60
|
+
|
|
61
|
+
def stream_audio(self, text: str, voice: str = None, chunk_size: int = 1024, verbose: bool = False) -> Generator[bytes, None, None]:
|
|
62
|
+
"""
|
|
63
|
+
Stream audio in chunks.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
text (str): The text to convert to speech
|
|
67
|
+
voice (str, optional): The voice to use. Defaults to provider's default voice.
|
|
68
|
+
chunk_size (int, optional): Size of audio chunks to yield. Defaults to 1024.
|
|
69
|
+
verbose (bool, optional): Whether to print debug information. Defaults to False.
|
|
70
|
+
|
|
71
|
+
Yields:
|
|
72
|
+
Generator[bytes, None, None]: Audio data chunks
|
|
73
|
+
"""
|
|
74
|
+
# Generate the audio file
|
|
75
|
+
audio_file = self.tts(text, voice=voice, verbose=verbose)
|
|
76
|
+
|
|
77
|
+
# Stream the file in chunks
|
|
78
|
+
with open(audio_file, 'rb') as f:
|
|
79
|
+
while chunk := f.read(chunk_size):
|
|
80
|
+
yield chunk
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class AsyncBaseTTSProvider:
|
|
84
|
+
"""
|
|
85
|
+
Base class for async TTS providers with common functionality.
|
|
86
|
+
|
|
87
|
+
This class implements common async methods like save_audio and stream_audio
|
|
88
|
+
that can be used by all async TTS providers.
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
def __init__(self):
|
|
92
|
+
"""Initialize the async base TTS provider."""
|
|
93
|
+
self.temp_dir = tempfile.mkdtemp(prefix="webscout_tts_")
|
|
94
|
+
|
|
95
|
+
async def save_audio(self, audio_file: str, destination: str = None, verbose: bool = False) -> str:
|
|
96
|
+
"""
|
|
97
|
+
Save audio to a specific destination asynchronously.
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
audio_file (str): Path to the source audio file
|
|
101
|
+
destination (str, optional): Destination path. Defaults to current directory with timestamp.
|
|
102
|
+
verbose (bool, optional): Whether to print debug information. Defaults to False.
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
str: Path to the saved audio file
|
|
106
|
+
|
|
107
|
+
Raises:
|
|
108
|
+
FileNotFoundError: If the audio file doesn't exist
|
|
109
|
+
"""
|
|
110
|
+
import shutil
|
|
111
|
+
import time
|
|
112
|
+
import asyncio
|
|
113
|
+
|
|
114
|
+
source_path = Path(audio_file)
|
|
115
|
+
|
|
116
|
+
if not source_path.exists():
|
|
117
|
+
raise FileNotFoundError(f"Audio file not found: {audio_file}")
|
|
118
|
+
|
|
119
|
+
if destination is None:
|
|
120
|
+
# Create a default destination with timestamp in current directory
|
|
121
|
+
timestamp = int(time.time())
|
|
122
|
+
destination = os.path.join(os.getcwd(), f"tts_audio_{timestamp}{source_path.suffix}")
|
|
123
|
+
|
|
124
|
+
# Ensure the destination directory exists
|
|
125
|
+
os.makedirs(os.path.dirname(os.path.abspath(destination)), exist_ok=True)
|
|
126
|
+
|
|
127
|
+
# Copy the file using asyncio to avoid blocking
|
|
128
|
+
await asyncio.to_thread(shutil.copy2, source_path, destination)
|
|
129
|
+
|
|
130
|
+
if verbose:
|
|
131
|
+
print(f"[debug] Audio saved to {destination}")
|
|
132
|
+
|
|
133
|
+
return destination
|
|
134
|
+
|
|
135
|
+
async def stream_audio(self, text: str, voice: str = None, chunk_size: int = 1024, verbose: bool = False):
|
|
136
|
+
"""
|
|
137
|
+
Stream audio in chunks asynchronously.
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
text (str): The text to convert to speech
|
|
141
|
+
voice (str, optional): The voice to use. Defaults to provider's default voice.
|
|
142
|
+
chunk_size (int, optional): Size of audio chunks to yield. Defaults to 1024.
|
|
143
|
+
verbose (bool, optional): Whether to print debug information. Defaults to False.
|
|
144
|
+
|
|
145
|
+
Yields:
|
|
146
|
+
AsyncGenerator[bytes, None]: Audio data chunks
|
|
147
|
+
"""
|
|
148
|
+
try:
|
|
149
|
+
import aiofiles
|
|
150
|
+
except ImportError:
|
|
151
|
+
raise ImportError("The 'aiofiles' package is required for async streaming. Install it with 'pip install aiofiles'.")
|
|
152
|
+
|
|
153
|
+
# Generate the audio file
|
|
154
|
+
audio_file = await self.tts(text, voice=voice, verbose=verbose)
|
|
155
|
+
|
|
156
|
+
# Stream the file in chunks
|
|
157
|
+
async with aiofiles.open(audio_file, 'rb') as f:
|
|
158
|
+
while chunk := await f.read(chunk_size):
|
|
159
|
+
yield chunk
|