Spaces:
Running
Running
VikumKarunathilake
commited on
Commit
•
c02f818
1
Parent(s):
1a5c0cc
1st commit
Browse files- .gitignore +5 -0
- Dockerfile +17 -0
- app.py +344 -0
- genarationData.py +119 -0
- requirements.txt +7 -0
- style.css +69 -0
.gitignore
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.env
|
2 |
+
images.db
|
3 |
+
.vercel
|
4 |
+
.gradio
|
5 |
+
vercel.json
|
Dockerfile
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Use official Python runtime as a parent image
|
2 |
+
FROM python:3.10-slim
|
3 |
+
|
4 |
+
# Set the working directory
|
5 |
+
WORKDIR /app
|
6 |
+
|
7 |
+
# Copy the current directory contents into the container
|
8 |
+
COPY . /app
|
9 |
+
|
10 |
+
# Install any needed packages specified in requirements.txt
|
11 |
+
RUN pip install --no-cache-dir -r requirements.txt
|
12 |
+
|
13 |
+
# Expose port 7860 for Gradio and Flask
|
14 |
+
EXPOSE 7860
|
15 |
+
|
16 |
+
# Run the app
|
17 |
+
CMD ["python", "app.py"]
|
app.py
ADDED
@@ -0,0 +1,344 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
from together import Together
|
3 |
+
import base64
|
4 |
+
from PIL import Image
|
5 |
+
import io
|
6 |
+
import logging
|
7 |
+
import requests
|
8 |
+
from datetime import datetime
|
9 |
+
from dotenv import load_dotenv
|
10 |
+
import os
|
11 |
+
import json
|
12 |
+
import psycopg2
|
13 |
+
from psycopg2 import Error
|
14 |
+
from urllib.parse import urlparse
|
15 |
+
from functools import lru_cache
|
16 |
+
import time
|
17 |
+
from typing import Tuple, Any, Optional
|
18 |
+
import genarationData
|
19 |
+
|
20 |
+
# Load environment variables
|
21 |
+
load_dotenv()
|
22 |
+
|
23 |
+
# Logging configuration
|
24 |
+
logging.basicConfig(
|
25 |
+
level=logging.INFO,
|
26 |
+
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
27 |
+
)
|
28 |
+
logger = logging.getLogger(__name__)
|
29 |
+
|
30 |
+
# Initialize Together client
|
31 |
+
api_key = os.getenv("TOGETHER_API_KEY")
|
32 |
+
client = Together(api_key=api_key)
|
33 |
+
|
34 |
+
# Configuration constants
|
35 |
+
IMGBB_API_KEY = os.getenv("IMGBB_API_KEY")
|
36 |
+
POSTGRES_URL = os.getenv("POSTGRES_URL")
|
37 |
+
DEBUG = os.getenv("DEBUG", "False").lower() == "true"
|
38 |
+
|
39 |
+
CACHE_TIMEOUT = 3600
|
40 |
+
MAX_RETRIES = 3
|
41 |
+
RETRY_DELAY = 1
|
42 |
+
|
43 |
+
@lru_cache(maxsize=1)
|
44 |
+
def get_db_config() -> dict:
|
45 |
+
"""Parse PostgreSQL URL and return connection configuration."""
|
46 |
+
parsed = urlparse(POSTGRES_URL)
|
47 |
+
return {
|
48 |
+
'host': parsed.hostname,
|
49 |
+
'user': parsed.username,
|
50 |
+
'password': parsed.password,
|
51 |
+
'database': parsed.path.strip('/'),
|
52 |
+
'port': parsed.port,
|
53 |
+
}
|
54 |
+
|
55 |
+
def get_db_connection() -> psycopg2.extensions.connection:
|
56 |
+
"""Establish a connection to the PostgreSQL database with retry logic."""
|
57 |
+
for attempt in range(MAX_RETRIES):
|
58 |
+
try:
|
59 |
+
config = get_db_config()
|
60 |
+
connection = psycopg2.connect(**config)
|
61 |
+
if connection:
|
62 |
+
logger.info('Successfully connected to PostgreSQL database')
|
63 |
+
return connection
|
64 |
+
except Error as e:
|
65 |
+
if attempt == MAX_RETRIES - 1:
|
66 |
+
logger.error(f"Final attempt failed to connect to PostgreSQL: {e}")
|
67 |
+
raise
|
68 |
+
time.sleep(RETRY_DELAY)
|
69 |
+
raise Exception("Failed to connect to database after retries")
|
70 |
+
|
71 |
+
def init_db():
|
72 |
+
"""Initialize the database schema if it doesn't exist."""
|
73 |
+
try:
|
74 |
+
with get_db_connection() as connection:
|
75 |
+
with connection.cursor() as cursor:
|
76 |
+
cursor.execute('''
|
77 |
+
CREATE TABLE IF NOT EXISTS generated_images (
|
78 |
+
id SERIAL PRIMARY KEY,
|
79 |
+
generation_prompt TEXT NOT NULL,
|
80 |
+
generation_timestamp TIMESTAMP NOT NULL,
|
81 |
+
generation_width INT NOT NULL,
|
82 |
+
generation_height INT NOT NULL,
|
83 |
+
generation_steps INT NOT NULL,
|
84 |
+
imgbb_id VARCHAR(255) NOT NULL,
|
85 |
+
imgbb_title VARCHAR(255),
|
86 |
+
imgbb_url_viewer TEXT,
|
87 |
+
imgbb_url TEXT,
|
88 |
+
imgbb_display_url TEXT,
|
89 |
+
imgbb_width VARCHAR(50),
|
90 |
+
imgbb_height VARCHAR(50),
|
91 |
+
imgbb_size VARCHAR(50),
|
92 |
+
imgbb_time VARCHAR(50),
|
93 |
+
imgbb_expiration VARCHAR(50),
|
94 |
+
delete_url TEXT,
|
95 |
+
raw_response TEXT,
|
96 |
+
user_id VARCHAR(255)
|
97 |
+
);
|
98 |
+
''')
|
99 |
+
cursor.execute('CREATE INDEX IF NOT EXISTS idx_timestamp ON generated_images (generation_timestamp);')
|
100 |
+
cursor.execute('CREATE INDEX IF NOT EXISTS idx_imgbb_id ON generated_images (imgbb_id);')
|
101 |
+
connection.commit()
|
102 |
+
logger.info("Database initialized successfully")
|
103 |
+
except Error as e:
|
104 |
+
logger.error(f"Error initializing database: {e}")
|
105 |
+
raise
|
106 |
+
|
107 |
+
def retry_with_backoff(func):
|
108 |
+
"""Decorator for functions to retry with exponential backoff."""
|
109 |
+
def wrapper(*args, **kwargs):
|
110 |
+
for i in range(MAX_RETRIES):
|
111 |
+
try:
|
112 |
+
return func(*args, **kwargs)
|
113 |
+
except Exception as e:
|
114 |
+
if i == MAX_RETRIES - 1:
|
115 |
+
raise
|
116 |
+
wait = (2 ** i) * RETRY_DELAY
|
117 |
+
logger.warning(f"Attempt {i+1} failed, retrying in {wait} seconds...")
|
118 |
+
time.sleep(wait)
|
119 |
+
return wrapper
|
120 |
+
|
121 |
+
@retry_with_backoff
|
122 |
+
def upload_to_imgbb(image_bytes: bytes) -> dict:
|
123 |
+
"""Upload image to ImgBB and return the response."""
|
124 |
+
img_base64 = base64.b64encode(image_bytes).decode('utf-8')
|
125 |
+
url = "https://api.imgbb.com/1/upload"
|
126 |
+
payload = {
|
127 |
+
"key": IMGBB_API_KEY,
|
128 |
+
"image": img_base64
|
129 |
+
}
|
130 |
+
response = requests.post(url, payload, timeout=30)
|
131 |
+
response.raise_for_status()
|
132 |
+
logger.info("Successfully uploaded to ImgBB")
|
133 |
+
return response.json()
|
134 |
+
|
135 |
+
@retry_with_backoff
|
136 |
+
def generate_image(prompt: str, width: int, height: int, steps: int) -> Tuple[Image.Image, bytes]:
|
137 |
+
"""Generate an image using the Together API."""
|
138 |
+
if not prompt.strip():
|
139 |
+
raise ValueError("Please enter a prompt")
|
140 |
+
|
141 |
+
logger.info(f"Generating image with parameters: width={width}, height={height}, steps={steps}")
|
142 |
+
response = client.images.generate(
|
143 |
+
prompt=prompt,
|
144 |
+
model="black-forest-labs/FLUX.1-schnell-Free",
|
145 |
+
width=width,
|
146 |
+
height=height,
|
147 |
+
steps=steps,
|
148 |
+
n=1,
|
149 |
+
response_format="b64_json"
|
150 |
+
)
|
151 |
+
|
152 |
+
image_bytes = base64.b64decode(response.data[0].b64_json)
|
153 |
+
image = Image.open(io.BytesIO(image_bytes))
|
154 |
+
|
155 |
+
logger.info("Image generated successfully")
|
156 |
+
return image, image_bytes
|
157 |
+
|
158 |
+
def handle_generation(prompt: str, width: int, height: int, steps: int) -> Tuple[Optional[Image.Image], str]:
|
159 |
+
"""Handle the entire process of image generation, upload, and database save."""
|
160 |
+
try:
|
161 |
+
image, image_bytes = generate_image(prompt, width, height, steps)
|
162 |
+
imgbb_response = upload_to_imgbb(image_bytes)
|
163 |
+
if save_to_database(prompt, width, height, steps, imgbb_response):
|
164 |
+
return image, "Image generated successfully!"
|
165 |
+
else:
|
166 |
+
return image, "Image generated and uploaded, but database save failed!"
|
167 |
+
except Exception as e:
|
168 |
+
logger.error(f"Error in handle_generation: {str(e)}")
|
169 |
+
return None, f"Error: {str(e)}"
|
170 |
+
|
171 |
+
def save_to_database(prompt: str, width: int, height: int, steps: int, imgbb_response: dict) -> bool:
|
172 |
+
"""Save image generation details to the database."""
|
173 |
+
try:
|
174 |
+
with get_db_connection() as connection:
|
175 |
+
with connection.cursor() as cursor:
|
176 |
+
data = imgbb_response['data']
|
177 |
+
insert_query = '''
|
178 |
+
INSERT INTO generated_images (
|
179 |
+
generation_prompt, generation_timestamp, generation_width, generation_height,
|
180 |
+
generation_steps, imgbb_id, imgbb_title, imgbb_url_viewer, imgbb_url,
|
181 |
+
imgbb_display_url, imgbb_width, imgbb_height, imgbb_size, imgbb_time,
|
182 |
+
imgbb_expiration, delete_url, raw_response, user_id
|
183 |
+
) VALUES (
|
184 |
+
%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s
|
185 |
+
)
|
186 |
+
'''
|
187 |
+
values = (
|
188 |
+
prompt, datetime.now(), width, height, steps,
|
189 |
+
data.get('id'), data.get('title'), data.get('url_viewer'),
|
190 |
+
data.get('url'), data.get('display_url'), data.get('width'),
|
191 |
+
data.get('height'), data.get('size'), data.get('time'),
|
192 |
+
data.get('expiration'), data.get('delete_url'),
|
193 |
+
json.dumps(imgbb_response), None # Assuming user_id is optional
|
194 |
+
)
|
195 |
+
cursor.execute(insert_query, values)
|
196 |
+
connection.commit()
|
197 |
+
logger.info("Successfully saved to database")
|
198 |
+
return True
|
199 |
+
except Error as e:
|
200 |
+
logger.error(f"Database error: {e}")
|
201 |
+
return False
|
202 |
+
|
203 |
+
def create_demo():
|
204 |
+
"""Create and return the Gradio demo interface."""
|
205 |
+
with gr.Blocks(css="style.css", theme="NoCrypt/miku", title="Elixir Craft Image Generator") as demo:
|
206 |
+
gr.HTML("""
|
207 |
+
<head>
|
208 |
+
<meta charset="UTF-8">
|
209 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
210 |
+
<title>Elixir Craft Image Generator</title>
|
211 |
+
<meta name="description" content="Generate stunning AI art from text descriptions with Elixir Craft's FLUX.1 [schnell] model. Unleash your creativity and bring your visions to life.">
|
212 |
+
<meta name="keywords" content="AI art generator, text-to-image, AI image generation, FLUX.1, Elixir Craft, digital art, AI art, creative tools, image synthesis, artificial intelligence">
|
213 |
+
<meta name="author" content="Vikum Karunathilake">
|
214 |
+
|
215 |
+
<!-- Open Graph / Facebook -->
|
216 |
+
<meta property="og:type" content="website">
|
217 |
+
<meta property="og:url" content="https://generator.elixircraft.net/">
|
218 |
+
<meta property="og:title" content="Elixir Craft Image Generator">
|
219 |
+
<meta property="og:description" content="Generate stunning AI art from text descriptions with Elixir Craft's FLUX.1 [schnell] model. Unleash your creativity and bring your visions to life.">
|
220 |
+
|
221 |
+
|
222 |
+
<!-- Twitter -->
|
223 |
+
<meta name="twitter:card" content="summary"> <!-- Changed to summary for smaller image -->
|
224 |
+
<meta name="twitter:creator" content="@VikumKarunathilake">
|
225 |
+
<meta name="twitter:title" content="Elixir Craft Image Generator">
|
226 |
+
<meta name="twitter:description" content="Generate stunning AI art from text descriptions with Elixir Craft's FLUX.1 [schnell] model. Unleash your creativity and bring your visions to life.">
|
227 |
+
|
228 |
+
</head>
|
229 |
+
<div class="text-center p-5" role="main">
|
230 |
+
<h1 class="text-3xl sm:text-4xl font-semibold text-gray-800">
|
231 |
+
Elixir Craft Image Generator 🖼️
|
232 |
+
</h1>
|
233 |
+
<p class="text-base sm:text-lg text-gray-600 max-w-3xl mx-auto mt-2">
|
234 |
+
Welcome to the <strong>AI Image Generator</strong> powered by the <strong>FLUX.1 [schnell]</strong> model! 🎨
|
235 |
+
</p>
|
236 |
+
<p class="text-sm sm:text-base text-gray-600 max-w-3xl mx-auto mt-4">
|
237 |
+
Enter a description of any scene, character, or object you'd like to see come to life, adjust image dimensions,
|
238 |
+
and select the number of steps to control image detail. Click <strong>"Generate Image"</strong> to create your
|
239 |
+
custom artwork in seconds!
|
240 |
+
</p>
|
241 |
+
<div class="text-left max-w-3xl mx-auto mt-6 text-gray-800">
|
242 |
+
<h2 class="text-lg sm:text-xl font-semibold">Features:</h2>
|
243 |
+
<ul class="list-disc list-inside text-gray-600 mt-2 space-y-1" role="list">
|
244 |
+
<li>Generate high-quality images from text descriptions</li>
|
245 |
+
<li>Optimized for quick and reliable outputs</li>
|
246 |
+
</ul>
|
247 |
+
</div>
|
248 |
+
</div>
|
249 |
+
""")
|
250 |
+
|
251 |
+
with gr.Row():
|
252 |
+
with gr.Column():
|
253 |
+
prompt_input = gr.Textbox(
|
254 |
+
label="Image Description",
|
255 |
+
placeholder="Enter your image description here...",
|
256 |
+
lines=3,
|
257 |
+
elem_id="prompt-input",
|
258 |
+
elem_classes="accessible-input",
|
259 |
+
)
|
260 |
+
|
261 |
+
with gr.Row():
|
262 |
+
width_input = gr.Slider(
|
263 |
+
minimum=256,
|
264 |
+
maximum=1440,
|
265 |
+
value=832,
|
266 |
+
step=16,
|
267 |
+
label="Image Width",
|
268 |
+
)
|
269 |
+
height_input = gr.Slider(
|
270 |
+
minimum=256,
|
271 |
+
maximum=1440,
|
272 |
+
value=1216,
|
273 |
+
step=16,
|
274 |
+
label="Image Height",
|
275 |
+
)
|
276 |
+
|
277 |
+
steps_input = gr.Slider(
|
278 |
+
minimum=1,
|
279 |
+
maximum=4,
|
280 |
+
value=4,
|
281 |
+
step=1,
|
282 |
+
label="Generation Steps",
|
283 |
+
)
|
284 |
+
|
285 |
+
generate_btn = gr.Button(
|
286 |
+
"Generate Image",
|
287 |
+
variant="primary",
|
288 |
+
elem_id="generate-btn",
|
289 |
+
elem_classes="accessible-button"
|
290 |
+
)
|
291 |
+
|
292 |
+
with gr.Column():
|
293 |
+
image_output = gr.Image(
|
294 |
+
label="Generated Image",
|
295 |
+
elem_id="generated-image",
|
296 |
+
elem_classes="accessible-image",
|
297 |
+
show_label=True
|
298 |
+
)
|
299 |
+
status_output = gr.Textbox(
|
300 |
+
label="Generation Status",
|
301 |
+
interactive=False,
|
302 |
+
elem_id="status-output",
|
303 |
+
elem_classes="accessible-status"
|
304 |
+
)
|
305 |
+
|
306 |
+
# Event binding for image generation
|
307 |
+
generate_btn.click(
|
308 |
+
fn=handle_generation,
|
309 |
+
inputs=[prompt_input, width_input, height_input, steps_input],
|
310 |
+
outputs=[image_output, status_output]
|
311 |
+
)
|
312 |
+
|
313 |
+
gr.HTML("""
|
314 |
+
<div class="text-center p-6" role="complementary">
|
315 |
+
<h2 class="text-2xl sm:text-3xl font-semibold text-gray-800">Explore the FLUX.1 Gallery</h2>
|
316 |
+
<p class="text-sm sm:text-base text-gray-600 max-w-2xl mx-auto mt-2">
|
317 |
+
Discover all images generated with the FLUX.1 AI Image Generator. Each creation is stored in the gallery for
|
318 |
+
you to view, share, or download. Every image includes the prompt details and settings.
|
319 |
+
</p>
|
320 |
+
<a href="https://gallery.elixircraft.net"
|
321 |
+
target="_blank"
|
322 |
+
rel="noopener noreferrer"
|
323 |
+
class="inline-block mt-4 px-4 py-2 text-base sm:text-lg font-medium text-white bg-blue-500 rounded hover:bg-blue-600 transition"
|
324 |
+
role="button"
|
325 |
+
aria-label="Visit the FLUX.1 Gallery">
|
326 |
+
Visit the Gallery
|
327 |
+
</a>
|
328 |
+
</div>
|
329 |
+
<footer role="contentinfo" class="text-center p-4 mt-8 text-sm text-gray-600">
|
330 |
+
<hr class="my-4">
|
331 |
+
<p>© 2024 FLUX.1[schnell] AI Image Generator. All rights reserved.</p>
|
332 |
+
<p>Contact: <a href="https://discord.com/users/781158548364853270" target="_blank" rel="noopener noreferrer" aria-label="Contact us on Discord">Discord (Vikum_K)</a></p>
|
333 |
+
<p>Powered by <a href="https://api.together.xyz/" target="_blank" rel="noopener noreferrer" aria-label="Visit Together.ai">Together.ai</a></p>
|
334 |
+
</footer>
|
335 |
+
""")
|
336 |
+
return demo
|
337 |
+
|
338 |
+
if __name__ == "__main__":
|
339 |
+
init_db() # Initialize the database on program start
|
340 |
+
demo = create_demo()
|
341 |
+
demo.launch(
|
342 |
+
server_name="0.0.0.0",
|
343 |
+
server_port=7860,
|
344 |
+
share=True)
|
genarationData.py
ADDED
@@ -0,0 +1,119 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# config.py
|
2 |
+
aspect_ratios = [
|
3 |
+
"1152 x 896",
|
4 |
+
"896 x 1152",
|
5 |
+
"1216 x 832",
|
6 |
+
"832 x 1216",
|
7 |
+
"1344 x 768",
|
8 |
+
"768 x 1344",
|
9 |
+
"1536 x 640",
|
10 |
+
"640 x 1536",
|
11 |
+
"Custom",
|
12 |
+
]
|
13 |
+
|
14 |
+
style_list = [
|
15 |
+
{
|
16 |
+
"name": "(None)",
|
17 |
+
"prompt": "{prompt}",
|
18 |
+
},
|
19 |
+
{
|
20 |
+
"name": "Cinematic",
|
21 |
+
"prompt": "{prompt}, cinematic still, emotional, harmonious, vignette, high-budget production, bokeh, cinemascope, moody, epic, breathtaking, film grain, vintage film look, atmospheric lighting, rich textures",
|
22 |
+
},
|
23 |
+
{
|
24 |
+
"name": "Photographic",
|
25 |
+
"prompt": "{prompt}, cinematic photo, shot with a 35mm camera, soft bokeh, professional 4k resolution, hyper-realistic lighting, fine-grain film look, studio quality, beautifully composed",
|
26 |
+
},
|
27 |
+
{
|
28 |
+
"name": "Anime",
|
29 |
+
"prompt": "{prompt}, vibrant anime artwork, highly detailed, studio-quality animation, vivid colors, dynamic composition, emotive character design, energy-filled, inspired by popular anime key visuals",
|
30 |
+
},
|
31 |
+
{
|
32 |
+
"name": "Manga",
|
33 |
+
"prompt": "{prompt}, dynamic manga style, high-energy scenes, crisp line work, bold expressions, iconic Japanese comic style, expressive characters, vibrant panels, intricate detailing, action-packed",
|
34 |
+
},
|
35 |
+
{
|
36 |
+
"name": "Digital Art",
|
37 |
+
"prompt": "{prompt}, concept art, digital painting, painterly textures, cinematic lighting, matte painting style, stunning visual effects, visually captivating, ultra-detailed, imaginative and illustrative",
|
38 |
+
},
|
39 |
+
{
|
40 |
+
"name": "Pixel art",
|
41 |
+
"prompt": "{prompt}, pixel-art style, 8-bit graphics, retro, low-res, nostalgic, charmingly blocky, simplistic but detailed, colorful, evoking the spirit of vintage video games, playful design",
|
42 |
+
},
|
43 |
+
{
|
44 |
+
"name": "Fantasy art",
|
45 |
+
"prompt": "{prompt}, ethereal fantasy concept art, celestial landscapes, magical realism, majestic and otherworldly creatures, painterly strokes, otherworldly lighting, dreamy atmosphere, mystical and enchanting world-building",
|
46 |
+
},
|
47 |
+
{
|
48 |
+
"name": "Neonpunk",
|
49 |
+
"prompt": "{prompt}, neonpunk style, cyberpunk aesthetic, vaporwave vibes, vibrant neon lighting, stunning contrasts, futuristic cityscapes, sleek and ultra-modern design, high energy, magenta and dark purple highlights, intricate detailing",
|
50 |
+
},
|
51 |
+
{
|
52 |
+
"name": "3D Model",
|
53 |
+
"prompt": "{prompt}, professional 3D model rendered in octane, hyper-realistic textures, volumetric lighting effects, dramatic composition, highly detailed design, cutting-edge 3D art, lifelike rendering",
|
54 |
+
},
|
55 |
+
{
|
56 |
+
"name": "Gothic",
|
57 |
+
"prompt": "{prompt}, gothic art style, dark and moody atmosphere, intricate architectural details, high contrast lighting, dramatic shadows, ethereal yet eerie ambiance, elaborate robes and accessories, dark-toned color palette, mysterious and brooding",
|
58 |
+
},
|
59 |
+
{
|
60 |
+
"name": "Steampunk",
|
61 |
+
"prompt": "{prompt}, steampunk style, Victorian-inspired fashion with futuristic mechanical elements, brass, copper, and leather details, steam-powered gadgets, intricate clockwork design, dark earthy tones, rich textures, adventurous, historical yet speculative",
|
62 |
+
},
|
63 |
+
{
|
64 |
+
"name": "Surrealism",
|
65 |
+
"prompt": "{prompt}, surrealistic art, dreamlike and otherworldly imagery, unexpected combinations, distorted forms, unusual perspectives, vibrant colors blending into a chaotic yet beautiful scene, mind-bending compositions, blending reality and fantasy seamlessly",
|
66 |
+
},
|
67 |
+
{
|
68 |
+
"name": "Baroque",
|
69 |
+
"prompt": "{prompt}, baroque art style, highly detailed, lavish and ornate, dramatic use of light and shadow (chiaroscuro), luxurious textures, classical and grandiose composition, rich golden tones, historical richness, intricate patterns and flourishes",
|
70 |
+
},
|
71 |
+
{
|
72 |
+
"name": "Pop Art",
|
73 |
+
"prompt": "{prompt}, pop art style, bold, bright, and colorful, comic book-inspired, exaggerated, graphic imagery, iconic modern symbols, graphic lines, heavy use of primary colors, playful and nostalgic, inspired by Andy Warhol and Roy Lichtenstein",
|
74 |
+
},
|
75 |
+
{
|
76 |
+
"name": "Art Deco",
|
77 |
+
"prompt": "{prompt}, art deco style, geometric patterns, symmetrical designs, luxurious and elegant, metallic accents, 1920s-inspired architecture, sleek lines, opulent color palette with gold, chrome, and jewel tones, glamorous and sophisticated",
|
78 |
+
},
|
79 |
+
{
|
80 |
+
"name": "Retro Futurism",
|
81 |
+
"prompt": "{prompt}, retro futurism style, mid-20th century vision of the future, vintage technology with futuristic elements, neon lights, metallic finishes, vibrant, bold contrasts, sleek designs, space-age aesthetics, bold use of chrome and neon colors",
|
82 |
+
},
|
83 |
+
{
|
84 |
+
"name": "Pulp Fiction",
|
85 |
+
"prompt": "{prompt}, pulp fiction style, comic-book style, exaggerated expressions, high contrast and shadow, gritty and raw, vintage detective or noir vibe, dark tones with bursts of bright red or yellow, pulp magazine aesthetic, vintage typography",
|
86 |
+
},
|
87 |
+
{
|
88 |
+
"name": "Dreamcore",
|
89 |
+
"prompt": "{prompt}, dreamcore style, unsettlingly surreal, distorted reality, strange and abstract visuals, a mix of eerie, innocent, and nostalgic elements, distorted faces, floating objects, distorted environments, unsettling atmosphere with soft lighting and soft textures",
|
90 |
+
},
|
91 |
+
{
|
92 |
+
"name": "Minimalism",
|
93 |
+
"prompt": "{prompt}, minimalist art style, clean and simple lines, sparse use of color, negative space, subtle design elements, understated composition, harmonious and balanced, focus on essential forms and shapes, elegance in simplicity",
|
94 |
+
},
|
95 |
+
{
|
96 |
+
"name": "Gritty Urban",
|
97 |
+
"prompt": "{prompt}, gritty urban art style, street art-inspired, graffiti elements, worn textures, raw and authentic feel, urban decay, bold, aggressive lines, faded and weathered colors, rough concrete and metal surfaces, atmosphere of rebellion",
|
98 |
+
},
|
99 |
+
{
|
100 |
+
"name": "Watercolor",
|
101 |
+
"prompt": "{prompt}, watercolor style, soft and fluid brush strokes, light and airy feel, transparent layers of pigment, delicate and ethereal, gentle blend of hues, romantic, detailed nature scenes, subtle gradients and light reflections, dreamy yet realistic",
|
102 |
+
},
|
103 |
+
{
|
104 |
+
"name": "Impressionism",
|
105 |
+
"prompt": "{prompt}, impressionist style, loose brush strokes, light-filled scenes, emphasis on light and movement, rich colors and dynamic compositions, blurred forms, evocative yet not highly detailed, capturing the essence of a moment in time, natural landscapes or urban settings",
|
106 |
+
},
|
107 |
+
{
|
108 |
+
"name": "Hyperrealism",
|
109 |
+
"prompt": "{prompt}, hyperrealistic art style, extremely detailed, photo-like quality, lifelike textures, meticulous attention to every detail, vibrant, near-perfect reproduction of real life, capturing the beauty in small things, immaculate lighting, and sharp focus",
|
110 |
+
},
|
111 |
+
{
|
112 |
+
"name": "Futurism",
|
113 |
+
"prompt": "{prompt}, futurism art style, dynamic lines, motion and speed, technological advancements, sleek and innovative designs, metallic and digital textures, bright neon lights, abstract representations of the future, energy-filled, angular composition",
|
114 |
+
},
|
115 |
+
{
|
116 |
+
"name": "Victorian Gothic",
|
117 |
+
"prompt": "{prompt}, Victorian gothic style, dark romanticism, ornate Victorian architecture, elegant yet eerie, lace, velvet, and deep red tones, mysterious ambiance, heavy use of shadows, vintage clothing with a gothic twist, haunted, brooding atmosphere",
|
118 |
+
},
|
119 |
+
]
|
requirements.txt
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
gradio
|
2 |
+
together
|
3 |
+
requests
|
4 |
+
Pillow
|
5 |
+
python-dotenv
|
6 |
+
pymysql
|
7 |
+
mysql-connector-python
|
style.css
ADDED
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
:root {
|
2 |
+
--title-font-size: clamp(1.5rem, 6vw, 3rem);
|
3 |
+
--subtitle-font-size: clamp(1rem, 2vw, 1.2rem);
|
4 |
+
}
|
5 |
+
|
6 |
+
h1 {
|
7 |
+
text-align: center;
|
8 |
+
font-size: var(--title-font-size);
|
9 |
+
display: block;
|
10 |
+
}
|
11 |
+
|
12 |
+
h2 {
|
13 |
+
text-align: center;
|
14 |
+
font-size: 2rem;
|
15 |
+
display: block;
|
16 |
+
}
|
17 |
+
|
18 |
+
#duplicate-button {
|
19 |
+
display: block;
|
20 |
+
margin: 1rem auto;
|
21 |
+
color: #fff;
|
22 |
+
background: #1565c0;
|
23 |
+
border-radius: 100vh;
|
24 |
+
padding: 0.5rem 1rem;
|
25 |
+
font-size: clamp(0.8rem, 2vw, 1rem);
|
26 |
+
}
|
27 |
+
|
28 |
+
#component-0 {
|
29 |
+
max-width: min(85%, 600px);
|
30 |
+
margin: 2rem auto;
|
31 |
+
padding: 2rem;
|
32 |
+
}
|
33 |
+
|
34 |
+
@media (max-width: 600px) {
|
35 |
+
#component-0 {
|
36 |
+
max-width: 100%;
|
37 |
+
padding: 1rem;
|
38 |
+
}
|
39 |
+
}
|
40 |
+
|
41 |
+
#title-container {
|
42 |
+
text-align: center;
|
43 |
+
padding: 2rem 0;
|
44 |
+
}
|
45 |
+
|
46 |
+
#title {
|
47 |
+
font-size: var(--title-font-size);
|
48 |
+
color: #333;
|
49 |
+
font-family: 'Helvetica Neue', sans-serif;
|
50 |
+
text-transform: uppercase;
|
51 |
+
background: transparent;
|
52 |
+
}
|
53 |
+
|
54 |
+
#title span {
|
55 |
+
background: linear-gradient(45deg, #4EACEF, #28b485);
|
56 |
+
background-clip: text;
|
57 |
+
color: transparent;
|
58 |
+
}
|
59 |
+
|
60 |
+
#subtitle {
|
61 |
+
text-align: center;
|
62 |
+
font-size: var(--subtitle-font-size);
|
63 |
+
margin-top: 1rem;
|
64 |
+
}
|
65 |
+
|
66 |
+
.accessibility-aid { position: absolute; left: -9999px; }
|
67 |
+
@media (prefers-reduced-motion: reduce) {
|
68 |
+
* { animation: none !important; transition: none !important; }
|
69 |
+
}
|