Spaces:
Running
on
Zero
Running
on
Zero
Update app.py
Browse files
app.py
CHANGED
@@ -1,616 +1,35 @@
|
|
1 |
-
# ===== CRITICAL: Import spaces FIRST before any CUDA operations =====
|
2 |
-
try:
|
3 |
-
import spaces
|
4 |
-
HF_SPACES = True
|
5 |
-
except ImportError:
|
6 |
-
# If running locally, create a dummy decorator
|
7 |
-
def spaces_gpu_decorator(duration=60):
|
8 |
-
def decorator(func):
|
9 |
-
return func
|
10 |
-
return decorator
|
11 |
-
spaces = type('spaces', (), {'GPU': spaces_gpu_decorator})()
|
12 |
-
HF_SPACES = False
|
13 |
-
print("Warning: Running without Hugging Face Spaces GPU allocation")
|
14 |
-
|
15 |
-
# ===== Now import other libraries =====
|
16 |
-
import random
|
17 |
import os
|
18 |
-
import
|
19 |
-
|
20 |
-
|
21 |
-
import gradio as gr
|
22 |
-
import numpy as np
|
23 |
-
import torch
|
24 |
-
from diffusers import StableDiffusionXLPipeline
|
25 |
-
from diffusers import EulerAncestralDiscreteScheduler
|
26 |
-
from compel import Compel, ReturnedEmbeddingsType
|
27 |
-
from PIL import Image
|
28 |
-
|
29 |
-
# ===== Style presets =====
|
30 |
-
STYLE_PRESETS = {
|
31 |
-
"None": "",
|
32 |
-
"Realistic Photo": "photorealistic, 8k, ultra-detailed, cinematic lighting, realistic skin texture",
|
33 |
-
"Oil Painting": "oil painting, rich brush strokes, canvas texture, baroque lighting",
|
34 |
-
"Comic Book": "comic book style, bold ink outlines, cel shading, vibrant colors",
|
35 |
-
"Watercolor": "watercolor illustration, soft gradients, splatter effect, pastel palette",
|
36 |
-
}
|
37 |
-
|
38 |
-
# ===== Random prompt examples =====
|
39 |
-
prompt_examples = [
|
40 |
-
"The shy college girl, with glasses and a tight plaid skirt, nervously approaches her professor",
|
41 |
-
"Her skirt rose a little higher with each gentle push, a soft blush of blush spreading across her cheeks as she felt the satisfying warmth of his breath on her cheek.",
|
42 |
-
"a girl in a school uniform having her skirt pulled up by a boy, and then being fucked",
|
43 |
-
"Moody mature anime scene of two lovers fuck under neon rain, sensual atmosphere",
|
44 |
-
"Moody mature anime scene of two lovers kissing under neon rain, sensual atmosphere",
|
45 |
-
"The girl sits on the boy's lap by the window, his hands resting on her waist. She is unbuttoning his shirt, her expression focused and intense.",
|
46 |
-
"A girl with long, black hair is sleeping on her desk in the classroom. Her skirt has ridden up, revealing her thighs, and a trail of drool escapes her slightly parted lips.",
|
47 |
-
"The waves rolled gently, a slow, sweet kiss of the lip, a slow, slow build of anticipation as their toes bumped gently – a slow, sweet kiss of the lip, a promise of more to come.",
|
48 |
-
"Her elegant silk gown swayed gracefully as she approached him, the delicate fabric brushing against her legs. A warm blush spread across her cheeks as she felt his breath on her face.",
|
49 |
-
"Her white blouse and light cotton skirt rose a little higher with each gentle push, a soft blush spreading across her cheeks as she felt the satisfying warmth of his breath on her cheek.",
|
50 |
-
"A woman in a business suit having her skirt lifted by a man, and then being sexually assaulted.",
|
51 |
-
"The older woman sits on the man's lap by the fireplace, his hands resting on her hips. She is unbuttoning his vest, her expression focused and intense. He takes control of the situation as she finishes unbuttoning his shirt, pushing her onto her back and begins to have sex with her.",
|
52 |
-
"There is a woman with long black hair. Her face features alluring eyes and full lips, with a slender figure adorned in black lace lingerie. She lies on the bed, loosening her lingerie strap with one hand while seductively glancing downward.",
|
53 |
-
"In a dimly lit room, the same woman teases with her dark, flowing hair, now covering her voluptuous breasts, while a black garter belt accentuates her thighs. She sits on the sofa, leaning back, lifting one leg to expose her most private areas through the sheer lingerie.",
|
54 |
-
"A woman with glasses, lying on the bed in just her bra, spreads her legs wide, revealing all! She wears a sultry expression, gazing directly at the viewer with her brown eyes, her short black hair cascading over the pillow. Her slim figure, accentuated by the lacy lingerie, exudes a seductive aura.",
|
55 |
-
"A soft focus on the girl's face, eyes closed, biting her lip, as her roommate performs oral pleasure, the experienced woman's hair cascading between her thighs.",
|
56 |
-
"A woman in a blue hanbok sits on a wooden floor, her legs folded beneath her, gazing out of a window, the sunlight highlighting the graceful lines of her clothing.",
|
57 |
-
"The couple, immersed in a wooden outdoor bath, share an intimate moment, her wet kimono clinging to her curves, his hands exploring her body beneath the water's surface.",
|
58 |
-
"A steamy shower scene, the twins embrace under the warm water, their soapy hands gliding over each other's curves, their passion intensifying as they explore uncharted territories.",
|
59 |
-
"The teacher, with a firm grip, pins the student against the blackboard, her skirt hiked up, exposing her delicate lace panties. Their heavy breathing echoes in the quiet room as they share an intense, intimate moment.",
|
60 |
-
"After hours, the girl sits on top of the teacher's lap, riding him on the classroom floor, her hair cascading over her face as she moves with increasing intensity, their bodies glistening with sweat.",
|
61 |
-
"In the dimly lit dorm room, the roommates lay entangled in a passionate embrace, their naked bodies glistening with sweat, as the experienced woman teaches her lover the art of kissing and touching.",
|
62 |
-
"The once-innocent student, now confident, takes charge, straddling her lover on the couch, their bare skin illuminated by the warm glow of the sunset through the window.",
|
63 |
-
"A close-up of the secretary's hand unzipping her boss's dress shirt, her fingers gently caressing his chest, their eyes locked in a heated embrace in the supply closet.",
|
64 |
-
"The secretary, in a tight pencil skirt and silk blouse, leans back on the boss's desk, her legs wrapped around his waist, her blouse unbuttoned, revealing her lace bra, as he passionately kisses her, his hands exploring her body.",
|
65 |
-
"On the living room couch, one twin sits astride her sister's lap, their lips locked in a passionate kiss, their hands tangled in each other's hair, unraveling a new level of intimacy.",
|
66 |
-
"In a dimly lit chamber, the dominant woman, dressed in a leather corset and thigh-high boots, stands tall, her hand gripping her submissive partner's hair, his eyes closed in submission as she instructs him to please her.",
|
67 |
-
"The dominant, in a sheer lace bodysuit, sits on a throne-like chair, her legs spread, as the submissive, on his knees, worships her with his tongue, his hands bound behind his back.",
|
68 |
-
"A traditional Japanese onsen, with steam rising, a young woman in a colorful kimono kneels on a tatami mat, her back to the viewer, as her male partner, also in a kimono, gently unties her obi, revealing her bare back.",
|
69 |
-
"In a serene outdoor setting, the woman, in a vibrant summer kimono, sits on a bench, her legs slightly spread, her partner kneeling before her, his hands gently caressing her exposed thigh.",
|
70 |
-
]
|
71 |
-
|
72 |
-
# ===== Save directory =====
|
73 |
-
SAVE_DIR = "saved_images"
|
74 |
-
if not os.path.exists(SAVE_DIR):
|
75 |
-
os.makedirs(SAVE_DIR, exist_ok=True)
|
76 |
-
|
77 |
-
# ===== Device & model loading =====
|
78 |
-
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
79 |
-
print(f"Using device: {device}")
|
80 |
-
|
81 |
-
# Add error handling for model loading
|
82 |
-
try:
|
83 |
-
# Make sure to use torch.float16 consistently throughout the pipeline
|
84 |
-
pipeline = StableDiffusionXLPipeline.from_pretrained(
|
85 |
-
"votepurchase/pornmasterPro_noobV3VAE",
|
86 |
-
torch_dtype=torch.float16,
|
87 |
-
variant="fp16", # Explicitly use fp16 variant
|
88 |
-
use_safetensors=True # Use safetensors if available
|
89 |
-
)
|
90 |
-
|
91 |
-
pipeline.scheduler = EulerAncestralDiscreteScheduler.from_config(pipeline.scheduler.config)
|
92 |
-
pipeline.to(device)
|
93 |
-
|
94 |
-
# Force all components to use the same dtype
|
95 |
-
pipeline.text_encoder.to(torch.float16)
|
96 |
-
pipeline.text_encoder_2.to(torch.float16)
|
97 |
-
pipeline.vae.to(torch.float16)
|
98 |
-
pipeline.unet.to(torch.float16)
|
99 |
-
|
100 |
-
# Initialize Compel for long prompt processing
|
101 |
-
compel = Compel(
|
102 |
-
tokenizer=[pipeline.tokenizer, pipeline.tokenizer_2],
|
103 |
-
text_encoder=[pipeline.text_encoder, pipeline.text_encoder_2],
|
104 |
-
returned_embeddings_type=ReturnedEmbeddingsType.PENULTIMATE_HIDDEN_STATES_NON_NORMALIZED,
|
105 |
-
requires_pooled=[False, True],
|
106 |
-
truncate_long_prompts=False
|
107 |
-
)
|
108 |
-
|
109 |
-
print("Model loaded successfully")
|
110 |
-
except Exception as e:
|
111 |
-
print(f"Error loading model: {e}")
|
112 |
-
pipeline = None
|
113 |
-
compel = None
|
114 |
-
|
115 |
-
MAX_SEED = np.iinfo(np.int32).max
|
116 |
-
MAX_IMAGE_SIZE = 1216
|
117 |
|
118 |
-
def
|
119 |
-
"""Apply style preset to prompt."""
|
120 |
-
style_suffix = STYLE_PRESETS.get(style_key, "")
|
121 |
-
if style_suffix:
|
122 |
-
final_prompt = f"{user_prompt}, {style_suffix}"
|
123 |
-
else:
|
124 |
-
final_prompt = user_prompt
|
125 |
-
|
126 |
-
return final_prompt
|
127 |
-
|
128 |
-
def get_random_prompt():
|
129 |
-
"""Get a random prompt from the examples list."""
|
130 |
-
return random.choice(prompt_examples)
|
131 |
-
|
132 |
-
# ===== Image saving =====
|
133 |
-
def save_generated_image(image: Image.Image, prompt: str) -> str:
|
134 |
-
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
135 |
-
unique_id = str(uuid.uuid4())[:8]
|
136 |
-
filename = f"{timestamp}_{unique_id}.png"
|
137 |
-
filepath = os.path.join(SAVE_DIR, filename)
|
138 |
-
image.save(filepath)
|
139 |
-
|
140 |
-
# Save metadata
|
141 |
-
metadata_file = os.path.join(SAVE_DIR, "metadata.txt")
|
142 |
-
with open(metadata_file, "a", encoding="utf-8") as f:
|
143 |
-
f.write(f"{filename}|{prompt}|{timestamp}\n")
|
144 |
-
return filepath
|
145 |
-
|
146 |
-
# ===== Long prompt processing function =====
|
147 |
-
def process_long_prompt(prompt, negative_prompt=""):
|
148 |
-
"""Simple long prompt processing using Compel"""
|
149 |
try:
|
150 |
-
|
151 |
-
|
152 |
-
except Exception as e:
|
153 |
-
print(f"Long prompt processing failed: {e}, falling back to standard processing")
|
154 |
-
return None, None
|
155 |
-
|
156 |
-
# ===== Diffusion call =====
|
157 |
-
def run_pipeline(prompt: str, negative_prompt: str, seed: int, width: int, height: int, guidance_scale: float, num_steps: int):
|
158 |
-
if pipeline is None:
|
159 |
-
raise ValueError("Model pipeline not loaded")
|
160 |
-
|
161 |
-
generator = torch.Generator(device=device).manual_seed(int(seed))
|
162 |
-
|
163 |
-
# Check if prompt is long
|
164 |
-
use_long_prompt = len(prompt.split()) > 60 or len(prompt) > 300
|
165 |
-
|
166 |
-
try:
|
167 |
-
# Try long prompt processing first if prompt is long
|
168 |
-
if use_long_prompt and compel is not None:
|
169 |
-
print("Using long prompt processing...")
|
170 |
-
conditioning, pooled = process_long_prompt(prompt, negative_prompt)
|
171 |
-
|
172 |
-
if conditioning is not None:
|
173 |
-
result = pipeline(
|
174 |
-
prompt_embeds=conditioning[0:1],
|
175 |
-
pooled_prompt_embeds=pooled[0:1],
|
176 |
-
negative_prompt_embeds=conditioning[1:2],
|
177 |
-
negative_pooled_prompt_embeds=pooled[1:2],
|
178 |
-
guidance_scale=guidance_scale,
|
179 |
-
num_inference_steps=num_steps,
|
180 |
-
width=width,
|
181 |
-
height=height,
|
182 |
-
generator=generator
|
183 |
-
).images[0]
|
184 |
-
return result
|
185 |
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
negative_prompt=negative_prompt,
|
190 |
-
guidance_scale=guidance_scale,
|
191 |
-
num_inference_steps=num_steps,
|
192 |
-
width=width,
|
193 |
-
height=height,
|
194 |
-
generator=generator
|
195 |
-
).images[0]
|
196 |
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
# ===== Gradio inference wrapper =====
|
205 |
-
@spaces.GPU(duration=60)
|
206 |
-
def generate_image(
|
207 |
-
user_prompt: str,
|
208 |
-
style_key: str,
|
209 |
-
negative_prompt: str = "nsfw, (low quality, worst quality:1.2), very displeasing, 3d, watermark, signature, ugly, poorly drawn",
|
210 |
-
seed: int = 42,
|
211 |
-
randomize_seed: bool = True,
|
212 |
-
width: int = 1024,
|
213 |
-
height: int = 1024,
|
214 |
-
guidance_scale: float = 7.0,
|
215 |
-
num_inference_steps: int = 28,
|
216 |
-
progress=None,
|
217 |
-
):
|
218 |
-
try:
|
219 |
-
if randomize_seed:
|
220 |
-
seed = random.randint(0, MAX_SEED)
|
221 |
-
|
222 |
-
# Apply style preset
|
223 |
-
final_prompt = prepare_prompt(user_prompt, style_key)
|
224 |
-
print(f"Final prompt: {final_prompt}")
|
225 |
-
|
226 |
-
# Generate image
|
227 |
-
image = run_pipeline(final_prompt, negative_prompt, seed, width, height, guidance_scale, num_inference_steps)
|
228 |
-
|
229 |
-
# Save image
|
230 |
-
save_generated_image(image, final_prompt)
|
231 |
-
|
232 |
-
return image, seed
|
233 |
-
|
234 |
-
except Exception as e:
|
235 |
-
print(f"Error generating image: {e}")
|
236 |
-
# Return a placeholder or error message
|
237 |
-
error_image = Image.new('RGB', (width, height), color='red')
|
238 |
-
return error_image, seed
|
239 |
-
|
240 |
-
# ===== Custom CSS (Pastel Gradient Design) =====
|
241 |
-
custom_css = """
|
242 |
-
:root {
|
243 |
-
--color-primary: #A8E6CF;
|
244 |
-
--color-secondary: #FFD3A5;
|
245 |
-
--color-accent: #FD9BB8;
|
246 |
-
--color-purple: #C5A3F0;
|
247 |
-
--color-blue: #A8D8F0;
|
248 |
-
--color-warm-gray: #F8F9FA;
|
249 |
-
--color-dark-gray: #495057;
|
250 |
-
--background-primary: linear-gradient(135deg, #FFE5F1 0%, #E5F3FF 25%, #F0E5FF 50%, #E5FFE5 75%, #FFF5E5 100%);
|
251 |
-
--background-accent: linear-gradient(135deg, #FFD3E8 0%, #D3E8FF 50%, #E8D3FF 100%);
|
252 |
-
--text-primary: #2D3748;
|
253 |
-
--text-secondary: #718096;
|
254 |
-
--shadow-soft: 0 4px 20px rgba(168, 230, 207, 0.15);
|
255 |
-
--shadow-medium: 0 8px 30px rgba(168, 230, 207, 0.25);
|
256 |
-
--border-radius: 20px;
|
257 |
-
}
|
258 |
-
|
259 |
-
/* Global background */
|
260 |
-
footer {visibility: hidden;}
|
261 |
-
.gradio-container {
|
262 |
-
background: var(--background-primary) !important;
|
263 |
-
min-height: 100vh;
|
264 |
-
font-family: 'Inter', sans-serif;
|
265 |
-
}
|
266 |
-
|
267 |
-
/* Title styles */
|
268 |
-
.title {
|
269 |
-
color: var(--text-primary) !important;
|
270 |
-
font-size: 3rem !important;
|
271 |
-
font-weight: 700 !important;
|
272 |
-
text-align: center;
|
273 |
-
margin: 2rem 0;
|
274 |
-
background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-accent) 50%, var(--color-purple) 100%);
|
275 |
-
-webkit-background-clip: text;
|
276 |
-
-webkit-text-fill-color: transparent;
|
277 |
-
background-clip: text;
|
278 |
-
letter-spacing: -0.02em;
|
279 |
-
}
|
280 |
-
|
281 |
-
.subtitle {
|
282 |
-
color: var(--text-secondary) !important;
|
283 |
-
font-size: 1.2rem !important;
|
284 |
-
text-align: center;
|
285 |
-
margin-bottom: 2rem;
|
286 |
-
font-weight: 400;
|
287 |
-
}
|
288 |
-
|
289 |
-
/* Simple card styles */
|
290 |
-
.model-description {
|
291 |
-
background: linear-gradient(135deg, rgba(255, 255, 255, 0.9) 0%, rgba(248, 187, 208, 0.1) 100%);
|
292 |
-
border: 1px solid rgba(168, 230, 207, 0.3);
|
293 |
-
border-radius: var(--border-radius);
|
294 |
-
padding: 2rem;
|
295 |
-
margin: 1.5rem 0;
|
296 |
-
box-shadow: var(--shadow-soft);
|
297 |
-
backdrop-filter: blur(10px);
|
298 |
-
-webkit-backdrop-filter: blur(10px);
|
299 |
-
}
|
300 |
-
|
301 |
-
.model-description p {
|
302 |
-
color: var(--text-primary) !important;
|
303 |
-
font-size: 1rem;
|
304 |
-
line-height: 1.6;
|
305 |
-
margin: 0;
|
306 |
-
}
|
307 |
-
|
308 |
-
/* Button styles */
|
309 |
-
button.primary {
|
310 |
-
background: var(--background-accent) !important;
|
311 |
-
color: var(--text-primary) !important;
|
312 |
-
border: 1px solid var(--color-primary) !important;
|
313 |
-
border-radius: 15px !important;
|
314 |
-
box-shadow: var(--shadow-soft) !important;
|
315 |
-
transition: all 0.3s ease !important;
|
316 |
-
font-weight: 600 !important;
|
317 |
-
font-size: 0.95rem !important;
|
318 |
-
}
|
319 |
-
|
320 |
-
button.primary:hover {
|
321 |
-
background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-secondary) 100%) !important;
|
322 |
-
transform: translateY(-2px) !important;
|
323 |
-
box-shadow: var(--shadow-medium) !important;
|
324 |
-
}
|
325 |
-
|
326 |
-
/* Random button specific styles */
|
327 |
-
.random-btn {
|
328 |
-
background: linear-gradient(135deg, var(--color-accent) 0%, var(--color-purple) 100%) !important;
|
329 |
-
color: white !important;
|
330 |
-
border: none !important;
|
331 |
-
border-radius: 15px !important;
|
332 |
-
padding: 12px 24px !important;
|
333 |
-
font-weight: 600 !important;
|
334 |
-
box-shadow: var(--shadow-soft) !important;
|
335 |
-
transition: all 0.3s ease !important;
|
336 |
-
}
|
337 |
-
|
338 |
-
.random-btn:hover {
|
339 |
-
transform: translateY(-2px) !important;
|
340 |
-
box-shadow: var(--shadow-medium) !important;
|
341 |
-
background: linear-gradient(135deg, var(--color-purple) 0%, var(--color-blue) 100%) !important;
|
342 |
-
}
|
343 |
-
|
344 |
-
/* Input container */
|
345 |
-
.input-container {
|
346 |
-
background: linear-gradient(135deg, rgba(255, 255, 255, 0.9) 0%, rgba(168, 230, 207, 0.1) 100%);
|
347 |
-
border: 1px solid rgba(168, 230, 207, 0.3);
|
348 |
-
border-radius: var(--border-radius);
|
349 |
-
padding: 1.5rem;
|
350 |
-
margin-bottom: 1.5rem;
|
351 |
-
box-shadow: var(--shadow-soft);
|
352 |
-
backdrop-filter: blur(10px);
|
353 |
-
-webkit-backdrop-filter: blur(10px);
|
354 |
-
}
|
355 |
-
|
356 |
-
/* Advanced settings */
|
357 |
-
.advanced-settings {
|
358 |
-
background: linear-gradient(135deg, rgba(255, 255, 255, 0.8) 0%, rgba(197, 163, 240, 0.1) 100%);
|
359 |
-
border: 1px solid rgba(168, 216, 240, 0.3);
|
360 |
-
border-radius: var(--border-radius);
|
361 |
-
padding: 1.5rem;
|
362 |
-
margin-top: 1rem;
|
363 |
-
box-shadow: var(--shadow-soft);
|
364 |
-
backdrop-filter: blur(8px);
|
365 |
-
-webkit-backdrop-filter: blur(8px);
|
366 |
-
}
|
367 |
-
|
368 |
-
/* Style preset section */
|
369 |
-
.style-preset-section {
|
370 |
-
background: linear-gradient(135deg, rgba(255, 211, 232, 0.3) 0%, rgba(211, 232, 255, 0.3) 100%);
|
371 |
-
border: 1px solid rgba(168, 230, 207, 0.3);
|
372 |
-
border-radius: var(--border-radius);
|
373 |
-
padding: 1.2rem;
|
374 |
-
margin-top: 1rem;
|
375 |
-
box-shadow: var(--shadow-soft);
|
376 |
-
}
|
377 |
-
|
378 |
-
/* Prompt input styles */
|
379 |
-
.large-prompt textarea {
|
380 |
-
min-height: 120px !important;
|
381 |
-
font-size: 15px !important;
|
382 |
-
line-height: 1.5 !important;
|
383 |
-
background: rgba(255, 255, 255, 0.9) !important;
|
384 |
-
border: 2px solid rgba(168, 230, 207, 0.4) !important;
|
385 |
-
border-radius: 15px !important;
|
386 |
-
color: var(--text-primary) !important;
|
387 |
-
transition: all 0.3s ease !important;
|
388 |
-
padding: 1rem !important;
|
389 |
-
}
|
390 |
-
|
391 |
-
.large-prompt textarea:focus {
|
392 |
-
border-color: var(--color-primary) !important;
|
393 |
-
box-shadow: 0 0 0 3px rgba(168, 230, 207, 0.2) !important;
|
394 |
-
outline: none !important;
|
395 |
-
}
|
396 |
-
|
397 |
-
.large-prompt textarea::placeholder {
|
398 |
-
color: var(--text-secondary) !important;
|
399 |
-
font-style: italic;
|
400 |
-
}
|
401 |
-
|
402 |
-
/* Generate button */
|
403 |
-
.small-generate-btn {
|
404 |
-
max-width: 140px !important;
|
405 |
-
height: 48px !important;
|
406 |
-
font-size: 15px !important;
|
407 |
-
padding: 12px 24px !important;
|
408 |
-
border-radius: 15px !important;
|
409 |
-
font-weight: 600 !important;
|
410 |
-
}
|
411 |
-
|
412 |
-
/* Labels */
|
413 |
-
label {
|
414 |
-
color: var(--text-primary) !important;
|
415 |
-
font-weight: 600 !important;
|
416 |
-
font-size: 0.95rem !important;
|
417 |
-
}
|
418 |
-
|
419 |
-
/* Info text */
|
420 |
-
.gr-info, .gr-textbox-info {
|
421 |
-
color: var(--text-secondary) !important;
|
422 |
-
font-size: 0.85rem !important;
|
423 |
-
line-height: 1.4 !important;
|
424 |
-
}
|
425 |
-
|
426 |
-
/* Form elements */
|
427 |
-
input[type="radio"], input[type="checkbox"] {
|
428 |
-
accent-color: var(--color-primary) !important;
|
429 |
-
}
|
430 |
-
|
431 |
-
input[type="range"] {
|
432 |
-
accent-color: var(--color-primary) !important;
|
433 |
-
}
|
434 |
-
|
435 |
-
/* Result image container */
|
436 |
-
.image-container {
|
437 |
-
border-radius: var(--border-radius) !important;
|
438 |
-
overflow: hidden !important;
|
439 |
-
box-shadow: var(--shadow-medium) !important;
|
440 |
-
background: rgba(255, 255, 255, 0.9) !important;
|
441 |
-
border: 1px solid rgba(168, 230, 207, 0.2) !important;
|
442 |
-
}
|
443 |
-
|
444 |
-
/* Slider containers */
|
445 |
-
.gr-slider {
|
446 |
-
margin: 0.5rem 0 !important;
|
447 |
-
}
|
448 |
-
|
449 |
-
/* Accordion styles */
|
450 |
-
.gr-accordion {
|
451 |
-
border: 1px solid rgba(168, 230, 207, 0.3) !important;
|
452 |
-
border-radius: var(--border-radius) !important;
|
453 |
-
background: rgba(255, 255, 255, 0.8) !important;
|
454 |
-
}
|
455 |
-
|
456 |
-
.gr-accordion-header {
|
457 |
-
background: var(--background-accent) !important;
|
458 |
-
color: var(--text-primary) !important;
|
459 |
-
font-weight: 600 !important;
|
460 |
-
border-radius: var(--border-radius) var(--border-radius) 0 0 !important;
|
461 |
-
}
|
462 |
-
|
463 |
-
/* Smooth animations */
|
464 |
-
.model-description, .input-container, .style-preset-section {
|
465 |
-
animation: fadeInUp 0.4s ease-out;
|
466 |
-
}
|
467 |
-
|
468 |
-
@keyframes fadeInUp {
|
469 |
-
from {
|
470 |
-
opacity: 0;
|
471 |
-
transform: translateY(20px);
|
472 |
-
}
|
473 |
-
to {
|
474 |
-
opacity: 1;
|
475 |
-
transform: translateY(0);
|
476 |
-
}
|
477 |
-
}
|
478 |
-
|
479 |
-
/* Improved text readability */
|
480 |
-
* {
|
481 |
-
-webkit-font-smoothing: antialiased;
|
482 |
-
-moz-osx-font-smoothing: grayscale;
|
483 |
-
}
|
484 |
-
|
485 |
-
/* Dropdown and select styles */
|
486 |
-
select, .gr-dropdown {
|
487 |
-
background: rgba(255, 255, 255, 0.9) !important;
|
488 |
-
border: 1px solid rgba(168, 230, 207, 0.3) !important;
|
489 |
-
border-radius: 10px !important;
|
490 |
-
color: var(--text-primary) !important;
|
491 |
-
}
|
492 |
-
|
493 |
-
/* Checkbox and radio button improvements */
|
494 |
-
.gr-checkbox, .gr-radio {
|
495 |
-
background: transparent !important;
|
496 |
-
}
|
497 |
-
|
498 |
-
/* Overall container margin adjustment */
|
499 |
-
.gr-container {
|
500 |
-
max-width: 1200px !important;
|
501 |
-
margin: 0 auto !important;
|
502 |
-
padding: 2rem 1rem !important;
|
503 |
-
}
|
504 |
-
|
505 |
-
/* Mobile responsive */
|
506 |
-
@media (max-width: 768px) {
|
507 |
-
.title {
|
508 |
-
font-size: 2.2rem !important;
|
509 |
-
}
|
510 |
-
|
511 |
-
.model-description, .input-container, .advanced-settings {
|
512 |
-
padding: 1rem !important;
|
513 |
-
margin: 1rem 0 !important;
|
514 |
-
}
|
515 |
-
|
516 |
-
.large-prompt textarea {
|
517 |
-
min-height: 100px !important;
|
518 |
-
font-size: 14px !important;
|
519 |
-
}
|
520 |
-
}
|
521 |
-
"""
|
522 |
-
|
523 |
-
# ===== Gradio UI =====
|
524 |
-
def create_interface():
|
525 |
-
with gr.Blocks(css=custom_css, analytics_enabled=False) as demo:
|
526 |
-
with gr.Group(elem_classes="model-description"):
|
527 |
-
gr.HTML("""
|
528 |
-
<p>
|
529 |
-
<strong>Adult AI Image Generator</strong><br>
|
530 |
-
<small style="opacity: 0.8;">High-quality image generation powered by StableDiffusionXL. Supports long prompts and various artistic styles.</small><br><br>
|
531 |
-
""")
|
532 |
-
|
533 |
-
# ===== Main input =====
|
534 |
-
with gr.Column():
|
535 |
-
with gr.Row(elem_classes="input-container"):
|
536 |
-
with gr.Column(scale=3):
|
537 |
-
user_prompt = gr.Text(
|
538 |
-
label="Prompt",
|
539 |
-
max_lines=5,
|
540 |
-
value="",
|
541 |
-
elem_classes="large-prompt",
|
542 |
-
placeholder="Enter your image description here..."
|
543 |
-
)
|
544 |
-
with gr.Column(scale=1):
|
545 |
-
with gr.Row():
|
546 |
-
run_button = gr.Button(
|
547 |
-
"Generate",
|
548 |
-
variant="primary",
|
549 |
-
elem_classes="small-generate-btn"
|
550 |
-
)
|
551 |
-
with gr.Row():
|
552 |
-
random_button = gr.Button(
|
553 |
-
"🎲 Random",
|
554 |
-
elem_classes="random-btn"
|
555 |
-
)
|
556 |
-
|
557 |
-
# Style preset section
|
558 |
-
with gr.Group(elem_classes="style-preset-section"):
|
559 |
-
style_select = gr.Radio(
|
560 |
-
label="🎨 Style Preset",
|
561 |
-
choices=list(STYLE_PRESETS.keys()),
|
562 |
-
value="None",
|
563 |
-
interactive=True
|
564 |
-
)
|
565 |
-
|
566 |
-
result_image = gr.Image(label="Generated Image")
|
567 |
-
seed_output = gr.Number(label="Seed")
|
568 |
-
|
569 |
-
# ===== Advanced settings =====
|
570 |
-
with gr.Accordion("Advanced Settings", open=False, elem_classes="advanced-settings"):
|
571 |
-
negative_prompt = gr.Text(
|
572 |
-
label="Negative prompt",
|
573 |
-
max_lines=1,
|
574 |
-
placeholder="Enter a negative prompt",
|
575 |
-
value="nsfw, (low quality, worst quality:1.2), very displeasing, 3d, watermark, signature, ugly, poorly drawn"
|
576 |
-
)
|
577 |
-
|
578 |
-
seed = gr.Slider(label="Seed", minimum=0, maximum=MAX_SEED, step=1, value=42)
|
579 |
-
randomize_seed = gr.Checkbox(label="Randomize seed", value=True)
|
580 |
-
with gr.Row():
|
581 |
-
width = gr.Slider(label="Width", minimum=256, maximum=MAX_IMAGE_SIZE, step=32, value=1024)
|
582 |
-
height = gr.Slider(label="Height", minimum=256, maximum=MAX_IMAGE_SIZE, step=32, value=1024)
|
583 |
-
with gr.Row():
|
584 |
-
guidance_scale = gr.Slider(label="Guidance scale", minimum=0.0, maximum=20.0, step=0.1, value=7.0)
|
585 |
-
num_inference_steps = gr.Slider(label="Inference steps", minimum=1, maximum=50, step=1, value=28)
|
586 |
-
|
587 |
-
# ===== Events =====
|
588 |
-
run_button.click(
|
589 |
-
fn=generate_image,
|
590 |
-
inputs=[
|
591 |
-
user_prompt,
|
592 |
-
style_select,
|
593 |
-
negative_prompt,
|
594 |
-
seed,
|
595 |
-
randomize_seed,
|
596 |
-
width,
|
597 |
-
height,
|
598 |
-
guidance_scale,
|
599 |
-
num_inference_steps,
|
600 |
-
],
|
601 |
-
outputs=[result_image, seed_output],
|
602 |
-
)
|
603 |
|
604 |
-
#
|
605 |
-
|
606 |
-
|
607 |
-
|
608 |
-
|
609 |
-
|
610 |
-
|
|
|
|
|
|
|
611 |
|
612 |
-
# ===== Application execution =====
|
613 |
if __name__ == "__main__":
|
614 |
-
|
615 |
-
demo.queue()
|
616 |
-
demo.launch()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
import os
|
2 |
+
import sys
|
3 |
+
import streamlit as st
|
4 |
+
from tempfile import NamedTemporaryFile
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5 |
|
6 |
+
def main():
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
7 |
try:
|
8 |
+
# Get the code from secrets
|
9 |
+
code = os.environ.get("MAIN_CODE")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
10 |
|
11 |
+
if not code:
|
12 |
+
st.error("⚠️ The application code wasn't found in secrets. Please add the MAIN_CODE secret.")
|
13 |
+
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
14 |
|
15 |
+
# Create a temporary Python file
|
16 |
+
with NamedTemporaryFile(suffix='.py', delete=False, mode='w') as tmp:
|
17 |
+
tmp.write(code)
|
18 |
+
tmp_path = tmp.name
|
19 |
+
|
20 |
+
# Execute the code
|
21 |
+
exec(compile(code, tmp_path, 'exec'), globals())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
22 |
|
23 |
+
# Clean up the temporary file
|
24 |
+
try:
|
25 |
+
os.unlink(tmp_path)
|
26 |
+
except:
|
27 |
+
pass
|
28 |
+
|
29 |
+
except Exception as e:
|
30 |
+
st.error(f"⚠️ Error loading or executing the application: {str(e)}")
|
31 |
+
import traceback
|
32 |
+
st.code(traceback.format_exc())
|
33 |
|
|
|
34 |
if __name__ == "__main__":
|
35 |
+
main()
|
|
|
|