webscout 8.3.7__py3-none-any.whl → 2025.10.13__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/AIauto.py +250 -250
- webscout/AIbase.py +379 -379
- webscout/AIutel.py +60 -60
- webscout/Bard.py +1012 -1012
- webscout/Bing_search.py +417 -417
- webscout/DWEBS.py +529 -529
- webscout/Extra/Act.md +309 -309
- webscout/Extra/GitToolkit/__init__.py +10 -10
- webscout/Extra/GitToolkit/gitapi/README.md +110 -110
- webscout/Extra/GitToolkit/gitapi/__init__.py +11 -11
- webscout/Extra/GitToolkit/gitapi/repository.py +195 -195
- webscout/Extra/GitToolkit/gitapi/user.py +96 -96
- webscout/Extra/GitToolkit/gitapi/utils.py +61 -61
- webscout/Extra/YTToolkit/README.md +375 -375
- webscout/Extra/YTToolkit/YTdownloader.py +956 -956
- webscout/Extra/YTToolkit/__init__.py +2 -2
- webscout/Extra/YTToolkit/transcriber.py +475 -475
- webscout/Extra/YTToolkit/ytapi/README.md +44 -44
- webscout/Extra/YTToolkit/ytapi/__init__.py +6 -6
- webscout/Extra/YTToolkit/ytapi/channel.py +307 -307
- webscout/Extra/YTToolkit/ytapi/errors.py +13 -13
- webscout/Extra/YTToolkit/ytapi/extras.py +118 -118
- webscout/Extra/YTToolkit/ytapi/https.py +88 -88
- webscout/Extra/YTToolkit/ytapi/patterns.py +61 -61
- webscout/Extra/YTToolkit/ytapi/playlist.py +58 -58
- webscout/Extra/YTToolkit/ytapi/pool.py +7 -7
- webscout/Extra/YTToolkit/ytapi/query.py +39 -39
- webscout/Extra/YTToolkit/ytapi/stream.py +62 -62
- webscout/Extra/YTToolkit/ytapi/utils.py +62 -62
- webscout/Extra/YTToolkit/ytapi/video.py +232 -232
- webscout/Extra/autocoder/__init__.py +9 -9
- webscout/Extra/autocoder/autocoder.py +1105 -1105
- webscout/Extra/autocoder/autocoder_utiles.py +332 -332
- webscout/Extra/gguf.md +429 -429
- webscout/Extra/gguf.py +1213 -1213
- webscout/Extra/tempmail/README.md +487 -487
- webscout/Extra/tempmail/__init__.py +27 -27
- webscout/Extra/tempmail/async_utils.py +140 -140
- webscout/Extra/tempmail/base.py +160 -160
- webscout/Extra/tempmail/cli.py +186 -186
- webscout/Extra/tempmail/emailnator.py +84 -84
- webscout/Extra/tempmail/mail_tm.py +360 -360
- webscout/Extra/tempmail/temp_mail_io.py +291 -291
- webscout/Extra/weather.md +281 -281
- webscout/Extra/weather.py +193 -193
- webscout/Litlogger/README.md +10 -10
- webscout/Litlogger/__init__.py +15 -15
- webscout/Litlogger/formats.py +13 -13
- webscout/Litlogger/handlers.py +121 -121
- webscout/Litlogger/levels.py +13 -13
- webscout/Litlogger/logger.py +134 -134
- webscout/Provider/AISEARCH/Perplexity.py +332 -332
- webscout/Provider/AISEARCH/README.md +279 -279
- webscout/Provider/AISEARCH/__init__.py +16 -1
- webscout/Provider/AISEARCH/felo_search.py +206 -206
- webscout/Provider/AISEARCH/genspark_search.py +323 -323
- webscout/Provider/AISEARCH/hika_search.py +185 -185
- webscout/Provider/AISEARCH/iask_search.py +410 -410
- webscout/Provider/AISEARCH/monica_search.py +219 -219
- webscout/Provider/AISEARCH/scira_search.py +316 -316
- webscout/Provider/AISEARCH/stellar_search.py +177 -177
- webscout/Provider/AISEARCH/webpilotai_search.py +255 -255
- webscout/Provider/Aitopia.py +314 -314
- webscout/Provider/Andi.py +1 -1
- webscout/Provider/Apriel.py +306 -0
- webscout/Provider/ChatGPTClone.py +237 -236
- webscout/Provider/ChatSandbox.py +343 -343
- webscout/Provider/Cloudflare.py +324 -324
- webscout/Provider/Cohere.py +208 -208
- webscout/Provider/Deepinfra.py +370 -366
- webscout/Provider/ExaAI.py +260 -260
- webscout/Provider/ExaChat.py +308 -308
- webscout/Provider/Flowith.py +221 -221
- webscout/Provider/GMI.py +293 -0
- webscout/Provider/Gemini.py +164 -164
- webscout/Provider/GeminiProxy.py +167 -167
- webscout/Provider/GithubChat.py +371 -372
- webscout/Provider/Groq.py +800 -800
- webscout/Provider/HeckAI.py +383 -383
- webscout/Provider/Jadve.py +282 -282
- webscout/Provider/K2Think.py +307 -307
- webscout/Provider/Koboldai.py +205 -205
- webscout/Provider/LambdaChat.py +423 -423
- webscout/Provider/Nemotron.py +244 -244
- webscout/Provider/Netwrck.py +248 -248
- webscout/Provider/OLLAMA.py +395 -395
- webscout/Provider/OPENAI/Cloudflare.py +393 -393
- webscout/Provider/OPENAI/FalconH1.py +451 -451
- webscout/Provider/OPENAI/FreeGemini.py +296 -296
- webscout/Provider/OPENAI/K2Think.py +431 -431
- webscout/Provider/OPENAI/NEMOTRON.py +240 -240
- webscout/Provider/OPENAI/PI.py +427 -427
- webscout/Provider/OPENAI/README.md +959 -959
- webscout/Provider/OPENAI/TogetherAI.py +345 -345
- webscout/Provider/OPENAI/TwoAI.py +465 -465
- webscout/Provider/OPENAI/__init__.py +33 -18
- webscout/Provider/OPENAI/base.py +248 -248
- webscout/Provider/OPENAI/chatglm.py +528 -0
- webscout/Provider/OPENAI/chatgpt.py +592 -592
- webscout/Provider/OPENAI/chatgptclone.py +521 -521
- webscout/Provider/OPENAI/chatsandbox.py +202 -202
- webscout/Provider/OPENAI/deepinfra.py +318 -314
- webscout/Provider/OPENAI/e2b.py +1665 -1665
- webscout/Provider/OPENAI/exaai.py +420 -420
- webscout/Provider/OPENAI/exachat.py +452 -452
- webscout/Provider/OPENAI/friendli.py +232 -232
- webscout/Provider/OPENAI/{refact.py → gmi.py} +324 -274
- webscout/Provider/OPENAI/groq.py +364 -364
- webscout/Provider/OPENAI/heckai.py +314 -314
- webscout/Provider/OPENAI/llmchatco.py +337 -337
- webscout/Provider/OPENAI/netwrck.py +355 -355
- webscout/Provider/OPENAI/oivscode.py +290 -290
- webscout/Provider/OPENAI/opkfc.py +518 -518
- webscout/Provider/OPENAI/pydantic_imports.py +1 -1
- webscout/Provider/OPENAI/scirachat.py +535 -535
- webscout/Provider/OPENAI/sonus.py +308 -308
- webscout/Provider/OPENAI/standardinput.py +442 -442
- webscout/Provider/OPENAI/textpollinations.py +340 -340
- webscout/Provider/OPENAI/toolbaz.py +419 -416
- webscout/Provider/OPENAI/typefully.py +362 -362
- webscout/Provider/OPENAI/utils.py +295 -295
- webscout/Provider/OPENAI/venice.py +436 -436
- webscout/Provider/OPENAI/wisecat.py +387 -387
- webscout/Provider/OPENAI/writecream.py +166 -166
- webscout/Provider/OPENAI/x0gpt.py +378 -378
- webscout/Provider/OPENAI/yep.py +389 -389
- webscout/Provider/OpenGPT.py +230 -230
- webscout/Provider/Openai.py +243 -243
- webscout/Provider/PI.py +405 -405
- webscout/Provider/Perplexitylabs.py +430 -430
- webscout/Provider/QwenLM.py +272 -272
- webscout/Provider/STT/__init__.py +16 -1
- webscout/Provider/Sambanova.py +257 -257
- webscout/Provider/StandardInput.py +309 -309
- webscout/Provider/TTI/README.md +82 -82
- webscout/Provider/TTI/__init__.py +33 -18
- webscout/Provider/TTI/aiarta.py +413 -413
- webscout/Provider/TTI/base.py +136 -136
- webscout/Provider/TTI/bing.py +243 -243
- webscout/Provider/TTI/gpt1image.py +149 -149
- webscout/Provider/TTI/imagen.py +196 -196
- webscout/Provider/TTI/infip.py +211 -211
- webscout/Provider/TTI/magicstudio.py +232 -232
- webscout/Provider/TTI/monochat.py +219 -219
- webscout/Provider/TTI/piclumen.py +214 -214
- webscout/Provider/TTI/pixelmuse.py +232 -232
- webscout/Provider/TTI/pollinations.py +232 -232
- webscout/Provider/TTI/together.py +288 -288
- webscout/Provider/TTI/utils.py +12 -12
- webscout/Provider/TTI/venice.py +367 -367
- webscout/Provider/TTS/README.md +192 -192
- webscout/Provider/TTS/__init__.py +33 -18
- webscout/Provider/TTS/parler.py +110 -110
- webscout/Provider/TTS/streamElements.py +333 -333
- webscout/Provider/TTS/utils.py +280 -280
- webscout/Provider/TeachAnything.py +237 -237
- webscout/Provider/TextPollinationsAI.py +310 -310
- webscout/Provider/TogetherAI.py +356 -356
- webscout/Provider/TwoAI.py +312 -312
- webscout/Provider/TypliAI.py +311 -311
- webscout/Provider/UNFINISHED/ChatHub.py +208 -208
- webscout/Provider/UNFINISHED/ChutesAI.py +313 -313
- webscout/Provider/UNFINISHED/GizAI.py +294 -294
- webscout/Provider/UNFINISHED/Marcus.py +198 -198
- webscout/Provider/UNFINISHED/Qodo.py +477 -477
- webscout/Provider/UNFINISHED/VercelAIGateway.py +338 -338
- webscout/Provider/UNFINISHED/XenAI.py +324 -324
- webscout/Provider/UNFINISHED/Youchat.py +330 -330
- webscout/Provider/UNFINISHED/liner.py +334 -0
- webscout/Provider/UNFINISHED/liner_api_request.py +262 -262
- webscout/Provider/UNFINISHED/puterjs.py +634 -634
- webscout/Provider/UNFINISHED/samurai.py +223 -223
- webscout/Provider/UNFINISHED/test_lmarena.py +119 -119
- webscout/Provider/Venice.py +250 -250
- webscout/Provider/VercelAI.py +256 -256
- webscout/Provider/WiseCat.py +231 -231
- webscout/Provider/WrDoChat.py +366 -366
- webscout/Provider/__init__.py +33 -18
- webscout/Provider/ai4chat.py +174 -174
- webscout/Provider/akashgpt.py +331 -331
- webscout/Provider/cerebras.py +446 -446
- webscout/Provider/chatglm.py +394 -301
- webscout/Provider/cleeai.py +211 -211
- webscout/Provider/elmo.py +282 -282
- webscout/Provider/geminiapi.py +208 -208
- webscout/Provider/granite.py +261 -261
- webscout/Provider/hermes.py +263 -263
- webscout/Provider/julius.py +223 -223
- webscout/Provider/learnfastai.py +309 -309
- webscout/Provider/llama3mitril.py +214 -214
- webscout/Provider/llmchat.py +243 -243
- webscout/Provider/llmchatco.py +290 -290
- webscout/Provider/meta.py +801 -801
- webscout/Provider/oivscode.py +309 -309
- webscout/Provider/scira_chat.py +383 -383
- webscout/Provider/searchchat.py +292 -292
- webscout/Provider/sonus.py +258 -258
- webscout/Provider/toolbaz.py +370 -367
- webscout/Provider/turboseek.py +273 -273
- webscout/Provider/typefully.py +207 -207
- webscout/Provider/yep.py +372 -372
- webscout/__init__.py +27 -31
- webscout/__main__.py +5 -5
- webscout/auth/api_key_manager.py +189 -189
- webscout/auth/config.py +175 -175
- webscout/auth/models.py +185 -185
- webscout/auth/routes.py +663 -664
- webscout/auth/simple_logger.py +236 -236
- webscout/cli.py +523 -523
- webscout/conversation.py +438 -438
- webscout/exceptions.py +361 -361
- webscout/litagent/Readme.md +298 -298
- webscout/litagent/__init__.py +28 -28
- webscout/litagent/agent.py +581 -581
- webscout/litagent/constants.py +59 -59
- webscout/litprinter/__init__.py +58 -58
- webscout/models.py +181 -181
- webscout/optimizers.py +419 -419
- webscout/prompt_manager.py +288 -288
- webscout/sanitize.py +1078 -1078
- webscout/scout/README.md +401 -401
- webscout/scout/__init__.py +8 -8
- webscout/scout/core/__init__.py +6 -6
- webscout/scout/core/crawler.py +297 -297
- webscout/scout/core/scout.py +706 -706
- webscout/scout/core/search_result.py +95 -95
- webscout/scout/core/text_analyzer.py +62 -62
- webscout/scout/core/text_utils.py +277 -277
- webscout/scout/core/web_analyzer.py +51 -51
- webscout/scout/element.py +599 -599
- webscout/scout/parsers/__init__.py +69 -69
- webscout/scout/parsers/html5lib_parser.py +172 -172
- webscout/scout/parsers/html_parser.py +236 -236
- webscout/scout/parsers/lxml_parser.py +178 -178
- webscout/scout/utils.py +37 -37
- webscout/search/__init__.py +51 -0
- webscout/search/base.py +195 -0
- webscout/search/duckduckgo_main.py +54 -0
- webscout/search/engines/__init__.py +48 -0
- webscout/search/engines/bing.py +84 -0
- webscout/search/engines/bing_news.py +52 -0
- webscout/search/engines/brave.py +43 -0
- webscout/search/engines/duckduckgo/__init__.py +25 -0
- webscout/search/engines/duckduckgo/answers.py +78 -0
- webscout/search/engines/duckduckgo/base.py +187 -0
- webscout/search/engines/duckduckgo/images.py +97 -0
- webscout/search/engines/duckduckgo/maps.py +168 -0
- webscout/search/engines/duckduckgo/news.py +68 -0
- webscout/search/engines/duckduckgo/suggestions.py +21 -0
- webscout/search/engines/duckduckgo/text.py +211 -0
- webscout/search/engines/duckduckgo/translate.py +47 -0
- webscout/search/engines/duckduckgo/videos.py +63 -0
- webscout/search/engines/duckduckgo/weather.py +74 -0
- webscout/search/engines/mojeek.py +37 -0
- webscout/search/engines/wikipedia.py +56 -0
- webscout/search/engines/yahoo.py +65 -0
- webscout/search/engines/yahoo_news.py +64 -0
- webscout/search/engines/yandex.py +43 -0
- webscout/search/engines/yep/__init__.py +13 -0
- webscout/search/engines/yep/base.py +32 -0
- webscout/search/engines/yep/images.py +99 -0
- webscout/search/engines/yep/suggestions.py +35 -0
- webscout/search/engines/yep/text.py +114 -0
- webscout/search/http_client.py +156 -0
- webscout/search/results.py +137 -0
- webscout/search/yep_main.py +44 -0
- webscout/swiftcli/Readme.md +323 -323
- webscout/swiftcli/__init__.py +95 -95
- webscout/swiftcli/core/__init__.py +7 -7
- webscout/swiftcli/core/cli.py +308 -308
- webscout/swiftcli/core/context.py +104 -104
- webscout/swiftcli/core/group.py +241 -241
- webscout/swiftcli/decorators/__init__.py +28 -28
- webscout/swiftcli/decorators/command.py +221 -221
- webscout/swiftcli/decorators/options.py +220 -220
- webscout/swiftcli/decorators/output.py +302 -302
- webscout/swiftcli/exceptions.py +21 -21
- webscout/swiftcli/plugins/__init__.py +9 -9
- webscout/swiftcli/plugins/base.py +135 -135
- webscout/swiftcli/plugins/manager.py +269 -269
- webscout/swiftcli/utils/__init__.py +59 -59
- webscout/swiftcli/utils/formatting.py +252 -252
- webscout/swiftcli/utils/parsing.py +267 -267
- webscout/update_checker.py +117 -117
- webscout/version.py +1 -1
- webscout/version.py.bak +2 -0
- webscout/zeroart/README.md +89 -89
- webscout/zeroart/__init__.py +134 -134
- webscout/zeroart/base.py +66 -66
- webscout/zeroart/effects.py +100 -100
- webscout/zeroart/fonts.py +1238 -1238
- {webscout-8.3.7.dist-info → webscout-2025.10.13.dist-info}/METADATA +936 -937
- webscout-2025.10.13.dist-info/RECORD +329 -0
- webscout/Provider/AISEARCH/DeepFind.py +0 -254
- webscout/Provider/OPENAI/Qwen3.py +0 -303
- webscout/Provider/OPENAI/qodo.py +0 -630
- webscout/Provider/OPENAI/xenai.py +0 -514
- webscout/tempid.py +0 -134
- webscout/webscout_search.py +0 -1183
- webscout/webscout_search_async.py +0 -649
- webscout/yep_search.py +0 -346
- webscout-8.3.7.dist-info/RECORD +0 -301
- {webscout-8.3.7.dist-info → webscout-2025.10.13.dist-info}/WHEEL +0 -0
- {webscout-8.3.7.dist-info → webscout-2025.10.13.dist-info}/entry_points.txt +0 -0
- {webscout-8.3.7.dist-info → webscout-2025.10.13.dist-info}/licenses/LICENSE.md +0 -0
- {webscout-8.3.7.dist-info → webscout-2025.10.13.dist-info}/top_level.txt +0 -0
|
@@ -1,232 +1,232 @@
|
|
|
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 (
|
|
6
|
-
ImageData,
|
|
7
|
-
ImageResponse
|
|
8
|
-
)
|
|
9
|
-
from webscout.Provider.TTI.base import TTICompatibleProvider, BaseImages
|
|
10
|
-
from io import BytesIO
|
|
11
|
-
import os
|
|
12
|
-
import tempfile
|
|
13
|
-
from webscout.litagent import LitAgent
|
|
14
|
-
import time
|
|
15
|
-
import json
|
|
16
|
-
import random
|
|
17
|
-
|
|
18
|
-
try:
|
|
19
|
-
from PIL import Image
|
|
20
|
-
except ImportError:
|
|
21
|
-
Image = None
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
class Images(BaseImages):
|
|
25
|
-
def __init__(self, client):
|
|
26
|
-
self._client = client
|
|
27
|
-
|
|
28
|
-
def create(
|
|
29
|
-
self,
|
|
30
|
-
*,
|
|
31
|
-
model: str,
|
|
32
|
-
prompt: str,
|
|
33
|
-
n: int = 1,
|
|
34
|
-
size: str = "1024x1024",
|
|
35
|
-
response_format: str = "url",
|
|
36
|
-
user: Optional[str] = None,
|
|
37
|
-
style: str = "none",
|
|
38
|
-
aspect_ratio: str = "1:1",
|
|
39
|
-
timeout: int = 60,
|
|
40
|
-
image_format: str = "png",
|
|
41
|
-
seed: Optional[int] = None,
|
|
42
|
-
**kwargs,
|
|
43
|
-
) -> ImageResponse:
|
|
44
|
-
"""
|
|
45
|
-
image_format: "png" or "jpeg"
|
|
46
|
-
seed: Optional random seed for reproducibility. If not provided, a random seed is used.
|
|
47
|
-
"""
|
|
48
|
-
if Image is None:
|
|
49
|
-
raise ImportError("Pillow (PIL) is required for image format conversion.")
|
|
50
|
-
|
|
51
|
-
images = []
|
|
52
|
-
urls = []
|
|
53
|
-
|
|
54
|
-
def upload_file_with_retry(img_bytes, image_format, max_retries=3):
|
|
55
|
-
ext = "jpg" if image_format.lower() == "jpeg" else "png"
|
|
56
|
-
for attempt in range(max_retries):
|
|
57
|
-
tmp_path = None
|
|
58
|
-
try:
|
|
59
|
-
with tempfile.NamedTemporaryFile(
|
|
60
|
-
suffix=f".{ext}", delete=False
|
|
61
|
-
) as tmp:
|
|
62
|
-
tmp.write(img_bytes)
|
|
63
|
-
tmp.flush()
|
|
64
|
-
tmp_path = tmp.name
|
|
65
|
-
with open(tmp_path, "rb") as f:
|
|
66
|
-
files = {"fileToUpload": (f"image.{ext}", f, f"image/{ext}")}
|
|
67
|
-
data = {"reqtype": "fileupload", "json": "true"}
|
|
68
|
-
headers = {"User-Agent": LitAgent().random()}
|
|
69
|
-
if attempt > 0:
|
|
70
|
-
headers["Connection"] = "close"
|
|
71
|
-
resp = requests.post(
|
|
72
|
-
"https://catbox.moe/user/api.php",
|
|
73
|
-
files=files,
|
|
74
|
-
data=data,
|
|
75
|
-
headers=headers,
|
|
76
|
-
timeout=timeout,
|
|
77
|
-
)
|
|
78
|
-
if resp.status_code == 200 and resp.text.strip():
|
|
79
|
-
text = resp.text.strip()
|
|
80
|
-
if text.startswith("http"):
|
|
81
|
-
return text
|
|
82
|
-
try:
|
|
83
|
-
result = resp.json()
|
|
84
|
-
if "url" in result:
|
|
85
|
-
return result["url"]
|
|
86
|
-
except json.JSONDecodeError:
|
|
87
|
-
if "http" in text:
|
|
88
|
-
return text
|
|
89
|
-
except Exception:
|
|
90
|
-
if attempt < max_retries - 1:
|
|
91
|
-
time.sleep(1 * (attempt + 1))
|
|
92
|
-
finally:
|
|
93
|
-
if tmp_path and os.path.isfile(tmp_path):
|
|
94
|
-
try:
|
|
95
|
-
os.remove(tmp_path)
|
|
96
|
-
except Exception:
|
|
97
|
-
pass
|
|
98
|
-
return None
|
|
99
|
-
|
|
100
|
-
def upload_file_alternative(img_bytes, image_format):
|
|
101
|
-
try:
|
|
102
|
-
ext = "jpg" if image_format.lower() == "jpeg" else "png"
|
|
103
|
-
with tempfile.NamedTemporaryFile(suffix=f".{ext}", delete=False) as tmp:
|
|
104
|
-
tmp.write(img_bytes)
|
|
105
|
-
tmp.flush()
|
|
106
|
-
tmp_path = tmp.name
|
|
107
|
-
try:
|
|
108
|
-
if not os.path.isfile(tmp_path):
|
|
109
|
-
return None
|
|
110
|
-
with open(tmp_path, "rb") as img_file:
|
|
111
|
-
files = {"file": img_file}
|
|
112
|
-
response = requests.post("https://0x0.st", files=files)
|
|
113
|
-
response.raise_for_status()
|
|
114
|
-
image_url = response.text.strip()
|
|
115
|
-
if not image_url.startswith("http"):
|
|
116
|
-
return None
|
|
117
|
-
return image_url
|
|
118
|
-
except Exception:
|
|
119
|
-
return None
|
|
120
|
-
finally:
|
|
121
|
-
try:
|
|
122
|
-
os.remove(tmp_path)
|
|
123
|
-
except Exception:
|
|
124
|
-
pass
|
|
125
|
-
except Exception:
|
|
126
|
-
return None
|
|
127
|
-
|
|
128
|
-
for i in range(n):
|
|
129
|
-
# Prepare parameters for Pollinations API
|
|
130
|
-
params = {
|
|
131
|
-
"model": model,
|
|
132
|
-
"width": int(size.split("x")[0]) if "x" in size else 1024,
|
|
133
|
-
"height": int(size.split("x")[1]) if "x" in size else 1024,
|
|
134
|
-
"seed": seed if seed is not None else random.randint(0, 2**32 - 1),
|
|
135
|
-
}
|
|
136
|
-
# Build the API URL
|
|
137
|
-
base_url = f"https://image.pollinations.ai/prompt/{prompt}"
|
|
138
|
-
# Compose query string
|
|
139
|
-
query = "&".join(f"{k}={v}" for k, v in params.items())
|
|
140
|
-
url = f"{base_url}?{query}"
|
|
141
|
-
try:
|
|
142
|
-
resp = self._client.session.get(
|
|
143
|
-
url,
|
|
144
|
-
timeout=timeout,
|
|
145
|
-
)
|
|
146
|
-
resp.raise_for_status()
|
|
147
|
-
img_bytes = resp.content
|
|
148
|
-
except RequestException as e:
|
|
149
|
-
raise RuntimeError(f"Failed to fetch image from Pollinations API: {e}")
|
|
150
|
-
|
|
151
|
-
# Convert to png or jpeg in memory
|
|
152
|
-
with BytesIO(img_bytes) as input_io:
|
|
153
|
-
with Image.open(input_io) as im:
|
|
154
|
-
out_io = BytesIO()
|
|
155
|
-
if image_format.lower() == "jpeg":
|
|
156
|
-
im = im.convert("RGB")
|
|
157
|
-
im.save(out_io, format="JPEG")
|
|
158
|
-
else:
|
|
159
|
-
im.save(out_io, format="PNG")
|
|
160
|
-
img_bytes = out_io.getvalue()
|
|
161
|
-
|
|
162
|
-
images.append(img_bytes)
|
|
163
|
-
|
|
164
|
-
if response_format == "url":
|
|
165
|
-
uploaded_url = upload_file_with_retry(img_bytes, image_format)
|
|
166
|
-
if not uploaded_url:
|
|
167
|
-
uploaded_url = upload_file_alternative(img_bytes, image_format)
|
|
168
|
-
if uploaded_url:
|
|
169
|
-
urls.append(uploaded_url)
|
|
170
|
-
else:
|
|
171
|
-
raise RuntimeError(
|
|
172
|
-
"Failed to upload image to catbox.moe using all available methods"
|
|
173
|
-
)
|
|
174
|
-
|
|
175
|
-
result_data = []
|
|
176
|
-
if response_format == "url":
|
|
177
|
-
for url in urls:
|
|
178
|
-
result_data.append(ImageData(url=url))
|
|
179
|
-
elif response_format == "b64_json":
|
|
180
|
-
import base64
|
|
181
|
-
|
|
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
|
-
|
|
188
|
-
from time import time as _time
|
|
189
|
-
|
|
190
|
-
return ImageResponse(created=int(_time()), data=result_data)
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
class PollinationsAI(TTICompatibleProvider):
|
|
194
|
-
AVAILABLE_MODELS = ["flux", "turbo", "gptimage"]
|
|
195
|
-
|
|
196
|
-
def __init__(self):
|
|
197
|
-
self.api_endpoint = "https://image.pollinations.ai/prompt"
|
|
198
|
-
self.session = requests.Session()
|
|
199
|
-
self.user_agent = LitAgent().random()
|
|
200
|
-
self.headers = {
|
|
201
|
-
"accept": "*/*",
|
|
202
|
-
"accept-language": "en-US,en;q=0.9",
|
|
203
|
-
"content-type": "application/json",
|
|
204
|
-
"origin": "https://image.pollinations.ai",
|
|
205
|
-
"referer": "https://image.pollinations.ai/",
|
|
206
|
-
"user-agent": self.user_agent,
|
|
207
|
-
}
|
|
208
|
-
self.session.headers.update(self.headers)
|
|
209
|
-
self.images = Images(self)
|
|
210
|
-
|
|
211
|
-
@property
|
|
212
|
-
def models(self):
|
|
213
|
-
class _ModelList:
|
|
214
|
-
def list(inner_self):
|
|
215
|
-
return type(self).AVAILABLE_MODELS
|
|
216
|
-
|
|
217
|
-
return _ModelList()
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
if __name__ == "__main__":
|
|
221
|
-
from rich import print
|
|
222
|
-
|
|
223
|
-
client = PollinationsAI()
|
|
224
|
-
response = client.images.create(
|
|
225
|
-
model="flux",
|
|
226
|
-
prompt="a japanese waifu in short kimono clothes",
|
|
227
|
-
response_format="url",
|
|
228
|
-
n=4,
|
|
229
|
-
timeout=30,
|
|
230
|
-
seed=None, # You can set a specific seed for reproducibility
|
|
231
|
-
)
|
|
232
|
-
print(response)
|
|
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 (
|
|
6
|
+
ImageData,
|
|
7
|
+
ImageResponse
|
|
8
|
+
)
|
|
9
|
+
from webscout.Provider.TTI.base import TTICompatibleProvider, BaseImages
|
|
10
|
+
from io import BytesIO
|
|
11
|
+
import os
|
|
12
|
+
import tempfile
|
|
13
|
+
from webscout.litagent import LitAgent
|
|
14
|
+
import time
|
|
15
|
+
import json
|
|
16
|
+
import random
|
|
17
|
+
|
|
18
|
+
try:
|
|
19
|
+
from PIL import Image
|
|
20
|
+
except ImportError:
|
|
21
|
+
Image = None
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class Images(BaseImages):
|
|
25
|
+
def __init__(self, client):
|
|
26
|
+
self._client = client
|
|
27
|
+
|
|
28
|
+
def create(
|
|
29
|
+
self,
|
|
30
|
+
*,
|
|
31
|
+
model: str,
|
|
32
|
+
prompt: str,
|
|
33
|
+
n: int = 1,
|
|
34
|
+
size: str = "1024x1024",
|
|
35
|
+
response_format: str = "url",
|
|
36
|
+
user: Optional[str] = None,
|
|
37
|
+
style: str = "none",
|
|
38
|
+
aspect_ratio: str = "1:1",
|
|
39
|
+
timeout: int = 60,
|
|
40
|
+
image_format: str = "png",
|
|
41
|
+
seed: Optional[int] = None,
|
|
42
|
+
**kwargs,
|
|
43
|
+
) -> ImageResponse:
|
|
44
|
+
"""
|
|
45
|
+
image_format: "png" or "jpeg"
|
|
46
|
+
seed: Optional random seed for reproducibility. If not provided, a random seed is used.
|
|
47
|
+
"""
|
|
48
|
+
if Image is None:
|
|
49
|
+
raise ImportError("Pillow (PIL) is required for image format conversion.")
|
|
50
|
+
|
|
51
|
+
images = []
|
|
52
|
+
urls = []
|
|
53
|
+
|
|
54
|
+
def upload_file_with_retry(img_bytes, image_format, max_retries=3):
|
|
55
|
+
ext = "jpg" if image_format.lower() == "jpeg" else "png"
|
|
56
|
+
for attempt in range(max_retries):
|
|
57
|
+
tmp_path = None
|
|
58
|
+
try:
|
|
59
|
+
with tempfile.NamedTemporaryFile(
|
|
60
|
+
suffix=f".{ext}", delete=False
|
|
61
|
+
) as tmp:
|
|
62
|
+
tmp.write(img_bytes)
|
|
63
|
+
tmp.flush()
|
|
64
|
+
tmp_path = tmp.name
|
|
65
|
+
with open(tmp_path, "rb") as f:
|
|
66
|
+
files = {"fileToUpload": (f"image.{ext}", f, f"image/{ext}")}
|
|
67
|
+
data = {"reqtype": "fileupload", "json": "true"}
|
|
68
|
+
headers = {"User-Agent": LitAgent().random()}
|
|
69
|
+
if attempt > 0:
|
|
70
|
+
headers["Connection"] = "close"
|
|
71
|
+
resp = requests.post(
|
|
72
|
+
"https://catbox.moe/user/api.php",
|
|
73
|
+
files=files,
|
|
74
|
+
data=data,
|
|
75
|
+
headers=headers,
|
|
76
|
+
timeout=timeout,
|
|
77
|
+
)
|
|
78
|
+
if resp.status_code == 200 and resp.text.strip():
|
|
79
|
+
text = resp.text.strip()
|
|
80
|
+
if text.startswith("http"):
|
|
81
|
+
return text
|
|
82
|
+
try:
|
|
83
|
+
result = resp.json()
|
|
84
|
+
if "url" in result:
|
|
85
|
+
return result["url"]
|
|
86
|
+
except json.JSONDecodeError:
|
|
87
|
+
if "http" in text:
|
|
88
|
+
return text
|
|
89
|
+
except Exception:
|
|
90
|
+
if attempt < max_retries - 1:
|
|
91
|
+
time.sleep(1 * (attempt + 1))
|
|
92
|
+
finally:
|
|
93
|
+
if tmp_path and os.path.isfile(tmp_path):
|
|
94
|
+
try:
|
|
95
|
+
os.remove(tmp_path)
|
|
96
|
+
except Exception:
|
|
97
|
+
pass
|
|
98
|
+
return None
|
|
99
|
+
|
|
100
|
+
def upload_file_alternative(img_bytes, image_format):
|
|
101
|
+
try:
|
|
102
|
+
ext = "jpg" if image_format.lower() == "jpeg" else "png"
|
|
103
|
+
with tempfile.NamedTemporaryFile(suffix=f".{ext}", delete=False) as tmp:
|
|
104
|
+
tmp.write(img_bytes)
|
|
105
|
+
tmp.flush()
|
|
106
|
+
tmp_path = tmp.name
|
|
107
|
+
try:
|
|
108
|
+
if not os.path.isfile(tmp_path):
|
|
109
|
+
return None
|
|
110
|
+
with open(tmp_path, "rb") as img_file:
|
|
111
|
+
files = {"file": img_file}
|
|
112
|
+
response = requests.post("https://0x0.st", files=files)
|
|
113
|
+
response.raise_for_status()
|
|
114
|
+
image_url = response.text.strip()
|
|
115
|
+
if not image_url.startswith("http"):
|
|
116
|
+
return None
|
|
117
|
+
return image_url
|
|
118
|
+
except Exception:
|
|
119
|
+
return None
|
|
120
|
+
finally:
|
|
121
|
+
try:
|
|
122
|
+
os.remove(tmp_path)
|
|
123
|
+
except Exception:
|
|
124
|
+
pass
|
|
125
|
+
except Exception:
|
|
126
|
+
return None
|
|
127
|
+
|
|
128
|
+
for i in range(n):
|
|
129
|
+
# Prepare parameters for Pollinations API
|
|
130
|
+
params = {
|
|
131
|
+
"model": model,
|
|
132
|
+
"width": int(size.split("x")[0]) if "x" in size else 1024,
|
|
133
|
+
"height": int(size.split("x")[1]) if "x" in size else 1024,
|
|
134
|
+
"seed": seed if seed is not None else random.randint(0, 2**32 - 1),
|
|
135
|
+
}
|
|
136
|
+
# Build the API URL
|
|
137
|
+
base_url = f"https://image.pollinations.ai/prompt/{prompt}"
|
|
138
|
+
# Compose query string
|
|
139
|
+
query = "&".join(f"{k}={v}" for k, v in params.items())
|
|
140
|
+
url = f"{base_url}?{query}"
|
|
141
|
+
try:
|
|
142
|
+
resp = self._client.session.get(
|
|
143
|
+
url,
|
|
144
|
+
timeout=timeout,
|
|
145
|
+
)
|
|
146
|
+
resp.raise_for_status()
|
|
147
|
+
img_bytes = resp.content
|
|
148
|
+
except RequestException as e:
|
|
149
|
+
raise RuntimeError(f"Failed to fetch image from Pollinations API: {e}")
|
|
150
|
+
|
|
151
|
+
# Convert to png or jpeg in memory
|
|
152
|
+
with BytesIO(img_bytes) as input_io:
|
|
153
|
+
with Image.open(input_io) as im:
|
|
154
|
+
out_io = BytesIO()
|
|
155
|
+
if image_format.lower() == "jpeg":
|
|
156
|
+
im = im.convert("RGB")
|
|
157
|
+
im.save(out_io, format="JPEG")
|
|
158
|
+
else:
|
|
159
|
+
im.save(out_io, format="PNG")
|
|
160
|
+
img_bytes = out_io.getvalue()
|
|
161
|
+
|
|
162
|
+
images.append(img_bytes)
|
|
163
|
+
|
|
164
|
+
if response_format == "url":
|
|
165
|
+
uploaded_url = upload_file_with_retry(img_bytes, image_format)
|
|
166
|
+
if not uploaded_url:
|
|
167
|
+
uploaded_url = upload_file_alternative(img_bytes, image_format)
|
|
168
|
+
if uploaded_url:
|
|
169
|
+
urls.append(uploaded_url)
|
|
170
|
+
else:
|
|
171
|
+
raise RuntimeError(
|
|
172
|
+
"Failed to upload image to catbox.moe using all available methods"
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
result_data = []
|
|
176
|
+
if response_format == "url":
|
|
177
|
+
for url in urls:
|
|
178
|
+
result_data.append(ImageData(url=url))
|
|
179
|
+
elif response_format == "b64_json":
|
|
180
|
+
import base64
|
|
181
|
+
|
|
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
|
+
|
|
188
|
+
from time import time as _time
|
|
189
|
+
|
|
190
|
+
return ImageResponse(created=int(_time()), data=result_data)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
class PollinationsAI(TTICompatibleProvider):
|
|
194
|
+
AVAILABLE_MODELS = ["flux", "turbo", "gptimage"]
|
|
195
|
+
|
|
196
|
+
def __init__(self):
|
|
197
|
+
self.api_endpoint = "https://image.pollinations.ai/prompt"
|
|
198
|
+
self.session = requests.Session()
|
|
199
|
+
self.user_agent = LitAgent().random()
|
|
200
|
+
self.headers = {
|
|
201
|
+
"accept": "*/*",
|
|
202
|
+
"accept-language": "en-US,en;q=0.9",
|
|
203
|
+
"content-type": "application/json",
|
|
204
|
+
"origin": "https://image.pollinations.ai",
|
|
205
|
+
"referer": "https://image.pollinations.ai/",
|
|
206
|
+
"user-agent": self.user_agent,
|
|
207
|
+
}
|
|
208
|
+
self.session.headers.update(self.headers)
|
|
209
|
+
self.images = Images(self)
|
|
210
|
+
|
|
211
|
+
@property
|
|
212
|
+
def models(self):
|
|
213
|
+
class _ModelList:
|
|
214
|
+
def list(inner_self):
|
|
215
|
+
return type(self).AVAILABLE_MODELS
|
|
216
|
+
|
|
217
|
+
return _ModelList()
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
if __name__ == "__main__":
|
|
221
|
+
from rich import print
|
|
222
|
+
|
|
223
|
+
client = PollinationsAI()
|
|
224
|
+
response = client.images.create(
|
|
225
|
+
model="flux",
|
|
226
|
+
prompt="a japanese waifu in short kimono clothes",
|
|
227
|
+
response_format="url",
|
|
228
|
+
n=4,
|
|
229
|
+
timeout=30,
|
|
230
|
+
seed=None, # You can set a specific seed for reproducibility
|
|
231
|
+
)
|
|
232
|
+
print(response)
|