webscout 7.4__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.
- webscout/AIauto.py +5 -53
- webscout/AIutel.py +8 -318
- webscout/DWEBS.py +460 -489
- webscout/Extra/YTToolkit/YTdownloader.py +14 -53
- webscout/Extra/YTToolkit/transcriber.py +12 -13
- webscout/Extra/YTToolkit/ytapi/video.py +0 -1
- webscout/Extra/__init__.py +0 -1
- webscout/Extra/autocoder/autocoder_utiles.py +0 -4
- webscout/Extra/autocoder/rawdog.py +13 -41
- webscout/Extra/gguf.py +652 -428
- webscout/Extra/weather.py +178 -156
- webscout/Extra/weather_ascii.py +70 -17
- webscout/Litlogger/core/logger.py +1 -2
- webscout/Litlogger/handlers/file.py +1 -1
- webscout/Litlogger/styles/formats.py +0 -2
- webscout/Litlogger/utils/detectors.py +0 -1
- webscout/Provider/AISEARCH/DeepFind.py +0 -1
- webscout/Provider/AISEARCH/ISou.py +1 -1
- webscout/Provider/AISEARCH/felo_search.py +0 -1
- webscout/Provider/AllenAI.py +24 -9
- webscout/Provider/C4ai.py +432 -0
- webscout/Provider/ChatGPTGratis.py +24 -56
- webscout/Provider/Cloudflare.py +18 -21
- webscout/Provider/DeepSeek.py +27 -48
- webscout/Provider/Deepinfra.py +129 -53
- webscout/Provider/Gemini.py +1 -1
- webscout/Provider/GithubChat.py +362 -0
- webscout/Provider/Glider.py +25 -8
- webscout/Provider/HF_space/qwen_qwen2.py +2 -2
- webscout/Provider/HeckAI.py +38 -5
- webscout/Provider/HuggingFaceChat.py +462 -0
- webscout/Provider/Jadve.py +20 -5
- webscout/Provider/Marcus.py +7 -50
- webscout/Provider/Netwrck.py +43 -67
- webscout/Provider/PI.py +4 -2
- webscout/Provider/Perplexitylabs.py +26 -6
- webscout/Provider/Phind.py +29 -3
- webscout/Provider/PizzaGPT.py +10 -51
- webscout/Provider/TTI/AiForce/async_aiforce.py +4 -37
- webscout/Provider/TTI/AiForce/sync_aiforce.py +41 -38
- webscout/Provider/TTI/FreeAIPlayground/__init__.py +9 -9
- webscout/Provider/TTI/FreeAIPlayground/async_freeaiplayground.py +206 -206
- webscout/Provider/TTI/FreeAIPlayground/sync_freeaiplayground.py +192 -192
- webscout/Provider/TTI/MagicStudio/__init__.py +2 -0
- webscout/Provider/TTI/MagicStudio/async_magicstudio.py +111 -0
- webscout/Provider/TTI/MagicStudio/sync_magicstudio.py +109 -0
- webscout/Provider/TTI/PollinationsAI/async_pollinations.py +5 -24
- webscout/Provider/TTI/PollinationsAI/sync_pollinations.py +2 -22
- webscout/Provider/TTI/__init__.py +2 -3
- webscout/Provider/TTI/aiarta/__init__.py +2 -0
- webscout/Provider/TTI/aiarta/async_aiarta.py +482 -0
- webscout/Provider/TTI/aiarta/sync_aiarta.py +440 -0
- webscout/Provider/TTI/fastflux/__init__.py +22 -0
- webscout/Provider/TTI/fastflux/async_fastflux.py +257 -0
- webscout/Provider/TTI/fastflux/sync_fastflux.py +247 -0
- webscout/Provider/TTS/__init__.py +2 -2
- webscout/Provider/TTS/deepgram.py +12 -39
- webscout/Provider/TTS/elevenlabs.py +14 -40
- webscout/Provider/TTS/gesserit.py +11 -35
- webscout/Provider/TTS/murfai.py +13 -39
- webscout/Provider/TTS/parler.py +17 -40
- webscout/Provider/TTS/speechma.py +180 -0
- webscout/Provider/TTS/streamElements.py +17 -44
- webscout/Provider/TextPollinationsAI.py +39 -59
- webscout/Provider/Venice.py +217 -200
- webscout/Provider/WiseCat.py +27 -5
- webscout/Provider/Youchat.py +63 -36
- webscout/Provider/__init__.py +13 -8
- webscout/Provider/akashgpt.py +28 -10
- webscout/Provider/copilot.py +416 -0
- webscout/Provider/flowith.py +196 -0
- webscout/Provider/freeaichat.py +32 -45
- webscout/Provider/granite.py +17 -53
- webscout/Provider/koala.py +20 -5
- webscout/Provider/llamatutor.py +7 -47
- webscout/Provider/llmchat.py +36 -53
- webscout/Provider/multichat.py +92 -98
- webscout/Provider/talkai.py +1 -0
- webscout/Provider/turboseek.py +3 -0
- webscout/Provider/tutorai.py +2 -0
- webscout/Provider/typegpt.py +154 -64
- webscout/Provider/x0gpt.py +3 -1
- webscout/Provider/yep.py +102 -20
- webscout/__init__.py +3 -0
- webscout/cli.py +4 -40
- webscout/conversation.py +1 -10
- webscout/exceptions.py +19 -9
- webscout/litagent/__init__.py +2 -2
- webscout/litagent/agent.py +351 -20
- webscout/litagent/constants.py +34 -5
- webscout/litprinter/__init__.py +0 -3
- webscout/models.py +181 -0
- webscout/optimizers.py +1 -1
- webscout/prompt_manager.py +2 -8
- webscout/scout/core/scout.py +1 -4
- webscout/scout/core/search_result.py +1 -1
- webscout/scout/core/text_utils.py +1 -1
- webscout/scout/core.py +2 -5
- webscout/scout/element.py +1 -1
- webscout/scout/parsers/html_parser.py +1 -1
- webscout/scout/utils.py +0 -1
- webscout/swiftcli/__init__.py +1 -3
- webscout/tempid.py +1 -1
- webscout/update_checker.py +55 -95
- webscout/version.py +1 -1
- webscout/webscout_search_async.py +1 -2
- webscout/yep_search.py +297 -297
- webscout-7.6.dist-info/LICENSE.md +146 -0
- {webscout-7.4.dist-info → webscout-7.6.dist-info}/METADATA +104 -514
- {webscout-7.4.dist-info → webscout-7.6.dist-info}/RECORD +113 -120
- webscout/Extra/autollama.py +0 -231
- webscout/Local/__init__.py +0 -10
- webscout/Local/_version.py +0 -3
- webscout/Local/formats.py +0 -747
- webscout/Local/model.py +0 -1368
- webscout/Local/samplers.py +0 -125
- webscout/Local/thread.py +0 -539
- webscout/Local/ui.py +0 -401
- webscout/Local/utils.py +0 -388
- webscout/Provider/Amigo.py +0 -274
- webscout/Provider/Bing.py +0 -243
- webscout/Provider/DiscordRocks.py +0 -253
- webscout/Provider/TTI/blackbox/__init__.py +0 -4
- webscout/Provider/TTI/blackbox/async_blackbox.py +0 -212
- webscout/Provider/TTI/blackbox/sync_blackbox.py +0 -199
- webscout/Provider/TTI/deepinfra/__init__.py +0 -4
- webscout/Provider/TTI/deepinfra/async_deepinfra.py +0 -227
- webscout/Provider/TTI/deepinfra/sync_deepinfra.py +0 -199
- webscout/Provider/TTI/imgninza/__init__.py +0 -4
- webscout/Provider/TTI/imgninza/async_ninza.py +0 -214
- webscout/Provider/TTI/imgninza/sync_ninza.py +0 -209
- webscout/Provider/TTS/voicepod.py +0 -117
- webscout/Provider/dgaf.py +0 -214
- webscout-7.4.dist-info/LICENSE.md +0 -211
- {webscout-7.4.dist-info → webscout-7.6.dist-info}/WHEEL +0 -0
- {webscout-7.4.dist-info → webscout-7.6.dist-info}/entry_points.txt +0 -0
- {webscout-7.4.dist-info → webscout-7.6.dist-info}/top_level.txt +0 -0
|
@@ -25,16 +25,8 @@ from pathlib import Path
|
|
|
25
25
|
from typing import AsyncGenerator
|
|
26
26
|
|
|
27
27
|
from webscout.AIbase import AsyncImageProvider
|
|
28
|
-
from webscout.Litlogger import Logger, LogFormat
|
|
29
28
|
from webscout.litagent import LitAgent
|
|
30
29
|
|
|
31
|
-
# Set up logging with cyberpunk style! 🎨
|
|
32
|
-
logger = Logger(
|
|
33
|
-
name="AsyncPollinationsAI",
|
|
34
|
-
format=LogFormat.MODERN_EMOJI,
|
|
35
|
-
|
|
36
|
-
)
|
|
37
|
-
|
|
38
30
|
# Get a fresh user agent! 🔄
|
|
39
31
|
agent = LitAgent()
|
|
40
32
|
|
|
@@ -91,20 +83,13 @@ class AsyncPollinationsAI(AsyncImageProvider):
|
|
|
91
83
|
- "any-dark": Dark/gothic style
|
|
92
84
|
- "flux-pro": Professional quality
|
|
93
85
|
- "turbo": Fast generation
|
|
94
|
-
|
|
86
|
+
|
|
95
87
|
"""
|
|
96
88
|
|
|
97
89
|
AVAILABLE_MODELS = [
|
|
98
90
|
"flux",
|
|
99
|
-
"flux-realism",
|
|
100
|
-
"flux-cablyai",
|
|
101
|
-
"flux-anime",
|
|
102
|
-
"flux-3d",
|
|
103
|
-
"any-dark",
|
|
104
|
-
"flux-pro",
|
|
105
91
|
"turbo"
|
|
106
92
|
]
|
|
107
|
-
DEFAULT_MODEL = "flux"
|
|
108
93
|
|
|
109
94
|
def __init__(
|
|
110
95
|
self,
|
|
@@ -141,7 +126,7 @@ class AsyncPollinationsAI(AsyncImageProvider):
|
|
|
141
126
|
additives: bool = True,
|
|
142
127
|
width: int = 768,
|
|
143
128
|
height: int = 768,
|
|
144
|
-
model: str =
|
|
129
|
+
model: str = "flux",
|
|
145
130
|
max_retries: int = 3,
|
|
146
131
|
retry_delay: int = 5,
|
|
147
132
|
negative_prompt: Optional[str] = None,
|
|
@@ -214,11 +199,10 @@ class AsyncPollinationsAI(AsyncImageProvider):
|
|
|
214
199
|
params_str = "&".join(f"{k}={v}" for k, v in base_params.items())
|
|
215
200
|
url = f"{self.image_gen_endpoint.format(prompt=current_prompt)}?{params_str}"
|
|
216
201
|
|
|
217
|
-
logger.info(f"Queueing image {_ + 1}/{amount} generation... ⚡")
|
|
218
202
|
tasks.append(self._generate_single(session, url, max_retries, retry_delay))
|
|
219
203
|
|
|
220
204
|
response = await asyncio.gather(*tasks)
|
|
221
|
-
|
|
205
|
+
|
|
222
206
|
|
|
223
207
|
return response
|
|
224
208
|
|
|
@@ -237,9 +221,9 @@ class AsyncPollinationsAI(AsyncImageProvider):
|
|
|
237
221
|
return await resp.read()
|
|
238
222
|
except aiohttp.ClientError as e:
|
|
239
223
|
if attempt == max_retries - 1:
|
|
240
|
-
|
|
224
|
+
|
|
241
225
|
raise
|
|
242
|
-
|
|
226
|
+
|
|
243
227
|
await asyncio.sleep(retry_delay)
|
|
244
228
|
|
|
245
229
|
async def save(
|
|
@@ -277,7 +261,6 @@ class AsyncPollinationsAI(AsyncImageProvider):
|
|
|
277
261
|
save_dir = dir if dir else os.getcwd()
|
|
278
262
|
if not os.path.exists(save_dir):
|
|
279
263
|
os.makedirs(save_dir)
|
|
280
|
-
logger.info(f"Created directory: {save_dir} 📁")
|
|
281
264
|
|
|
282
265
|
saved_paths = []
|
|
283
266
|
timestamp = int(time.time())
|
|
@@ -295,7 +278,6 @@ class AsyncPollinationsAI(AsyncImageProvider):
|
|
|
295
278
|
with open(filepath, "wb") as f:
|
|
296
279
|
f.write(image_bytes)
|
|
297
280
|
|
|
298
|
-
logger.success(f"Saved image to: {filepath} 💾")
|
|
299
281
|
return filepath
|
|
300
282
|
|
|
301
283
|
# Handle both List[bytes] and AsyncGenerator
|
|
@@ -306,7 +288,6 @@ class AsyncPollinationsAI(AsyncImageProvider):
|
|
|
306
288
|
|
|
307
289
|
tasks = [save_single_image(img, i) for i, img in enumerate(image_list)]
|
|
308
290
|
saved_paths = await asyncio.gather(*tasks)
|
|
309
|
-
logger.success(f"Successfully saved all {len(saved_paths)} images! 🎉")
|
|
310
291
|
return saved_paths
|
|
311
292
|
|
|
312
293
|
|
|
@@ -28,15 +28,8 @@ from requests.exceptions import RequestException
|
|
|
28
28
|
from pathlib import Path
|
|
29
29
|
|
|
30
30
|
from webscout.AIbase import ImageProvider
|
|
31
|
-
from webscout.Litlogger import Logger, LogFormat
|
|
32
31
|
from webscout.litagent import LitAgent
|
|
33
32
|
|
|
34
|
-
# Set up logging with cyberpunk style! 🎨
|
|
35
|
-
logger = Logger(
|
|
36
|
-
name="PollinationsAI",
|
|
37
|
-
format=LogFormat.MODERN_EMOJI,
|
|
38
|
-
|
|
39
|
-
)
|
|
40
33
|
|
|
41
34
|
# Get a fresh user agent! 🔄
|
|
42
35
|
agent = LitAgent()
|
|
@@ -83,20 +76,13 @@ class PollinationsAI(ImageProvider):
|
|
|
83
76
|
- "any-dark": Dark/gothic style
|
|
84
77
|
- "flux-pro": Professional quality
|
|
85
78
|
- "turbo": Fast generation
|
|
86
|
-
DEFAULT_MODEL (str): Default model to use ("flux")
|
|
87
79
|
"""
|
|
88
80
|
|
|
89
81
|
AVAILABLE_MODELS = [
|
|
90
82
|
"flux",
|
|
91
|
-
"flux-realism",
|
|
92
|
-
"flux-cablyai",
|
|
93
|
-
"flux-anime",
|
|
94
|
-
"flux-3d",
|
|
95
|
-
"any-dark",
|
|
96
|
-
"flux-pro",
|
|
97
83
|
"turbo"
|
|
98
84
|
]
|
|
99
|
-
|
|
85
|
+
|
|
100
86
|
|
|
101
87
|
def __init__(
|
|
102
88
|
self,
|
|
@@ -136,7 +122,7 @@ class PollinationsAI(ImageProvider):
|
|
|
136
122
|
additives: bool = True,
|
|
137
123
|
width: int = 768,
|
|
138
124
|
height: int = 768,
|
|
139
|
-
model: str =
|
|
125
|
+
model: str = "flux",
|
|
140
126
|
max_retries: int = 3,
|
|
141
127
|
retry_delay: int = 5,
|
|
142
128
|
negative_prompt: Optional[str] = None,
|
|
@@ -197,17 +183,13 @@ class PollinationsAI(ImageProvider):
|
|
|
197
183
|
|
|
198
184
|
for attempt in range(max_retries):
|
|
199
185
|
try:
|
|
200
|
-
logger.info(f"Generating image {_ + 1}/{amount} with prompt: {current_prompt[:50]}...")
|
|
201
186
|
resp = self.session.get(url, timeout=self.timeout)
|
|
202
187
|
resp.raise_for_status()
|
|
203
188
|
response.append(resp.content)
|
|
204
|
-
logger.success(f"Successfully generated image {_ + 1}/{amount}! 🎨")
|
|
205
189
|
break
|
|
206
190
|
except RequestException as e:
|
|
207
191
|
if attempt == max_retries - 1:
|
|
208
|
-
logger.error(f"Failed to generate image after {max_retries} attempts: {e} 😢")
|
|
209
192
|
raise
|
|
210
|
-
logger.warning(f"Attempt {attempt + 1} failed. Retrying in {retry_delay} seconds... 🔄")
|
|
211
193
|
time.sleep(retry_delay)
|
|
212
194
|
|
|
213
195
|
return response
|
|
@@ -246,7 +228,6 @@ class PollinationsAI(ImageProvider):
|
|
|
246
228
|
save_dir = dir if dir else os.getcwd()
|
|
247
229
|
if not os.path.exists(save_dir):
|
|
248
230
|
os.makedirs(save_dir)
|
|
249
|
-
logger.info(f"Created directory: {save_dir} 📁")
|
|
250
231
|
|
|
251
232
|
saved_paths = []
|
|
252
233
|
timestamp = int(time.time())
|
|
@@ -263,7 +244,6 @@ class PollinationsAI(ImageProvider):
|
|
|
263
244
|
f.write(image_bytes)
|
|
264
245
|
|
|
265
246
|
saved_paths.append(filepath)
|
|
266
|
-
logger.success(f"Saved image to: {filepath} 💾")
|
|
267
247
|
|
|
268
248
|
return saved_paths
|
|
269
249
|
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
from .FreeAIPlayground import *
|
|
2
|
-
from .deepinfra import *
|
|
3
2
|
from .PollinationsAI import *
|
|
4
3
|
from .AiForce import *
|
|
5
|
-
from .blackbox import *
|
|
6
4
|
from .Nexra import *
|
|
7
5
|
from .huggingface import *
|
|
8
6
|
from .artbit import *
|
|
9
|
-
from .imgninza import *
|
|
10
7
|
from .talkai import *
|
|
11
8
|
from .piclumen import *
|
|
9
|
+
from .MagicStudio import *
|
|
10
|
+
from .fastflux import *
|
|
@@ -0,0 +1,482 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AsyncAIArtaImager - Your go-to async provider for generating fire images with AI Arta! ⚡
|
|
3
|
+
|
|
4
|
+
Examples:
|
|
5
|
+
>>> from webscout import AsyncAIArtaImager
|
|
6
|
+
>>> import asyncio
|
|
7
|
+
>>>
|
|
8
|
+
>>> async def example():
|
|
9
|
+
... # Initialize with logging
|
|
10
|
+
... provider = AsyncAIArtaImager(logging=True)
|
|
11
|
+
...
|
|
12
|
+
... # Generate a single image
|
|
13
|
+
... images = await provider.generate("A beautiful sunset over mountains")
|
|
14
|
+
... paths = await provider.save(images)
|
|
15
|
+
...
|
|
16
|
+
... # Generate multiple images with parameters
|
|
17
|
+
... images = await provider.generate(
|
|
18
|
+
... prompt="Epic dragon in cyberpunk city",
|
|
19
|
+
... amount=2,
|
|
20
|
+
... model="fantasy_art",
|
|
21
|
+
... negative_prompt="ugly, deformed",
|
|
22
|
+
... guidance_scale=7,
|
|
23
|
+
... num_inference_steps=30
|
|
24
|
+
... )
|
|
25
|
+
... paths = await provider.save(images, name="dragon", dir="outputs")
|
|
26
|
+
>>>
|
|
27
|
+
>>> # Run the example
|
|
28
|
+
>>> asyncio.run(example())
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
import aiohttp
|
|
32
|
+
import asyncio
|
|
33
|
+
import json
|
|
34
|
+
import os
|
|
35
|
+
import time
|
|
36
|
+
from pathlib import Path
|
|
37
|
+
from typing import List, Optional, Union, Dict, Any, AsyncGenerator
|
|
38
|
+
import aiofiles
|
|
39
|
+
|
|
40
|
+
from webscout.AIbase import AsyncImageProvider
|
|
41
|
+
from webscout.litagent import LitAgent
|
|
42
|
+
|
|
43
|
+
class AsyncAIArtaImager(AsyncImageProvider):
|
|
44
|
+
"""Your go-to async provider for generating fire images with AI Arta! ⚡
|
|
45
|
+
|
|
46
|
+
Examples:
|
|
47
|
+
>>> provider = AsyncAIArtaImager()
|
|
48
|
+
>>> async def example():
|
|
49
|
+
... # Basic usage
|
|
50
|
+
... images = await provider.generate("Cool art")
|
|
51
|
+
... paths = await provider.save(images)
|
|
52
|
+
>>>
|
|
53
|
+
>>> # Advanced usage
|
|
54
|
+
>>> provider = AsyncAIArtaImager(timeout=120)
|
|
55
|
+
>>> async def example():
|
|
56
|
+
... images = await provider.generate(
|
|
57
|
+
... prompt="Epic dragon",
|
|
58
|
+
... amount=2,
|
|
59
|
+
... model="fantasy_art",
|
|
60
|
+
... negative_prompt="ugly, deformed"
|
|
61
|
+
... )
|
|
62
|
+
... paths = await provider.save(images, name="dragon", dir="my_art")
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
# API endpoints
|
|
66
|
+
url = "https://img-gen-prod.ai-arta.com"
|
|
67
|
+
auth_url = "https://www.googleapis.com/identitytoolkit/v3/relyingparty/signupNewUser?key=AIzaSyB3-71wG0fIt0shj0ee4fvx1shcjJHGrrQ"
|
|
68
|
+
token_refresh_url = "https://securetoken.googleapis.com/v1/token?key=AIzaSyB3-71wG0fIt0shj0ee4fvx1shcjJHGrrQ"
|
|
69
|
+
image_generation_url = "https://img-gen-prod.ai-arta.com/api/v1/text2image"
|
|
70
|
+
status_check_url = "https://img-gen-prod.ai-arta.com/api/v1/text2image/{record_id}/status"
|
|
71
|
+
|
|
72
|
+
# Available models
|
|
73
|
+
AVAILABLE_MODELS = {
|
|
74
|
+
"flux": "Flux",
|
|
75
|
+
"medieval": "Medieval",
|
|
76
|
+
"vincent_van_gogh": "Vincent Van Gogh",
|
|
77
|
+
"f_dev": "F Dev",
|
|
78
|
+
"low_poly": "Low Poly",
|
|
79
|
+
"dreamshaper_xl": "Dreamshaper-xl",
|
|
80
|
+
"anima_pencil_xl": "Anima-pencil-xl",
|
|
81
|
+
"biomech": "Biomech",
|
|
82
|
+
"trash_polka": "Trash Polka",
|
|
83
|
+
"no_style": "No Style",
|
|
84
|
+
"cheyenne_xl": "Cheyenne-xl",
|
|
85
|
+
"chicano": "Chicano",
|
|
86
|
+
"embroidery_tattoo": "Embroidery tattoo",
|
|
87
|
+
"red_and_black": "Red and Black",
|
|
88
|
+
"fantasy_art": "Fantasy Art",
|
|
89
|
+
"watercolor": "Watercolor",
|
|
90
|
+
"dotwork": "Dotwork",
|
|
91
|
+
"old_school_colored": "Old school colored",
|
|
92
|
+
"realistic_tattoo": "Realistic tattoo",
|
|
93
|
+
"japanese_2": "Japanese_2",
|
|
94
|
+
"realistic_stock_xl": "Realistic-stock-xl",
|
|
95
|
+
"f_pro": "F Pro",
|
|
96
|
+
"revanimated": "RevAnimated",
|
|
97
|
+
"katayama_mix_xl": "Katayama-mix-xl",
|
|
98
|
+
"sdxl_l": "SDXL L",
|
|
99
|
+
"cor_epica_xl": "Cor-epica-xl",
|
|
100
|
+
"anime_tattoo": "Anime tattoo",
|
|
101
|
+
"new_school": "New School",
|
|
102
|
+
"death_metal": "Death metal",
|
|
103
|
+
"old_school": "Old School",
|
|
104
|
+
"juggernaut_xl": "Juggernaut-xl",
|
|
105
|
+
"photographic": "Photographic",
|
|
106
|
+
"sdxl_1_0": "SDXL 1.0",
|
|
107
|
+
"graffiti": "Graffiti",
|
|
108
|
+
"mini_tattoo": "Mini tattoo",
|
|
109
|
+
"surrealism": "Surrealism",
|
|
110
|
+
"neo_traditional": "Neo-traditional",
|
|
111
|
+
"on_limbs_black": "On limbs black",
|
|
112
|
+
# "yamers_realistic_xl": "Yamers-realistic-xl",
|
|
113
|
+
# "pony_xl": "Pony-xl",
|
|
114
|
+
# "playground_xl": "Playground-xl",
|
|
115
|
+
# "anything_xl": "Anything-xl",
|
|
116
|
+
# "flame_design": "Flame design",
|
|
117
|
+
# "kawaii": "Kawaii",
|
|
118
|
+
# "cinematic_art": "Cinematic Art",
|
|
119
|
+
# "professional": "Professional",
|
|
120
|
+
# "flux_black_ink": "Flux Black Ink"
|
|
121
|
+
}
|
|
122
|
+
models = list(AVAILABLE_MODELS.keys())
|
|
123
|
+
|
|
124
|
+
def __init__(self, timeout: int = 60, proxies: dict = None, logging: bool = True):
|
|
125
|
+
"""Initialize your async AIArtaImager provider with custom settings ⚙️
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
timeout (int): HTTP request timeout in seconds (default: 60)
|
|
129
|
+
proxies (dict, optional): Proxy configuration for requests
|
|
130
|
+
logging (bool): Enable/disable logging (default: True)
|
|
131
|
+
"""
|
|
132
|
+
self.headers = {
|
|
133
|
+
"Accept": "application/json",
|
|
134
|
+
"Accept-Language": "en-US,en;q=0.5",
|
|
135
|
+
"User-Agent": LitAgent().random()
|
|
136
|
+
}
|
|
137
|
+
self.timeout = timeout
|
|
138
|
+
self.proxies = proxies
|
|
139
|
+
self.prompt: str = "AI-generated image - webscout"
|
|
140
|
+
self.image_extension: str = "png"
|
|
141
|
+
self.logging = logging
|
|
142
|
+
|
|
143
|
+
if self.logging and False: # Disabled logger
|
|
144
|
+
print("AsyncAIArtaImager initialized! Ready to create some fire art! 🚀")
|
|
145
|
+
|
|
146
|
+
async def get_auth_file(self) -> Path:
|
|
147
|
+
"""Get path to authentication file"""
|
|
148
|
+
path = Path(os.path.join(os.path.expanduser("~"), ".ai_arta_cookies"))
|
|
149
|
+
path.mkdir(exist_ok=True)
|
|
150
|
+
filename = f"auth_{self.__class__.__name__}.json"
|
|
151
|
+
return path / filename
|
|
152
|
+
|
|
153
|
+
async def create_token(self, path: Path) -> Dict[str, Any]:
|
|
154
|
+
"""Create a new authentication token"""
|
|
155
|
+
# Step 1: Generate Authentication Token
|
|
156
|
+
auth_payload = {"clientType": "CLIENT_TYPE_ANDROID"}
|
|
157
|
+
|
|
158
|
+
async with aiohttp.ClientSession(headers=self.headers) as session:
|
|
159
|
+
async with session.post(
|
|
160
|
+
self.auth_url,
|
|
161
|
+
json=auth_payload,
|
|
162
|
+
timeout=self.timeout,
|
|
163
|
+
proxy=self.proxies.get('http') if self.proxies else None
|
|
164
|
+
) as response:
|
|
165
|
+
response.raise_for_status()
|
|
166
|
+
auth_data = await response.json()
|
|
167
|
+
|
|
168
|
+
auth_token = auth_data.get("idToken")
|
|
169
|
+
|
|
170
|
+
if not auth_token:
|
|
171
|
+
if self.logging:
|
|
172
|
+
print("Failed to obtain authentication token 😢")
|
|
173
|
+
raise Exception("Failed to obtain authentication token.")
|
|
174
|
+
|
|
175
|
+
async with aiofiles.open(path, 'w') as f:
|
|
176
|
+
await f.write(json.dumps(auth_data))
|
|
177
|
+
|
|
178
|
+
return auth_data
|
|
179
|
+
|
|
180
|
+
async def refresh_token(self, refresh_token: str) -> tuple[str, str]:
|
|
181
|
+
"""Refresh authentication token"""
|
|
182
|
+
payload = {
|
|
183
|
+
"grant_type": "refresh_token",
|
|
184
|
+
"refresh_token": refresh_token,
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
async with aiohttp.ClientSession(headers=self.headers) as session:
|
|
188
|
+
async with session.post(
|
|
189
|
+
self.token_refresh_url,
|
|
190
|
+
data=payload,
|
|
191
|
+
timeout=self.timeout,
|
|
192
|
+
proxy=self.proxies.get('http') if self.proxies else None
|
|
193
|
+
) as response:
|
|
194
|
+
response.raise_for_status()
|
|
195
|
+
response_data = await response.json()
|
|
196
|
+
|
|
197
|
+
return response_data.get("id_token"), response_data.get("refresh_token")
|
|
198
|
+
|
|
199
|
+
async def read_and_refresh_token(self) -> Dict[str, Any]:
|
|
200
|
+
"""Read token from file and refresh if needed"""
|
|
201
|
+
path = await self.get_auth_file()
|
|
202
|
+
|
|
203
|
+
if path.is_file():
|
|
204
|
+
async with aiofiles.open(path, 'r') as f:
|
|
205
|
+
auth_data = json.loads(await f.read())
|
|
206
|
+
|
|
207
|
+
diff = time.time() - os.path.getmtime(path)
|
|
208
|
+
expires_in = int(auth_data.get("expiresIn"))
|
|
209
|
+
|
|
210
|
+
if diff < expires_in:
|
|
211
|
+
if diff > expires_in / 2:
|
|
212
|
+
# Refresh token if it's older than half its lifetime
|
|
213
|
+
if self.logging:
|
|
214
|
+
print("Refreshing authentication token... 🔄")
|
|
215
|
+
auth_data["idToken"], auth_data["refreshToken"] = await self.refresh_token(
|
|
216
|
+
auth_data.get("refreshToken")
|
|
217
|
+
)
|
|
218
|
+
async with aiofiles.open(path, 'w') as f:
|
|
219
|
+
await f.write(json.dumps(auth_data))
|
|
220
|
+
return auth_data
|
|
221
|
+
|
|
222
|
+
# Create new token if file doesn't exist or token expired
|
|
223
|
+
if self.logging:
|
|
224
|
+
print("Creating new authentication token... 🔑")
|
|
225
|
+
return await self.create_token(path)
|
|
226
|
+
|
|
227
|
+
def get_model(self, model_name: str) -> str:
|
|
228
|
+
"""Get actual model name from alias"""
|
|
229
|
+
if model_name.lower() in self.AVAILABLE_MODELS:
|
|
230
|
+
return self.AVAILABLE_MODELS[model_name.lower()]
|
|
231
|
+
return model_name
|
|
232
|
+
|
|
233
|
+
async def generate(
|
|
234
|
+
self,
|
|
235
|
+
prompt: str,
|
|
236
|
+
amount: int = 1,
|
|
237
|
+
model: str = "Flux",
|
|
238
|
+
negative_prompt: str = "blurry, deformed hands, ugly",
|
|
239
|
+
guidance_scale: int = 7,
|
|
240
|
+
num_inference_steps: int = 30,
|
|
241
|
+
aspect_ratio: str = "1:1",
|
|
242
|
+
max_retries: int = 3,
|
|
243
|
+
retry_delay: int = 5,
|
|
244
|
+
**kwargs
|
|
245
|
+
) -> List[bytes]:
|
|
246
|
+
"""Generate some fire images from your prompt asynchronously! ⚡
|
|
247
|
+
|
|
248
|
+
Examples:
|
|
249
|
+
>>> provider = AsyncAIArtaImager()
|
|
250
|
+
>>> async def example():
|
|
251
|
+
... # Basic usage
|
|
252
|
+
... images = await provider.generate("Cool art")
|
|
253
|
+
... # Advanced usage
|
|
254
|
+
... images = await provider.generate(
|
|
255
|
+
... prompt="Epic dragon",
|
|
256
|
+
... amount=2,
|
|
257
|
+
... model="fantasy_art",
|
|
258
|
+
... negative_prompt="ugly, deformed"
|
|
259
|
+
... )
|
|
260
|
+
|
|
261
|
+
Args:
|
|
262
|
+
prompt (str): Your image description
|
|
263
|
+
amount (int): How many images you want (default: 1)
|
|
264
|
+
model (str): Model to use - check AVAILABLE_MODELS (default: "flux")
|
|
265
|
+
negative_prompt (str): What you don't want in the image
|
|
266
|
+
guidance_scale (int): Controls how closely the model follows your prompt
|
|
267
|
+
num_inference_steps (int): More steps = better quality but slower
|
|
268
|
+
aspect_ratio (str): Image aspect ratio (default: "1:1")
|
|
269
|
+
max_retries (int): Max retry attempts if something fails
|
|
270
|
+
retry_delay (int): Seconds to wait between retries
|
|
271
|
+
**kwargs: Additional parameters for future compatibility
|
|
272
|
+
|
|
273
|
+
Returns:
|
|
274
|
+
List[bytes]: Your generated images
|
|
275
|
+
|
|
276
|
+
Raises:
|
|
277
|
+
ValueError: If the inputs ain't valid
|
|
278
|
+
Exception: If the API calls fail after retries
|
|
279
|
+
"""
|
|
280
|
+
if not prompt:
|
|
281
|
+
raise ValueError("Yo fam, the prompt can't be empty! 🤔")
|
|
282
|
+
if not isinstance(amount, int) or amount < 1:
|
|
283
|
+
raise ValueError("Amount needs to be a positive number! 📈")
|
|
284
|
+
|
|
285
|
+
model_name = self.get_model(model)
|
|
286
|
+
self.prompt = prompt
|
|
287
|
+
response = []
|
|
288
|
+
|
|
289
|
+
if self.logging:
|
|
290
|
+
print(f"Generating {amount} images with {model_name}... 🎨")
|
|
291
|
+
|
|
292
|
+
# Step 1: Get Authentication Token
|
|
293
|
+
auth_data = await self.read_and_refresh_token()
|
|
294
|
+
|
|
295
|
+
# Headers for generation requests
|
|
296
|
+
gen_headers = {
|
|
297
|
+
"Authorization": auth_data.get("idToken"),
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
async with aiohttp.ClientSession(headers={**self.headers, **gen_headers}) as session:
|
|
301
|
+
for i in range(amount):
|
|
302
|
+
# Step 2: Generate Image
|
|
303
|
+
image_payload = {
|
|
304
|
+
"prompt": prompt,
|
|
305
|
+
"negative_prompt": negative_prompt,
|
|
306
|
+
"style": model_name,
|
|
307
|
+
"images_num": "1", # Generate 1 at a time
|
|
308
|
+
"cfg_scale": str(guidance_scale),
|
|
309
|
+
"steps": str(num_inference_steps),
|
|
310
|
+
"aspect_ratio": aspect_ratio,
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
for attempt in range(max_retries):
|
|
314
|
+
try:
|
|
315
|
+
if self.logging:
|
|
316
|
+
print(f"Starting generation for image {i+1}/{amount}... ⏳")
|
|
317
|
+
|
|
318
|
+
# Submit generation request
|
|
319
|
+
async with session.post(
|
|
320
|
+
self.image_generation_url,
|
|
321
|
+
data=image_payload,
|
|
322
|
+
timeout=self.timeout,
|
|
323
|
+
proxy=self.proxies.get('http') if self.proxies else None
|
|
324
|
+
) as image_response:
|
|
325
|
+
image_response.raise_for_status()
|
|
326
|
+
image_data = await image_response.json()
|
|
327
|
+
|
|
328
|
+
record_id = image_data.get("record_id")
|
|
329
|
+
|
|
330
|
+
if not record_id:
|
|
331
|
+
if self.logging:
|
|
332
|
+
print("Failed to initiate image generation 😢")
|
|
333
|
+
raise Exception(f"Failed to initiate image generation: {image_data}")
|
|
334
|
+
|
|
335
|
+
# Step 3: Check Generation Status
|
|
336
|
+
status_url = self.status_check_url.format(record_id=record_id)
|
|
337
|
+
|
|
338
|
+
counter = 0
|
|
339
|
+
dots = [".", "..", "...", "...."]
|
|
340
|
+
|
|
341
|
+
while True:
|
|
342
|
+
async with session.get(
|
|
343
|
+
status_url,
|
|
344
|
+
timeout=self.timeout,
|
|
345
|
+
proxy=self.proxies.get('http') if self.proxies else None
|
|
346
|
+
) as status_response:
|
|
347
|
+
status_data = await status_response.json()
|
|
348
|
+
|
|
349
|
+
status = status_data.get("status")
|
|
350
|
+
|
|
351
|
+
if status == "DONE":
|
|
352
|
+
image_urls = [image["url"] for image in status_data.get("response", [])]
|
|
353
|
+
|
|
354
|
+
if not image_urls:
|
|
355
|
+
if self.logging:
|
|
356
|
+
print("No image URLs in response 😢")
|
|
357
|
+
raise Exception("No image URLs in response")
|
|
358
|
+
|
|
359
|
+
# Download the generated image
|
|
360
|
+
async with session.get(image_urls[0]) as img_response:
|
|
361
|
+
img_response.raise_for_status()
|
|
362
|
+
response.append(await img_response.read())
|
|
363
|
+
|
|
364
|
+
if self.logging:
|
|
365
|
+
print(f"Successfully generated image {i+1}/{amount}! 🎨")
|
|
366
|
+
break
|
|
367
|
+
|
|
368
|
+
elif status in ("IN_QUEUE", "IN_PROGRESS"):
|
|
369
|
+
status_text = "Waiting" if status == "IN_QUEUE" else "Generating"
|
|
370
|
+
if self.logging:
|
|
371
|
+
print(f"{status_text}{dots[counter % 4]}")
|
|
372
|
+
await asyncio.sleep(5) # Poll every 5 seconds
|
|
373
|
+
counter += 1
|
|
374
|
+
|
|
375
|
+
else:
|
|
376
|
+
if self.logging:
|
|
377
|
+
print(f"Generation failed with status: {status} 😢")
|
|
378
|
+
raise Exception(f"Image generation failed with status: {status}")
|
|
379
|
+
|
|
380
|
+
# If we got here, we successfully generated an image
|
|
381
|
+
break
|
|
382
|
+
|
|
383
|
+
except Exception as e:
|
|
384
|
+
if attempt == max_retries - 1:
|
|
385
|
+
if self.logging:
|
|
386
|
+
print(f"Failed after {max_retries} attempts: {e} 😢")
|
|
387
|
+
raise
|
|
388
|
+
else:
|
|
389
|
+
if self.logging:
|
|
390
|
+
print(f"Attempt {attempt + 1} failed. Retrying in {retry_delay} seconds... 🔄")
|
|
391
|
+
await asyncio.sleep(retry_delay)
|
|
392
|
+
|
|
393
|
+
if self.logging:
|
|
394
|
+
print(f"Successfully generated {len(response)} images! 🎉")
|
|
395
|
+
return response
|
|
396
|
+
|
|
397
|
+
async def save(
|
|
398
|
+
self,
|
|
399
|
+
response: Union[List[bytes], AsyncGenerator[bytes, None]],
|
|
400
|
+
name: Optional[str] = None,
|
|
401
|
+
dir: Optional[Union[str, Path]] = None,
|
|
402
|
+
filenames_prefix: str = "",
|
|
403
|
+
) -> List[str]:
|
|
404
|
+
"""Save your fire generated images asynchronously! 💾
|
|
405
|
+
|
|
406
|
+
Examples:
|
|
407
|
+
>>> provider = AsyncAIArtaImager()
|
|
408
|
+
>>> async def example():
|
|
409
|
+
... images = await provider.generate("Cool art")
|
|
410
|
+
... # Save with default settings
|
|
411
|
+
... paths = await provider.save(images)
|
|
412
|
+
... # Save with custom name and directory
|
|
413
|
+
... paths = await provider.save(
|
|
414
|
+
... images,
|
|
415
|
+
... name="my_art",
|
|
416
|
+
... dir="my_images",
|
|
417
|
+
... filenames_prefix="test_"
|
|
418
|
+
... )
|
|
419
|
+
|
|
420
|
+
Args:
|
|
421
|
+
response (Union[List[bytes], AsyncGenerator[bytes, None]]): Your generated images
|
|
422
|
+
name (Optional[str]): Custom name for your images
|
|
423
|
+
dir (Optional[Union[str, Path]]): Where to save the images (default: current directory)
|
|
424
|
+
filenames_prefix (str): Prefix for your image files
|
|
425
|
+
|
|
426
|
+
Returns:
|
|
427
|
+
List[str]: Paths to your saved images
|
|
428
|
+
"""
|
|
429
|
+
save_dir = dir if dir else os.getcwd()
|
|
430
|
+
if not os.path.exists(save_dir):
|
|
431
|
+
os.makedirs(save_dir)
|
|
432
|
+
if self.logging:
|
|
433
|
+
print(f"Created directory: {save_dir} 📁")
|
|
434
|
+
|
|
435
|
+
name = self.prompt if name is None else name
|
|
436
|
+
|
|
437
|
+
# Clean up name for filename use
|
|
438
|
+
safe_name = "".join(c if c.isalnum() or c in "_-" else "_" for c in name)
|
|
439
|
+
safe_name = safe_name[:50] # Truncate if too long
|
|
440
|
+
|
|
441
|
+
# Handle both List[bytes] and AsyncGenerator
|
|
442
|
+
if isinstance(response, list):
|
|
443
|
+
image_list = response
|
|
444
|
+
else:
|
|
445
|
+
image_list = [chunk async for chunk in response]
|
|
446
|
+
|
|
447
|
+
if self.logging:
|
|
448
|
+
print(f"Saving {len(image_list)} images... 💾")
|
|
449
|
+
|
|
450
|
+
saved_paths = []
|
|
451
|
+
|
|
452
|
+
async def save_single_image(image_bytes: bytes, index: int) -> str:
|
|
453
|
+
filename = f"{filenames_prefix}{safe_name}_{index}.{self.image_extension}"
|
|
454
|
+
filepath = os.path.join(save_dir, filename)
|
|
455
|
+
|
|
456
|
+
async with aiofiles.open(filepath, "wb") as f:
|
|
457
|
+
await f.write(image_bytes)
|
|
458
|
+
|
|
459
|
+
if self.logging:
|
|
460
|
+
print(f"Saved image to: {filepath} 💾")
|
|
461
|
+
return filename
|
|
462
|
+
|
|
463
|
+
tasks = [save_single_image(img, i) for i, img in enumerate(image_list)]
|
|
464
|
+
saved_paths = await asyncio.gather(*tasks)
|
|
465
|
+
|
|
466
|
+
if self.logging:
|
|
467
|
+
print(f"Images saved successfully! Check {dir} 🎉")
|
|
468
|
+
return saved_paths
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
if __name__ == "__main__":
|
|
472
|
+
# Example usage
|
|
473
|
+
async def main():
|
|
474
|
+
provider = AsyncAIArtaImager()
|
|
475
|
+
try:
|
|
476
|
+
images = await provider.generate("A beautiful sunset over mountains", amount=1)
|
|
477
|
+
paths = await provider.save(images, dir="generated_images")
|
|
478
|
+
print(f"Images saved to: {paths}")
|
|
479
|
+
except Exception as e:
|
|
480
|
+
print(f"An error occurred: {e}")
|
|
481
|
+
|
|
482
|
+
asyncio.run(main())
|