Update app.py
Browse files
app.py
CHANGED
|
@@ -11,212 +11,125 @@ import sys
|
|
| 11 |
import os
|
| 12 |
import re
|
| 13 |
import random
|
| 14 |
-
import torch
|
| 15 |
import gc
|
| 16 |
import logging
|
| 17 |
import time
|
| 18 |
import io
|
| 19 |
import threading
|
| 20 |
-
import urllib.request
|
| 21 |
-
import zipfile
|
| 22 |
from datetime import datetime
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
StableDiffusionXLImg2ImgPipeline,
|
| 26 |
-
EulerDiscreteScheduler,
|
| 27 |
-
)
|
| 28 |
-
from huggingface_hub import hf_hub_download, HfApi, CommitOperationAdd
|
| 29 |
-
from huggingface_hub.utils import HfHubHTTPError
|
| 30 |
from PIL import Image
|
| 31 |
import gradio as gr
|
|
|
|
|
|
|
| 32 |
|
| 33 |
-
# =====
|
| 34 |
logging.basicConfig(
|
| 35 |
level=logging.INFO,
|
| 36 |
format="%(asctime)s - %(levelname)s - %(message)s",
|
| 37 |
handlers=[logging.StreamHandler(sys.stdout)],
|
| 38 |
)
|
| 39 |
-
logger = logging.getLogger("
|
| 40 |
-
logger.info("🚀
|
| 41 |
|
| 42 |
if hasattr(sys.stdout, "reconfigure"):
|
| 43 |
sys.stdout.reconfigure(encoding="utf-8")
|
| 44 |
if hasattr(sys.stderr, "reconfigure"):
|
| 45 |
sys.stderr.reconfigure(encoding="utf-8")
|
| 46 |
|
| 47 |
-
|
| 48 |
-
|
|
|
|
| 49 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 50 |
MODEL_REPOS = {
|
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
"Juggernaut-XL_v9_RunDiffusionPhoto_v2.safetensors": "saber-end/Juggernaut-XL-v9",
|
| 52 |
"RealVisXL_V4.0.safetensors": "SG161222/RealVisXL_V4.0",
|
| 53 |
-
"CyberRealisticXLPlay_V10.0_FP16.safetensors": "cyberdelia/CyberRealisticXL",
|
| 54 |
-
"animagine-xl-4.0.safetensors": "cagliostrolab/animagine-xl-4.0",
|
| 55 |
-
"OnomaAIResearch_Illustrious-XL-v1.0.safetensors": "AriaVale2/my-safetensors-backup",
|
| 56 |
"Pony Diffusion V6 XL.safetensors": "AriaVale2/my-safetensors-backup",
|
| 57 |
"Hassaku XL (Illustrious).safetensors": "AriaVale2/my-safetensors-backup",
|
| 58 |
-
"
|
| 59 |
-
"
|
| 60 |
-
"pornworksHardcoreFantasy_v04.safetensors": "AI-Porn/PornWorks-Hardcore-Fantasy-NSFW-3D-SDXL-Chekpoint",
|
| 61 |
"UnfilteredAI_NSFW-GEN-ANIME-v2.safetensors": "AriaVale2/my-safetensors-backup",
|
| 62 |
"Mala Anime Mix NSFW PonyXL.safetensors": "AriaVale2/my-safetensors-backup",
|
| 63 |
"PornWorks_Anime_Desire_NSFW_Anime_Hentai_SDXL_Pony_Chekpoint.safetensors": "AriaVale2/my-safetensors-backup",
|
| 64 |
}
|
| 65 |
|
| 66 |
DIFFUSERS_MODELS = {
|
| 67 |
-
"OhMyDaughter Beautiful Woman (Pony)": "John6666/omd-ohmaydaughter-beautiful-woman-pony-v10-sdxl",
|
| 68 |
"OhMySister Beautiful Asian Woman": "John6666/oms-oh-my-sister-beautiful-asian-woman-v10-sdxl",
|
|
|
|
| 69 |
"Fucktastic Anime Pony NSFW": "John6666/fucktastic-anime-checkpoint-pony-pdxl-porn-nsfw-sfw-22-sdxl",
|
| 70 |
"Mala Anime Mix NSFW Pony v7": "John6666/mala-anime-mix-nsfw-pony-xl-v7-sdxl",
|
| 71 |
"YAAI WAI NSFW Illustrious": "John6666/yaai-yet-another-anime-illustrious-model-full-fine-tune-based-on-wai-nsfw-v20-sdxl",
|
| 72 |
"Flare Diffusion NSFW Anime": "John6666/flare-diffusion-nsfw-v16-semi-realism-anime-sdxl",
|
| 73 |
-
"Perfect RSB Anime Cartoon Hentai": "John6666/perfect-rsb-mix-illustrious-real-anime-sfw-nsfw-definitive-iota-sdxl",
|
| 74 |
"PornWorks Anime Desire (Diffusers)": "John6666/pornworks-anime-desire-nsfw-pony-sdxl",
|
| 75 |
-
"NSFW Anime XL v1": "John6666/nsfw-anime-xl-v1-sdxl",
|
| 76 |
-
"Coco Anime Pony Turnup": "John6666/coco-anime-pony-model-final-sfw-nsfw-turnup-sdxl",
|
| 77 |
-
"Easy Full NSFW Pony": "John6666/easy-sfw-nsfw-pony-xl-fullnsfwv1-sdxl",
|
| 78 |
-
"Perfect RSB Pony Real Anime": "John6666/perfect-rsb-mix-pony-real-anime-sfw-nsfw-lastversion-sdxl",
|
| 79 |
}
|
| 80 |
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
LAST_GEN_TIME = {}
|
| 84 |
-
COOLDOWN_SECONDS = 10
|
| 85 |
|
| 86 |
-
|
| 87 |
-
STYLE_KEYWORDS = {
|
| 88 |
"Men Only": {
|
| 89 |
-
"prefix": "for men's eyes only, hyper sexual pinup
|
| 90 |
-
"suffix": "deep cleavage
|
| 91 |
},
|
| 92 |
-
"
|
| 93 |
-
"prefix": "
|
| 94 |
-
"suffix": "
|
| 95 |
},
|
| 96 |
-
"
|
| 97 |
-
"prefix": "
|
| 98 |
-
"suffix": "
|
| 99 |
},
|
| 100 |
"Rembrandt Oil": {
|
| 101 |
-
"prefix": "Rembrandt oil painting, warm
|
| 102 |
-
"suffix": "classical academic nude energy
|
| 103 |
-
},
|
| 104 |
-
"Rubens Baroque": {
|
| 105 |
-
"prefix": "Rubens baroque oil painting, lush soft flesh, warm golden glow, dramatic drapery",
|
| 106 |
-
"suffix": "voluptuous hourglass, soft weighty breasts, old master nude",
|
| 107 |
-
},
|
| 108 |
-
"Caravaggio Light": {
|
| 109 |
-
"prefix": "Caravaggio Baroque oil painting, dramatic chiaroscuro, candle lit, deep black background",
|
| 110 |
-
"suffix": "spotlight on body, erotic old master composition",
|
| 111 |
-
},
|
| 112 |
-
"Ingres Salon": {
|
| 113 |
-
"prefix": "Jean-Auguste-Dominique Ingres neoclassical oil painting, smooth porcelain skin, salon nude",
|
| 114 |
-
"suffix": "elegant erotic academic pose, refined classical anatomy",
|
| 115 |
-
},
|
| 116 |
-
"Klimt Gold": {
|
| 117 |
-
"prefix": "Gustav Klimt style, gold leaf patterns, ornate mosaic backdrop, Art Nouveau erotic",
|
| 118 |
-
"suffix": "decorative gold frame energy, sensual patterned nude",
|
| 119 |
-
},
|
| 120 |
-
"Art Nouveau": {
|
| 121 |
-
"prefix": "Alphonse Mucha Art Nouveau poster, gold ornate linework, floral decorative borders",
|
| 122 |
-
"suffix": "elegant erotic illustration, flowing hair, classic poster nude",
|
| 123 |
-
},
|
| 124 |
-
"Titian Venetian": {
|
| 125 |
-
"prefix": "Titian Venetian Renaissance oil, rich red drapery, warm skin glazes",
|
| 126 |
-
"suffix": "reclining odalisque energy, classical erotic painting",
|
| 127 |
-
},
|
| 128 |
-
"Boucher Rococo": {
|
| 129 |
-
"prefix": "Francois Boucher rococo oil, soft pastel light, silk pillows, playful erotic",
|
| 130 |
-
"suffix": "flushed skin, intimate boudoir nude",
|
| 131 |
-
},
|
| 132 |
-
"Nude": {
|
| 133 |
-
"prefix": "Full body",
|
| 134 |
-
"suffix": "nude woman, large breasts, full breasts, heavy breasts, bare breasts",
|
| 135 |
-
},
|
| 136 |
-
"Editorial Lingerie": {
|
| 137 |
-
"prefix": "photorealistic editorial fashion photo, dramatic side lighting, chiaroscuro",
|
| 138 |
-
"suffix": "high-end lingerie campaign, soft warm interior background, sharp focus, cleavage",
|
| 139 |
-
},
|
| 140 |
-
"Erk Pin-up": {
|
| 141 |
-
"prefix": "Erk IAart style, polished anime digital pin-up, clean lineart, soft airbrushed shading",
|
| 142 |
-
"suffix": "hourglass figure, lace thigh highs, garter straps, soft warm lighting, large breasts, cleavage",
|
| 143 |
},
|
| 144 |
"4K TV Wide": {
|
| 145 |
-
"prefix": "16:9 ultra wide
|
| 146 |
-
"suffix": "
|
| 147 |
-
},
|
| 148 |
-
"Harness Chains": {
|
| 149 |
-
"prefix": "dark emerald leather body harness, thick straps, metal rings buckles, silver chains draped across chest and hips",
|
| 150 |
-
"suffix": "harness framing and lifting breasts, oiled shiny skin, ultra detailed leather and chain metal",
|
| 151 |
},
|
| 152 |
-
"
|
| 153 |
-
"prefix": "
|
| 154 |
-
"suffix": "
|
| 155 |
},
|
| 156 |
}
|
| 157 |
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
"
|
| 163 |
-
"large breasts, full breasts, heavy breasts, cleavage, deep cleavage, "
|
| 164 |
-
"perky breasts, round breasts, natural breasts, breasts straining fabric, "
|
| 165 |
-
"tiny waist, wide hips, thick thighs, hourglass figure, "
|
| 166 |
-
"ultra-detailed iris, sub-surface skin scattering, smooth detailed skin texture, "
|
| 167 |
-
"seductive expression, bedroom eyes, parted glossy lips"
|
| 168 |
)
|
| 169 |
|
| 170 |
BASE_NEGATIVES = (
|
| 171 |
-
"
|
| 172 |
-
"
|
| 173 |
-
"
|
| 174 |
-
"bad anatomy, wrong anatomy, bad eyes, deformed eyes, mismatched eyes, dead eyes, empty eyes, "
|
| 175 |
-
"blurry eyes, poorly drawn eyes, low quality eyes, extra limbs, extra arms, extra legs, mutated, "
|
| 176 |
-
"disfigured, ugly, deformed, bad proportions, asymmetrical breasts, "
|
| 177 |
-
"bad face, ugly face, blurry face, plastic skin, doll, barbie, child, loli, underage, censored, "
|
| 178 |
-
"watermark, text, signature, grainy, noisy, lowres, worst quality, "
|
| 179 |
-
"elongated torso, stretched body, distorted anatomy, long neck, deformed spine, elongated body"
|
| 180 |
)
|
| 181 |
|
| 182 |
-
MODEL_CHOICES = [
|
| 183 |
-
"Fucktastic Anime Pony NSFW",
|
| 184 |
-
"Mala Anime Mix NSFW Pony v7",
|
| 185 |
-
"YAAI WAI NSFW Illustrious",
|
| 186 |
-
"Flare Diffusion NSFW Anime",
|
| 187 |
-
"Perfect RSB Anime Cartoon Hentai",
|
| 188 |
-
"Perfect RSB Pony Real Anime",
|
| 189 |
-
"PornWorks Anime Desire (Diffusers)",
|
| 190 |
-
"NSFW Anime XL v1",
|
| 191 |
-
"Coco Anime Pony Turnup",
|
| 192 |
-
"Easy Full NSFW Pony",
|
| 193 |
-
"OhMyDaughter Beautiful Woman (Pony)",
|
| 194 |
-
"OhMySister Beautiful Asian Woman",
|
| 195 |
-
"PornWorks_Anime_Desire_NSFW_Anime_Hentai_SDXL_Pony_Chekpoint.safetensors",
|
| 196 |
-
"UnfilteredAI_NSFW-GEN-ANIME-v2.safetensors",
|
| 197 |
-
"Mala Anime Mix NSFW PonyXL.safetensors",
|
| 198 |
-
"pornworksSexyBeauty_v04.safetensors",
|
| 199 |
-
"pornworksRealPornPhoto_v04.safetensors",
|
| 200 |
-
"pornworksHardcoreFantasy_v04.safetensors",
|
| 201 |
-
"CyberRealisticXLPlay_V10.0_FP16.safetensors",
|
| 202 |
-
"Pony Diffusion V6 XL.safetensors",
|
| 203 |
-
"Hassaku XL (Illustrious).safetensors",
|
| 204 |
-
"animagine-xl-4.0.safetensors",
|
| 205 |
-
"OnomaAIResearch_Illustrious-XL-v1.0.safetensors",
|
| 206 |
-
"Juggernaut-XL_v9_RunDiffusionPhoto_v2.safetensors",
|
| 207 |
-
"RealVisXL_V4.0.safetensors",
|
| 208 |
-
]
|
| 209 |
-
|
| 210 |
-
BLEND_NONE = "(none - skip this chef)"
|
| 211 |
-
MODEL_CHOICES_OPTIONAL = [BLEND_NONE] + MODEL_CHOICES
|
| 212 |
-
|
| 213 |
RANDOM_PROMPTS = [
|
| 214 |
-
"adult
|
| 215 |
-
"adult
|
| 216 |
-
"adult
|
| 217 |
]
|
| 218 |
|
| 219 |
-
#
|
|
|
|
|
|
|
| 220 |
FORBIDDEN_KEYWORDS = []
|
| 221 |
if os.path.exists("restricted_words.txt"):
|
| 222 |
try:
|
|
@@ -227,17 +140,17 @@ if os.path.exists("restricted_words.txt"):
|
|
| 227 |
if line.strip() and not line.strip().startswith("#")
|
| 228 |
]
|
| 229 |
except Exception as e:
|
| 230 |
-
logger.error(f"
|
| 231 |
|
| 232 |
AGE_PATTERN = re.compile(
|
| 233 |
r"\b(one|two|three|four|five|six|seven|eight|nine|ten|"
|
| 234 |
-
r"eleven|twelve|thirteen|fourteen|fifteen|sixteen|seventeen"
|
| 235 |
-
r"
|
| 236 |
r"\s*[-\s]?\s*(year|yr|y\/o|yo)\b"
|
| 237 |
)
|
| 238 |
|
| 239 |
|
| 240 |
-
def check_prompt_safety(prompt):
|
| 241 |
clean = prompt.lower().replace("-", " ").replace("_", " ")
|
| 242 |
for word in FORBIDDEN_KEYWORDS:
|
| 243 |
if word in clean:
|
|
@@ -248,15 +161,14 @@ def check_prompt_safety(prompt):
|
|
| 248 |
return True, None
|
| 249 |
|
| 250 |
|
| 251 |
-
def
|
| 252 |
-
|
| 253 |
-
if not selected_styles:
|
| 254 |
return "", ""
|
| 255 |
-
if isinstance(
|
| 256 |
-
|
| 257 |
prefixes, suffixes = [], []
|
| 258 |
-
for name in
|
| 259 |
-
cfg =
|
| 260 |
if not cfg:
|
| 261 |
continue
|
| 262 |
if cfg.get("prefix"):
|
|
@@ -266,571 +178,358 @@ def build_style_layers(selected_styles):
|
|
| 266 |
return ", ".join(prefixes), ", ".join(suffixes)
|
| 267 |
|
| 268 |
|
| 269 |
-
|
| 270 |
-
|
| 271 |
-
|
| 272 |
-
if not bot_token or not chat_id:
|
| 273 |
-
return
|
| 274 |
-
try:
|
| 275 |
-
zip_buffer = io.BytesIO()
|
| 276 |
-
with zipfile.ZipFile(zip_buffer, "w", zipfile.ZIP_DEFLATED) as zip_file:
|
| 277 |
-
zip_file.writestr(f"{filename_base}.webp", img_bytes)
|
| 278 |
-
zip_buffer.seek(0)
|
| 279 |
-
zip_data = zip_buffer.getvalue()
|
| 280 |
-
|
| 281 |
-
safe_prompt = prompt if len(prompt) < 700 else prompt[:700] + "..."
|
| 282 |
-
caption_text = (
|
| 283 |
-
f"📦 ADULT AI ZIP ARCHIVE DISPATCH 📦\n"
|
| 284 |
-
f"━━━━━━━━━━━━━━━━━━━\n"
|
| 285 |
-
f"👤 User ID: {username} \n"
|
| 286 |
-
f"🤖 Model: {model_name} \n"
|
| 287 |
-
f"🎲 Seed Token: {seed} \n"
|
| 288 |
-
f"📐 Resolution: {width}x{height} \n"
|
| 289 |
-
f"📊 Configs: {steps} steps | CFG {cfg_scale}\n"
|
| 290 |
-
f"━━━━━━━━━━━━━━━━━━━\n"
|
| 291 |
-
f"💬 Prompt: \n {safe_prompt} "
|
| 292 |
-
)
|
| 293 |
-
|
| 294 |
-
boundary = "----AdultAIEngineBoundary" + str(int(time.time()))
|
| 295 |
-
body = []
|
| 296 |
-
parts = [
|
| 297 |
-
("chat_id", chat_id),
|
| 298 |
-
("caption", caption_text),
|
| 299 |
-
("parse_mode", "HTML"),
|
| 300 |
-
]
|
| 301 |
-
for key, val in parts:
|
| 302 |
-
body.append(f"--{boundary}".encode("utf-8"))
|
| 303 |
-
body.append(f'Content-Disposition: form-data; name="{key}"'.encode("utf-8"))
|
| 304 |
-
body.append(b"")
|
| 305 |
-
body.append(str(val).encode("utf-8"))
|
| 306 |
-
|
| 307 |
-
body.append(f"--{boundary}".encode("utf-8"))
|
| 308 |
-
body.append(
|
| 309 |
-
f'Content-Disposition: form-data; name="document"; filename="{filename_base}.zip"'.encode("utf-8")
|
| 310 |
-
)
|
| 311 |
-
body.append(b"Content-Type: application/zip")
|
| 312 |
-
body.append(b"")
|
| 313 |
-
body.append(zip_data)
|
| 314 |
-
body.append(f"--{boundary}--".encode("utf-8"))
|
| 315 |
-
body.append(b"")
|
| 316 |
-
|
| 317 |
-
payload_body = b"\r\n".join(body)
|
| 318 |
-
req = urllib.request.Request(
|
| 319 |
-
f"https://api.telegram.org/bot{bot_token}/sendDocument",
|
| 320 |
-
data=payload_body,
|
| 321 |
-
method="POST",
|
| 322 |
-
)
|
| 323 |
-
req.add_header("Content-Type", f"multipart/form-data; boundary={boundary}")
|
| 324 |
-
req.add_header("Content-Length", str(len(payload_body)))
|
| 325 |
-
with urllib.request.urlopen(req, timeout=15) as response:
|
| 326 |
-
response.read()
|
| 327 |
-
except Exception as e:
|
| 328 |
-
logger.error(f"⚠️ Telegram dispatch failed: {str(e)}")
|
| 329 |
-
|
| 330 |
|
| 331 |
-
def
|
| 332 |
hf_token = os.environ.get("HF_TOKEN")
|
| 333 |
-
if not username or username.strip()
|
| 334 |
username = "Unknown_Visitor"
|
| 335 |
-
|
| 336 |
timestamp = int(time.time())
|
| 337 |
-
|
| 338 |
-
filename_base = f"gen_{timestamp}_seed_{seed}_{
|
| 339 |
-
|
| 340 |
-
|
| 341 |
-
|
| 342 |
-
raw_webp_bytes = img_byte_arr.getvalue()
|
| 343 |
-
|
| 344 |
-
send_telegram_zipped_document(
|
| 345 |
-
raw_webp_bytes, filename_base, username, model_name, prompt, seed, width, height, steps, cfg_scale
|
| 346 |
-
)
|
| 347 |
-
|
| 348 |
if not hf_token:
|
| 349 |
-
logger.error(
|
| 350 |
-
f"❌ HF_TOKEN not found in environment for {username}'s generation — upload skipped entirely (this is the silent failure)."
|
| 351 |
-
)
|
| 352 |
return
|
| 353 |
-
|
| 354 |
api = HfApi(token=hf_token)
|
| 355 |
date_str = time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(timestamp))
|
| 356 |
log_entry = f"[{date_str}] | User: {username} | Model: {model_name} | Seed: {seed} | Prompt: {prompt[:150]}...\n"
|
| 357 |
-
|
| 358 |
-
|
| 359 |
-
max_attempts = 4
|
| 360 |
-
for attempt in range(1, max_attempts + 1):
|
| 361 |
try:
|
| 362 |
try:
|
| 363 |
-
|
| 364 |
-
|
| 365 |
-
filename="traffic_logs.txt",
|
| 366 |
-
repo_type="dataset",
|
| 367 |
-
token=hf_token,
|
| 368 |
-
)
|
| 369 |
-
with open(traffic_path, "r", encoding="utf-8") as f:
|
| 370 |
-
current_logs = f.read()
|
| 371 |
except Exception:
|
| 372 |
-
|
| 373 |
-
|
| 374 |
try:
|
| 375 |
-
|
| 376 |
-
|
| 377 |
-
filename=user_log_filename,
|
| 378 |
-
repo_type="dataset",
|
| 379 |
-
token=hf_token,
|
| 380 |
-
)
|
| 381 |
-
with open(user_log_path, "r", encoding="utf-8") as f:
|
| 382 |
-
user_logs = f.read()
|
| 383 |
except Exception:
|
| 384 |
user_logs = f"=== HISTORY FOR {username} ===\n"
|
| 385 |
-
|
| 386 |
-
|
| 387 |
-
CommitOperationAdd(
|
| 388 |
-
|
| 389 |
-
path_or_fileobj=(current_logs + log_entry).encode("utf-8"),
|
| 390 |
-
),
|
| 391 |
-
CommitOperationAdd(
|
| 392 |
-
path_in_repo=user_log_filename,
|
| 393 |
-
path_or_fileobj=(user_logs + log_entry).encode("utf-8"),
|
| 394 |
-
),
|
| 395 |
-
CommitOperationAdd(
|
| 396 |
-
path_in_repo=f"data/{filename_base}.webp",
|
| 397 |
-
path_or_fileobj=raw_webp_bytes,
|
| 398 |
-
),
|
| 399 |
]
|
| 400 |
-
|
| 401 |
api.create_commit(
|
| 402 |
repo_id=BACKUP_DATASET_ID,
|
| 403 |
repo_type="dataset",
|
| 404 |
-
operations=
|
| 405 |
commit_message=f"gen for {username} @ {timestamp}",
|
| 406 |
)
|
| 407 |
-
logger.info(f"☁️
|
| 408 |
return
|
| 409 |
-
except HfHubHTTPError as e:
|
| 410 |
-
status = getattr(e.response, "status_code", None)
|
| 411 |
-
if status == 429:
|
| 412 |
-
wait = 2**attempt
|
| 413 |
-
logger.error(
|
| 414 |
-
f"⚠️ Rate limited (429) on attempt {attempt}/{max_attempts} for {username}. Retrying in {wait}s."
|
| 415 |
-
)
|
| 416 |
-
time.sleep(wait)
|
| 417 |
-
continue
|
| 418 |
-
logger.error(f"⚠️ Storage worker HTTP error ({status}) for {username}: {str(e)}")
|
| 419 |
-
time.sleep(1.5 * attempt)
|
| 420 |
-
continue
|
| 421 |
except Exception as e:
|
| 422 |
-
logger.error(f"
|
| 423 |
time.sleep(1.5 * attempt)
|
| 424 |
-
continue
|
| 425 |
|
| 426 |
-
logger.error(f"❌ Storage worker gave up after {max_attempts} attempts for {username}. Entry NOT saved.")
|
| 427 |
-
|
| 428 |
-
|
| 429 |
-
pipeline = None
|
| 430 |
-
device = None
|
| 431 |
-
current_loaded_model = None
|
| 432 |
-
|
| 433 |
-
|
| 434 |
-
def get_infinite_embeddings(pipe, prompt, negative_prompt):
|
| 435 |
-
pos_ids_1 = pipe.tokenizer(prompt, return_tensors="pt", truncation=False).input_ids.to(device)
|
| 436 |
-
neg_ids_1 = pipe.tokenizer(negative_prompt, return_tensors="pt", truncation=False).input_ids.to(device)
|
| 437 |
-
pos_ids_2 = pipe.tokenizer_2(prompt, return_tensors="pt", truncation=False).input_ids.to(device)
|
| 438 |
-
neg_ids_2 = pipe.tokenizer_2(negative_prompt, return_tensors="pt", truncation=False).input_ids.to(device)
|
| 439 |
-
|
| 440 |
-
max_len = max(pos_ids_1.shape[1], neg_ids_1.shape[1], pos_ids_2.shape[1], neg_ids_2.shape[1])
|
| 441 |
-
num_chunks = (max_len - 2 + 74) // 75
|
| 442 |
-
if num_chunks == 0:
|
| 443 |
-
num_chunks = 1
|
| 444 |
-
|
| 445 |
-
def chunk_and_pad_tokens(token_ids, tokenizer, target_chunks):
|
| 446 |
-
raw_ids = token_ids[0, 1:-1].tolist()
|
| 447 |
-
chunks_list = []
|
| 448 |
-
for idx in range(target_chunks):
|
| 449 |
-
start_i = idx * 75
|
| 450 |
-
end_i = start_i + 75
|
| 451 |
-
chunk_slice = raw_ids[start_i:end_i]
|
| 452 |
-
formatted_chunk = [tokenizer.bos_token_id] + chunk_slice + [tokenizer.eos_token_id]
|
| 453 |
-
padding_needed = 77 - len(formatted_chunk)
|
| 454 |
-
if padding_needed > 0:
|
| 455 |
-
formatted_chunk += [tokenizer.pad_token_id] * padding_needed
|
| 456 |
-
chunks_list.append(formatted_chunk)
|
| 457 |
-
return torch.tensor(chunks_list, dtype=torch.long, device=device)
|
| 458 |
-
|
| 459 |
-
pos_pieces_1 = chunk_and_pad_tokens(pos_ids_1, pipe.tokenizer, num_chunks)
|
| 460 |
-
neg_pieces_1 = chunk_and_pad_tokens(neg_ids_1, pipe.tokenizer, num_chunks)
|
| 461 |
-
pos_pieces_2 = chunk_and_pad_tokens(pos_ids_2, pipe.tokenizer_2, num_chunks)
|
| 462 |
-
neg_pieces_2 = chunk_and_pad_tokens(neg_ids_2, pipe.tokenizer_2, num_chunks)
|
| 463 |
-
|
| 464 |
-
pos_embeds_1_list, neg_embeds_1_list = [], []
|
| 465 |
-
pos_embeds_2_list, neg_embeds_2_list = [], []
|
| 466 |
-
pooled_pos_list, pooled_neg_list = [], []
|
| 467 |
-
|
| 468 |
-
for i in range(num_chunks):
|
| 469 |
-
out_p1 = pipe.text_encoder(pos_pieces_1[i : i + 1], output_hidden_states=True)
|
| 470 |
-
out_n1 = pipe.text_encoder(neg_pieces_1[i : i + 1], output_hidden_states=True)
|
| 471 |
-
pos_embeds_1_list.append(out_p1.hidden_states[-2])
|
| 472 |
-
neg_embeds_1_list.append(out_n1.hidden_states[-2])
|
| 473 |
-
|
| 474 |
-
out_p2 = pipe.text_encoder_2(pos_pieces_2[i : i + 1], output_hidden_states=True)
|
| 475 |
-
out_n2 = pipe.text_encoder_2(neg_pieces_2[i : i + 1], output_hidden_states=True)
|
| 476 |
-
pos_embeds_2_list.append(out_p2.hidden_states[-2])
|
| 477 |
-
neg_embeds_2_list.append(out_n2.hidden_states[-2])
|
| 478 |
-
pooled_pos_list.append(out_p2.text_embeds)
|
| 479 |
-
pooled_neg_list.append(out_n2.text_embeds)
|
| 480 |
-
|
| 481 |
-
pos_embeds_1 = torch.cat(pos_embeds_1_list, dim=1)
|
| 482 |
-
neg_embeds_1 = torch.cat(neg_embeds_1_list, dim=1)
|
| 483 |
-
pos_embeds_2 = torch.cat(pos_embeds_2_list, dim=1)
|
| 484 |
-
neg_embeds_2 = torch.cat(neg_embeds_2_list, dim=1)
|
| 485 |
-
|
| 486 |
-
prompt_embeds = torch.cat([pos_embeds_1, pos_embeds_2], dim=-1)
|
| 487 |
-
negative_prompt_embeds = torch.cat([neg_embeds_1, neg_embeds_2], dim=-1)
|
| 488 |
-
return prompt_embeds, pooled_pos_list[0], negative_prompt_embeds, pooled_neg_list[0]
|
| 489 |
-
|
| 490 |
-
|
| 491 |
-
def initialize_model(model_name):
|
| 492 |
-
global pipeline, device, current_loaded_model
|
| 493 |
-
if current_loaded_model == model_name and pipeline is not None:
|
| 494 |
-
return True
|
| 495 |
-
try:
|
| 496 |
-
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
| 497 |
-
if pipeline is not None:
|
| 498 |
-
del pipeline
|
| 499 |
-
pipeline = None
|
| 500 |
-
gc.collect()
|
| 501 |
-
if torch.cuda.is_available():
|
| 502 |
-
torch.cuda.empty_cache()
|
| 503 |
-
|
| 504 |
-
dtype = torch.float16 if torch.cuda.is_available() else torch.float32
|
| 505 |
-
|
| 506 |
-
if model_name in DIFFUSERS_MODELS:
|
| 507 |
-
repo_id = DIFFUSERS_MODELS[model_name]
|
| 508 |
-
pipeline = StableDiffusionXLPipeline.from_pretrained(
|
| 509 |
-
repo_id,
|
| 510 |
-
torch_dtype=dtype,
|
| 511 |
-
use_safetensors=True,
|
| 512 |
-
)
|
| 513 |
-
else:
|
| 514 |
-
repo_id = MODEL_REPOS.get(model_name, HF_REPO_ID)
|
| 515 |
-
local_model_path = hf_hub_download(repo_id=repo_id, filename=model_name)
|
| 516 |
-
pipeline = StableDiffusionXLPipeline.from_single_file(
|
| 517 |
-
local_model_path,
|
| 518 |
-
torch_dtype=dtype,
|
| 519 |
-
use_safetensors=True,
|
| 520 |
-
safety_checker=None,
|
| 521 |
-
requires_safety_checker=False,
|
| 522 |
-
)
|
| 523 |
|
| 524 |
-
|
| 525 |
-
|
| 526 |
-
|
| 527 |
-
|
| 528 |
-
|
| 529 |
-
|
| 530 |
-
|
| 531 |
-
|
| 532 |
-
|
| 533 |
-
|
| 534 |
-
return
|
| 535 |
-
|
| 536 |
-
|
| 537 |
-
|
| 538 |
-
|
| 539 |
-
|
| 540 |
-
|
| 541 |
-
|
| 542 |
-
strength = max(0.05, min(0.95, float(strength)))
|
| 543 |
-
if not initialize_model(chef_name):
|
| 544 |
-
raise RuntimeError(f"Failed to load remix chef: {chef_name}")
|
| 545 |
-
|
| 546 |
-
img2img = StableDiffusionXLImg2ImgPipeline(
|
| 547 |
-
vae=pipeline.vae,
|
| 548 |
-
text_encoder=pipeline.text_encoder,
|
| 549 |
-
text_encoder_2=pipeline.text_encoder_2,
|
| 550 |
-
tokenizer=pipeline.tokenizer,
|
| 551 |
-
tokenizer_2=pipeline.tokenizer_2,
|
| 552 |
-
unet=pipeline.unet,
|
| 553 |
-
scheduler=pipeline.scheduler,
|
| 554 |
-
)
|
| 555 |
-
img2img = img2img.to(device)
|
| 556 |
-
pe, ppe, npe, nppe = get_infinite_embeddings(pipeline, enhanced_prompt, final_negative)
|
| 557 |
-
remix_steps = max(10, int(int(steps) * 0.7))
|
| 558 |
-
remixed = []
|
| 559 |
-
for img, current_seed in base_images:
|
| 560 |
-
generator = torch.Generator(device).manual_seed(int(current_seed) + int(seed_offset))
|
| 561 |
-
out = img2img(
|
| 562 |
-
image=img,
|
| 563 |
-
prompt_embeds=pe,
|
| 564 |
-
pooled_prompt_embeds=ppe,
|
| 565 |
-
negative_prompt_embeds=npe,
|
| 566 |
-
negative_pooled_prompt_embeds=nppe,
|
| 567 |
-
strength=strength,
|
| 568 |
-
num_inference_steps=remix_steps,
|
| 569 |
-
guidance_scale=float(cfg_scale),
|
| 570 |
-
generator=generator,
|
| 571 |
-
output_type="pil",
|
| 572 |
-
).images[0]
|
| 573 |
-
remixed.append((out, current_seed))
|
| 574 |
-
del img2img
|
| 575 |
gc.collect()
|
| 576 |
if torch.cuda.is_available():
|
| 577 |
torch.cuda.empty_cache()
|
| 578 |
-
return remixed
|
| 579 |
|
| 580 |
|
| 581 |
-
|
| 582 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 583 |
prompt,
|
| 584 |
styles,
|
| 585 |
-
|
|
|
|
| 586 |
steps,
|
| 587 |
-
|
| 588 |
seed,
|
| 589 |
-
num_images,
|
| 590 |
width,
|
| 591 |
height,
|
| 592 |
-
|
| 593 |
-
model_b,
|
| 594 |
-
blend_b,
|
| 595 |
-
model_c,
|
| 596 |
-
blend_c,
|
| 597 |
username,
|
| 598 |
):
|
| 599 |
-
if not prompt:
|
| 600 |
-
return None, "
|
| 601 |
|
| 602 |
now = time.time()
|
| 603 |
-
user_key = username
|
| 604 |
-
if user_key in LAST_GEN_TIME:
|
| 605 |
-
|
| 606 |
-
|
| 607 |
-
|
| 608 |
-
|
| 609 |
-
|
| 610 |
-
|
| 611 |
-
|
| 612 |
-
|
| 613 |
-
|
| 614 |
-
|
| 615 |
-
|
| 616 |
-
|
| 617 |
-
|
| 618 |
-
|
| 619 |
-
|
| 620 |
-
|
| 621 |
-
|
| 622 |
-
|
| 623 |
-
|
| 624 |
-
|
| 625 |
-
|
|
|
|
| 626 |
|
| 627 |
try:
|
| 628 |
-
|
| 629 |
-
|
| 630 |
-
|
| 631 |
-
|
| 632 |
-
|
| 633 |
-
|
| 634 |
-
|
| 635 |
-
|
| 636 |
-
|
| 637 |
-
|
| 638 |
-
|
| 639 |
-
|
| 640 |
-
|
| 641 |
-
|
| 642 |
-
|
| 643 |
-
|
| 644 |
-
|
| 645 |
-
|
| 646 |
-
|
| 647 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 648 |
|
| 649 |
-
|
| 650 |
-
|
| 651 |
-
|
| 652 |
-
|
| 653 |
-
|
| 654 |
-
|
| 655 |
-
|
| 656 |
-
|
| 657 |
-
|
| 658 |
-
|
| 659 |
-
|
| 660 |
-
|
| 661 |
-
|
| 662 |
-
|
| 663 |
-
|
| 664 |
-
|
| 665 |
-
|
| 666 |
-
|
| 667 |
-
|
| 668 |
-
|
| 669 |
-
base_images, model_b, blend_b, enhanced_prompt, final_negative, steps, cfg_scale, seed_offset=17
|
| 670 |
-
)
|
| 671 |
-
chain.append(f"B:{model_b}@{float(blend_b):.2f}")
|
| 672 |
-
if use_c:
|
| 673 |
-
# if C equals current loaded B, still re-run remix with new strength
|
| 674 |
-
base_images = remix_pass(
|
| 675 |
-
base_images, model_c, blend_c, enhanced_prompt, final_negative, steps, cfg_scale, seed_offset=31
|
| 676 |
-
)
|
| 677 |
-
chain.append(f"C:{model_c}@{float(blend_c):.2f}")
|
| 678 |
-
|
| 679 |
-
gallery_items = []
|
| 680 |
-
upload_jobs = []
|
| 681 |
-
log_model = " > ".join(chain)
|
| 682 |
-
for out, current_seed in base_images:
|
| 683 |
-
current_time = datetime.now().strftime("%I:%M:%S %p")
|
| 684 |
-
display_label = f"✨ Seed: {current_seed} | {log_model} | ⏱️ {current_time}"
|
| 685 |
-
gallery_items.append((out, display_label))
|
| 686 |
-
upload_jobs.append((out, current_seed, log_model, prompt, username, width, height, steps, cfg_scale))
|
| 687 |
-
|
| 688 |
-
style_note = ", ".join(styles) if isinstance(styles, (list, tuple)) else str(styles)
|
| 689 |
-
gen_info = f"Seeds: {seeds} | {width}x{height} | {log_model} | styles: {style_note}"
|
| 690 |
-
return gallery_items, gen_info, enhanced_prompt, upload_jobs
|
| 691 |
-
except Exception as e:
|
| 692 |
-
logger.error(f"❌ GENERATION CRASH: {str(e)}")
|
| 693 |
-
return None, "", f"❌ Crashed: {str(e)}", []
|
| 694 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 695 |
|
| 696 |
-
js_theme_injector = """
|
| 697 |
-
function() {
|
| 698 |
-
const formatDark = () => {
|
| 699 |
-
document.documentElement.classList.add('dark');
|
| 700 |
-
document.body.classList.add('dark');
|
| 701 |
-
const root = document.querySelector(':root');
|
| 702 |
-
if(root) {
|
| 703 |
-
root.style.setProperty('--background-fill-primary', '#0b0f19');
|
| 704 |
-
root.style.setProperty('--background-fill-secondary', '#111827');
|
| 705 |
-
root.style.setProperty('--block-background-fill', '#111827');
|
| 706 |
-
root.style.setProperty('--body-text-color', '#f3f4f6');
|
| 707 |
-
root.style.setProperty('--border-color-primary', '#1f2937');
|
| 708 |
-
}
|
| 709 |
-
};
|
| 710 |
-
formatDark();
|
| 711 |
-
setTimeout(formatDark, 500);
|
| 712 |
-
setTimeout(formatDark, 2000);
|
| 713 |
-
}
|
| 714 |
-
"""
|
| 715 |
|
| 716 |
-
|
|
|
|
|
|
|
|
|
|
| 717 |
function() {
|
| 718 |
-
|
| 719 |
-
|
| 720 |
-
|
| 721 |
-
|
| 722 |
-
|
| 723 |
-
|
| 724 |
-
return visitor_id;
|
| 725 |
}
|
| 726 |
"""
|
| 727 |
|
| 728 |
css = """
|
| 729 |
-
.gradio-container, html, body
|
| 730 |
-
.
|
| 731 |
-
span, label, p, h1, h2, h3, .gr-label, .meta-text { color: #f3f4f6 !important; font-weight: 600 !important; }
|
| 732 |
-
input, textarea, select { background-color: #1f2937 !important; color: #ffffff !important; border: 1px solid #374151 !important; border-radius: 6px !important; padding: 8px !important; }
|
| 733 |
-
div[role="listbox"], .dropdown-menu, .v-select, select { background-color: #1f2937 !important; background: #1f2937 !important; border: 1px solid #4b5563 !important; color: #ffffff !important; z-index: 99999 !important; }
|
| 734 |
-
option, .dark option { background-color: #1f2937 !important; color: #ffffff !important; }
|
| 735 |
-
.gradio-gallery, .image-output, .image-container {
|
| 736 |
-
max-height: 70vh !important;
|
| 737 |
-
overflow-y: auto !important;
|
| 738 |
-
}
|
| 739 |
"""
|
| 740 |
|
| 741 |
THEME = gr.themes.Default(primary_hue="purple", secondary_hue="indigo", neutral_hue="slate")
|
| 742 |
|
| 743 |
|
| 744 |
-
def
|
| 745 |
-
with gr.Blocks() as
|
| 746 |
-
|
| 747 |
-
|
| 748 |
-
gr.Markdown("# 🔥 MEN ONLY — Mad Mode")
|
| 749 |
gr.Markdown(
|
| 750 |
-
"###
|
| 751 |
-
"
|
|
|
|
| 752 |
)
|
| 753 |
|
| 754 |
with gr.Row():
|
| 755 |
with gr.Column(scale=2):
|
| 756 |
-
|
| 757 |
-
|
| 758 |
-
|
| 759 |
-
|
|
|
|
|
|
|
| 760 |
)
|
| 761 |
-
|
| 762 |
-
label="Negatives (stuff to kill)",
|
| 763 |
-
value=BASE_NEGATIVES,
|
| 764 |
-
lines=5,
|
| 765 |
-
)
|
| 766 |
-
style_input = gr.CheckboxGroup(
|
| 767 |
-
label="Style packs (stack as many as you want)",
|
| 768 |
-
choices=STYLE_CHOICES,
|
| 769 |
-
value=["Men Only", "Art Nude Meta", "Rembrandt Oil"],
|
| 770 |
-
)
|
| 771 |
-
generate_button = gr.Button("GENERATE — HIT IT", variant="primary", size="lg")
|
| 772 |
-
|
| 773 |
with gr.Column(scale=1):
|
| 774 |
-
|
| 775 |
-
|
| 776 |
-
|
| 777 |
-
|
| 778 |
-
|
| 779 |
-
|
| 780 |
-
|
| 781 |
-
|
| 782 |
-
|
| 783 |
-
|
| 784 |
-
|
| 785 |
-
|
| 786 |
-
|
| 787 |
-
|
| 788 |
-
|
| 789 |
-
|
| 790 |
-
|
| 791 |
-
|
| 792 |
-
|
| 793 |
-
|
| 794 |
-
|
| 795 |
-
|
| 796 |
-
|
| 797 |
-
|
| 798 |
-
if images is
|
| 799 |
-
return
|
| 800 |
-
return
|
| 801 |
-
|
| 802 |
-
|
| 803 |
-
fn=
|
| 804 |
-
inputs=[
|
| 805 |
-
|
| 806 |
-
style_input,
|
| 807 |
-
negative_prompt_input,
|
| 808 |
-
steps_input,
|
| 809 |
-
cfg_input,
|
| 810 |
-
seed_input,
|
| 811 |
-
num_images_input,
|
| 812 |
-
width_input,
|
| 813 |
-
height_input,
|
| 814 |
-
model_a_input,
|
| 815 |
-
model_b_input,
|
| 816 |
-
blend_b_input,
|
| 817 |
-
model_c_input,
|
| 818 |
-
blend_c_input,
|
| 819 |
-
user_id_store,
|
| 820 |
-
],
|
| 821 |
-
outputs=[image_output, generation_info, metadata_display, generation_info, metadata_display],
|
| 822 |
)
|
| 823 |
-
return
|
| 824 |
|
| 825 |
|
| 826 |
if __name__ == "__main__":
|
| 827 |
-
os.environ["GRADIO_WATCH_DIRS"] = ""
|
| 828 |
os.environ["GRADIO_ANALYTICS_ENABLED"] = "False"
|
| 829 |
-
app =
|
| 830 |
-
app.queue(max_size=
|
| 831 |
-
app.launch(
|
| 832 |
-
ssr_mode=False,
|
| 833 |
-
theme=THEME,
|
| 834 |
-
css=css,
|
| 835 |
-
js=js_theme_injector,
|
| 836 |
-
)
|
|
|
|
| 11 |
import os
|
| 12 |
import re
|
| 13 |
import random
|
|
|
|
| 14 |
import gc
|
| 15 |
import logging
|
| 16 |
import time
|
| 17 |
import io
|
| 18 |
import threading
|
|
|
|
|
|
|
| 19 |
from datetime import datetime
|
| 20 |
+
|
| 21 |
+
import torch
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
from PIL import Image
|
| 23 |
import gradio as gr
|
| 24 |
+
from huggingface_hub import hf_hub_download, HfApi, CommitOperationAdd
|
| 25 |
+
from huggingface_hub.utils import HfHubHTTPError
|
| 26 |
|
| 27 |
+
# ===== LOGGING =====
|
| 28 |
logging.basicConfig(
|
| 29 |
level=logging.INFO,
|
| 30 |
format="%(asctime)s - %(levelname)s - %(message)s",
|
| 31 |
handlers=[logging.StreamHandler(sys.stdout)],
|
| 32 |
)
|
| 33 |
+
logger = logging.getLogger("NextGenEngine")
|
| 34 |
+
logger.info("🚀 Next-gen multi-engine Space boot (FLUX / SD3.5 / Adult SDXL)")
|
| 35 |
|
| 36 |
if hasattr(sys.stdout, "reconfigure"):
|
| 37 |
sys.stdout.reconfigure(encoding="utf-8")
|
| 38 |
if hasattr(sys.stderr, "reconfigure"):
|
| 39 |
sys.stderr.reconfigure(encoding="utf-8")
|
| 40 |
|
| 41 |
+
BACKUP_DATASET_ID = "JustinD/generation-images"
|
| 42 |
+
LAST_GEN_TIME = {}
|
| 43 |
+
COOLDOWN_SECONDS = 8
|
| 44 |
|
| 45 |
+
# ---------------------------------------------------------------------------
|
| 46 |
+
# ENGINES
|
| 47 |
+
# ---------------------------------------------------------------------------
|
| 48 |
+
ENGINE_FLUX = "FLUX.1 Schnell (latest fast)"
|
| 49 |
+
ENGINE_SD35 = "SD 3.5 Medium (latest quality)"
|
| 50 |
+
ENGINE_SDXL = "Adult SDXL (NSFW chefs)"
|
| 51 |
+
|
| 52 |
+
ENGINE_CHOICES = [ENGINE_FLUX, ENGINE_SD35, ENGINE_SDXL]
|
| 53 |
+
|
| 54 |
+
# Adult SDXL single-file + Diffusers (proven Justin lane)
|
| 55 |
+
HF_REPO_ID = "AriaVale2/my-safetensors-backup"
|
| 56 |
MODEL_REPOS = {
|
| 57 |
+
"pornworksSexyBeauty_v04.safetensors": "AI-Porn/PornWorks-Sexy-Beauty-Aesthetic-Erotic-NSFW-SDXL-Chekpoint",
|
| 58 |
+
"pornworksRealPornPhoto_v04.safetensors": "Manjushri/pornworks-real-porn-photo-realistic-nsfw-sdxl-and-pony-chekpoint",
|
| 59 |
+
"pornworksHardcoreFantasy_v04.safetensors": "AI-Porn/PornWorks-Hardcore-Fantasy-NSFW-3D-SDXL-Chekpoint",
|
| 60 |
+
"CyberRealisticXLPlay_V10.0_FP16.safetensors": "cyberdelia/CyberRealisticXL",
|
| 61 |
"Juggernaut-XL_v9_RunDiffusionPhoto_v2.safetensors": "saber-end/Juggernaut-XL-v9",
|
| 62 |
"RealVisXL_V4.0.safetensors": "SG161222/RealVisXL_V4.0",
|
|
|
|
|
|
|
|
|
|
| 63 |
"Pony Diffusion V6 XL.safetensors": "AriaVale2/my-safetensors-backup",
|
| 64 |
"Hassaku XL (Illustrious).safetensors": "AriaVale2/my-safetensors-backup",
|
| 65 |
+
"animagine-xl-4.0.safetensors": "cagliostrolab/animagine-xl-4.0",
|
| 66 |
+
"OnomaAIResearch_Illustrious-XL-v1.0.safetensors": "AriaVale2/my-safetensors-backup",
|
|
|
|
| 67 |
"UnfilteredAI_NSFW-GEN-ANIME-v2.safetensors": "AriaVale2/my-safetensors-backup",
|
| 68 |
"Mala Anime Mix NSFW PonyXL.safetensors": "AriaVale2/my-safetensors-backup",
|
| 69 |
"PornWorks_Anime_Desire_NSFW_Anime_Hentai_SDXL_Pony_Chekpoint.safetensors": "AriaVale2/my-safetensors-backup",
|
| 70 |
}
|
| 71 |
|
| 72 |
DIFFUSERS_MODELS = {
|
|
|
|
| 73 |
"OhMySister Beautiful Asian Woman": "John6666/oms-oh-my-sister-beautiful-asian-woman-v10-sdxl",
|
| 74 |
+
"OhMyDaughter Beautiful Woman (Pony)": "John6666/omd-ohmaydaughter-beautiful-woman-pony-v10-sdxl",
|
| 75 |
"Fucktastic Anime Pony NSFW": "John6666/fucktastic-anime-checkpoint-pony-pdxl-porn-nsfw-sfw-22-sdxl",
|
| 76 |
"Mala Anime Mix NSFW Pony v7": "John6666/mala-anime-mix-nsfw-pony-xl-v7-sdxl",
|
| 77 |
"YAAI WAI NSFW Illustrious": "John6666/yaai-yet-another-anime-illustrious-model-full-fine-tune-based-on-wai-nsfw-v20-sdxl",
|
| 78 |
"Flare Diffusion NSFW Anime": "John6666/flare-diffusion-nsfw-v16-semi-realism-anime-sdxl",
|
|
|
|
| 79 |
"PornWorks Anime Desire (Diffusers)": "John6666/pornworks-anime-desire-nsfw-pony-sdxl",
|
|
|
|
|
|
|
|
|
|
|
|
|
| 80 |
}
|
| 81 |
|
| 82 |
+
SDXL_CHOICES = list(DIFFUSERS_MODELS.keys()) + list(MODEL_REPOS.keys())
|
|
|
|
|
|
|
|
|
|
| 83 |
|
| 84 |
+
STYLE_PACKS = {
|
|
|
|
| 85 |
"Men Only": {
|
| 86 |
+
"prefix": "for men's eyes only, hyper sexual pinup, hungry male gaze",
|
| 87 |
+
"suffix": "deep cleavage, arched back, bedroom eyes, wet parted lips",
|
| 88 |
},
|
| 89 |
+
"Art Nude": {
|
| 90 |
+
"prefix": "classical art nude, museum erotic nude",
|
| 91 |
+
"suffix": "bare breasts, soft weight, masterpiece lighting",
|
| 92 |
},
|
| 93 |
+
"Cartoon Slut": {
|
| 94 |
+
"prefix": "score_9, score_8_up, source_anime, rating_explicit, anime cartoon cel shading",
|
| 95 |
+
"suffix": "slutty pinup, exaggerated hourglass, heavy cleavage",
|
| 96 |
},
|
| 97 |
"Rembrandt Oil": {
|
| 98 |
+
"prefix": "Rembrandt oil painting, warm side light, visible brushstrokes",
|
| 99 |
+
"suffix": "classical academic nude energy",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 100 |
},
|
| 101 |
"4K TV Wide": {
|
| 102 |
+
"prefix": "16:9 ultra wide cinematic framing for 4K TV",
|
| 103 |
+
"suffix": "razor sharp when blown up large",
|
|
|
|
|
|
|
|
|
|
|
|
|
| 104 |
},
|
| 105 |
+
"Photo Gloss": {
|
| 106 |
+
"prefix": "photorealistic, wet shiny skin, editorial flash",
|
| 107 |
+
"suffix": "ultra detailed skin pores and fabric",
|
| 108 |
},
|
| 109 |
}
|
| 110 |
|
| 111 |
+
DEFAULT_QUALITY = (
|
| 112 |
+
"masterpiece, best quality, detailed face, "
|
| 113 |
+
"adult Korean woman late 20s, beautiful Korean face, "
|
| 114 |
+
"large breasts, full breasts, heavy breasts, cleavage, "
|
| 115 |
+
"tiny waist, wide hips, hourglass figure"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 116 |
)
|
| 117 |
|
| 118 |
BASE_NEGATIVES = (
|
| 119 |
+
"child, loli, underage, teen, young, "
|
| 120 |
+
"small breasts, flat chest, bad hands, extra fingers, deformed, "
|
| 121 |
+
"blurry, lowres, watermark, text, censored, ugly"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 122 |
)
|
| 123 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 124 |
RANDOM_PROMPTS = [
|
| 125 |
+
"adult Korean woman late 20s, beautiful Korean face, bedroom eyes, parted glossy lips, long wavy black hair, extreme hourglass, large breasts, full breasts, heavy breasts, bare breasts, soft weight, classical art nude, warm Rembrandt light",
|
| 126 |
+
"adult Korean woman late 20s, tight black crop top no bra, tiny skirt, deep cleavage, wet shiny skin, luxury condo night, for men's eyes only",
|
| 127 |
+
"adult Korean woman late 20s, anime cartoon slut pinup, huge breasts, heavy cleavage, sheer lingerie, looking at viewer, busy bedroom",
|
| 128 |
]
|
| 129 |
|
| 130 |
+
# ---------------------------------------------------------------------------
|
| 131 |
+
# SAFETY
|
| 132 |
+
# ---------------------------------------------------------------------------
|
| 133 |
FORBIDDEN_KEYWORDS = []
|
| 134 |
if os.path.exists("restricted_words.txt"):
|
| 135 |
try:
|
|
|
|
| 140 |
if line.strip() and not line.strip().startswith("#")
|
| 141 |
]
|
| 142 |
except Exception as e:
|
| 143 |
+
logger.error(f"restricted words load failed: {e}")
|
| 144 |
|
| 145 |
AGE_PATTERN = re.compile(
|
| 146 |
r"\b(one|two|three|four|five|six|seven|eight|nine|ten|"
|
| 147 |
+
r"eleven|twelve|thirteen|fourteen|fifteen|sixteen|seventeen|"
|
| 148 |
+
r"1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17)"
|
| 149 |
r"\s*[-\s]?\s*(year|yr|y\/o|yo)\b"
|
| 150 |
)
|
| 151 |
|
| 152 |
|
| 153 |
+
def check_prompt_safety(prompt: str):
|
| 154 |
clean = prompt.lower().replace("-", " ").replace("_", " ")
|
| 155 |
for word in FORBIDDEN_KEYWORDS:
|
| 156 |
if word in clean:
|
|
|
|
| 161 |
return True, None
|
| 162 |
|
| 163 |
|
| 164 |
+
def build_styles(selected):
|
| 165 |
+
if not selected:
|
|
|
|
| 166 |
return "", ""
|
| 167 |
+
if isinstance(selected, str):
|
| 168 |
+
selected = [selected]
|
| 169 |
prefixes, suffixes = [], []
|
| 170 |
+
for name in selected:
|
| 171 |
+
cfg = STYLE_PACKS.get(name)
|
| 172 |
if not cfg:
|
| 173 |
continue
|
| 174 |
if cfg.get("prefix"):
|
|
|
|
| 178 |
return ", ".join(prefixes), ", ".join(suffixes)
|
| 179 |
|
| 180 |
|
| 181 |
+
# ---------------------------------------------------------------------------
|
| 182 |
+
# LOGGING UPLOAD (same pantry)
|
| 183 |
+
# ---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 184 |
|
| 185 |
+
def async_log_upload(pil_img, seed, model_name, prompt, username, width, height, steps, cfg_scale):
|
| 186 |
hf_token = os.environ.get("HF_TOKEN")
|
| 187 |
+
if not username or not str(username).strip():
|
| 188 |
username = "Unknown_Visitor"
|
|
|
|
| 189 |
timestamp = int(time.time())
|
| 190 |
+
clean = str(model_name).replace("/", "_")[:18]
|
| 191 |
+
filename_base = f"gen_{timestamp}_seed_{seed}_{clean}"
|
| 192 |
+
buf = io.BytesIO()
|
| 193 |
+
pil_img.save(buf, format="WEBP", quality=95)
|
| 194 |
+
raw = buf.getvalue()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 195 |
if not hf_token:
|
| 196 |
+
logger.error("HF_TOKEN missing — log upload skipped")
|
|
|
|
|
|
|
| 197 |
return
|
|
|
|
| 198 |
api = HfApi(token=hf_token)
|
| 199 |
date_str = time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(timestamp))
|
| 200 |
log_entry = f"[{date_str}] | User: {username} | Model: {model_name} | Seed: {seed} | Prompt: {prompt[:150]}...\n"
|
| 201 |
+
user_log = f"user_logs/{username}_history.txt"
|
| 202 |
+
for attempt in range(1, 4):
|
|
|
|
|
|
|
| 203 |
try:
|
| 204 |
try:
|
| 205 |
+
path = hf_hub_download(BACKUP_DATASET_ID, "traffic_logs.txt", repo_type="dataset", token=hf_token)
|
| 206 |
+
current = open(path, encoding="utf-8").read()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 207 |
except Exception:
|
| 208 |
+
current = "=== TRAFFIC LOGS ===\n"
|
|
|
|
| 209 |
try:
|
| 210 |
+
upath = hf_hub_download(BACKUP_DATASET_ID, user_log, repo_type="dataset", token=hf_token)
|
| 211 |
+
user_logs = open(upath, encoding="utf-8").read()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 212 |
except Exception:
|
| 213 |
user_logs = f"=== HISTORY FOR {username} ===\n"
|
| 214 |
+
ops = [
|
| 215 |
+
CommitOperationAdd(path_in_repo="traffic_logs.txt", path_or_fileobj=(current + log_entry).encode()),
|
| 216 |
+
CommitOperationAdd(path_in_repo=user_log, path_or_fileobj=(user_logs + log_entry).encode()),
|
| 217 |
+
CommitOperationAdd(path_in_repo=f"data/{filename_base}.webp", path_or_fileobj=raw),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 218 |
]
|
|
|
|
| 219 |
api.create_commit(
|
| 220 |
repo_id=BACKUP_DATASET_ID,
|
| 221 |
repo_type="dataset",
|
| 222 |
+
operations=ops,
|
| 223 |
commit_message=f"gen for {username} @ {timestamp}",
|
| 224 |
)
|
| 225 |
+
logger.info(f"☁️ logged {username} attempt {attempt}")
|
| 226 |
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 227 |
except Exception as e:
|
| 228 |
+
logger.error(f"log fail {attempt}: {e}")
|
| 229 |
time.sleep(1.5 * attempt)
|
|
|
|
| 230 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 231 |
|
| 232 |
+
# ---------------------------------------------------------------------------
|
| 233 |
+
# PIPELINE CACHE
|
| 234 |
+
# ---------------------------------------------------------------------------
|
| 235 |
+
_pipes = {}
|
| 236 |
+
_current_key = None
|
| 237 |
+
|
| 238 |
+
|
| 239 |
+
def _dtype():
|
| 240 |
+
if torch.cuda.is_available():
|
| 241 |
+
# Flux prefers bfloat16 when available
|
| 242 |
+
return torch.bfloat16 if torch.cuda.is_bf16_supported() else torch.float16
|
| 243 |
+
return torch.float32
|
| 244 |
+
|
| 245 |
+
|
| 246 |
+
def unload_all():
|
| 247 |
+
global _pipes, _current_key
|
| 248 |
+
_pipes.clear()
|
| 249 |
+
_current_key = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 250 |
gc.collect()
|
| 251 |
if torch.cuda.is_available():
|
| 252 |
torch.cuda.empty_cache()
|
|
|
|
| 253 |
|
| 254 |
|
| 255 |
+
def get_flux_pipe():
|
| 256 |
+
key = "flux-schnell"
|
| 257 |
+
if key in _pipes:
|
| 258 |
+
return _pipes[key]
|
| 259 |
+
from diffusers import FluxPipeline
|
| 260 |
+
|
| 261 |
+
unload_all()
|
| 262 |
+
logger.info("Loading FLUX.1 Schnell…")
|
| 263 |
+
pipe = FluxPipeline.from_pretrained(
|
| 264 |
+
"black-forest-labs/FLUX.1-schnell",
|
| 265 |
+
torch_dtype=_dtype(),
|
| 266 |
+
)
|
| 267 |
+
if torch.cuda.is_available():
|
| 268 |
+
pipe.to("cuda")
|
| 269 |
+
_pipes[key] = pipe
|
| 270 |
+
return pipe
|
| 271 |
+
|
| 272 |
+
|
| 273 |
+
def get_sd35_pipe():
|
| 274 |
+
key = "sd35-medium"
|
| 275 |
+
if key in _pipes:
|
| 276 |
+
return _pipes[key]
|
| 277 |
+
from diffusers import StableDiffusion3Pipeline
|
| 278 |
+
|
| 279 |
+
unload_all()
|
| 280 |
+
logger.info("Loading SD 3.5 Medium…")
|
| 281 |
+
# May require accepting the model license on HF while logged in (Space has hf_oauth)
|
| 282 |
+
pipe = StableDiffusion3Pipeline.from_pretrained(
|
| 283 |
+
"stabilityai/stable-diffusion-3.5-medium",
|
| 284 |
+
torch_dtype=_dtype(),
|
| 285 |
+
)
|
| 286 |
+
if torch.cuda.is_available():
|
| 287 |
+
pipe.to("cuda")
|
| 288 |
+
_pipes[key] = pipe
|
| 289 |
+
return pipe
|
| 290 |
+
|
| 291 |
+
|
| 292 |
+
def get_sdxl_pipe(model_name: str):
|
| 293 |
+
key = f"sdxl:{model_name}"
|
| 294 |
+
if key in _pipes:
|
| 295 |
+
return _pipes[key]
|
| 296 |
+
from diffusers import StableDiffusionXLPipeline, EulerDiscreteScheduler
|
| 297 |
+
|
| 298 |
+
# Keep only one heavy pipe resident
|
| 299 |
+
unload_all()
|
| 300 |
+
dtype = torch.float16 if torch.cuda.is_available() else torch.float32
|
| 301 |
+
logger.info(f"Loading Adult SDXL chef: {model_name}")
|
| 302 |
+
if model_name in DIFFUSERS_MODELS:
|
| 303 |
+
pipe = StableDiffusionXLPipeline.from_pretrained(
|
| 304 |
+
DIFFUSERS_MODELS[model_name],
|
| 305 |
+
torch_dtype=dtype,
|
| 306 |
+
use_safetensors=True,
|
| 307 |
+
)
|
| 308 |
+
else:
|
| 309 |
+
repo = MODEL_REPOS.get(model_name, HF_REPO_ID)
|
| 310 |
+
path = hf_hub_download(repo_id=repo, filename=model_name)
|
| 311 |
+
pipe = StableDiffusionXLPipeline.from_single_file(
|
| 312 |
+
path,
|
| 313 |
+
torch_dtype=dtype,
|
| 314 |
+
use_safetensors=True,
|
| 315 |
+
)
|
| 316 |
+
if hasattr(pipe, "scheduler"):
|
| 317 |
+
pipe.scheduler = EulerDiscreteScheduler.from_config(
|
| 318 |
+
pipe.scheduler.config,
|
| 319 |
+
timestep_spacing="trailing",
|
| 320 |
+
)
|
| 321 |
+
if torch.cuda.is_available():
|
| 322 |
+
pipe.to("cuda")
|
| 323 |
+
if hasattr(pipe, "vae"):
|
| 324 |
+
pipe.vae.enable_slicing()
|
| 325 |
+
pipe.vae.enable_tiling()
|
| 326 |
+
_pipes[key] = pipe
|
| 327 |
+
return pipe
|
| 328 |
+
|
| 329 |
+
|
| 330 |
+
def enhance_prompt(prompt, styles):
|
| 331 |
+
pre, suf = build_styles(styles)
|
| 332 |
+
parts = [DEFAULT_QUALITY, pre, prompt.strip(), suf]
|
| 333 |
+
return ", ".join([p for p in parts if p])
|
| 334 |
+
|
| 335 |
+
|
| 336 |
+
# ---------------------------------------------------------------------------
|
| 337 |
+
# GENERATE
|
| 338 |
+
# ---------------------------------------------------------------------------
|
| 339 |
+
@spaces.GPU(duration=180) if SPACES_AVAILABLE else (lambda f: f)
|
| 340 |
+
def generate(
|
| 341 |
+
engine,
|
| 342 |
prompt,
|
| 343 |
styles,
|
| 344 |
+
negative,
|
| 345 |
+
sdxl_model,
|
| 346 |
steps,
|
| 347 |
+
cfg,
|
| 348 |
seed,
|
|
|
|
| 349 |
width,
|
| 350 |
height,
|
| 351 |
+
batch,
|
|
|
|
|
|
|
|
|
|
|
|
|
| 352 |
username,
|
| 353 |
):
|
| 354 |
+
if not prompt or not str(prompt).strip():
|
| 355 |
+
return None, "❌ Prompt missing", "", []
|
| 356 |
|
| 357 |
now = time.time()
|
| 358 |
+
user_key = username or "anonymous"
|
| 359 |
+
if user_key in LAST_GEN_TIME and now - LAST_GEN_TIME[user_key] < COOLDOWN_SECONDS:
|
| 360 |
+
left = int(COOLDOWN_SECONDS - (now - LAST_GEN_TIME[user_key]))
|
| 361 |
+
gr.Warning(f"Cooldown {left}s")
|
| 362 |
+
return None, f"⏳ Cooldown {left}s", "", []
|
| 363 |
+
|
| 364 |
+
ok, bad = check_prompt_safety(prompt)
|
| 365 |
+
if not ok:
|
| 366 |
+
gr.Warning("Restricted content")
|
| 367 |
+
return None, "❌ Restricted content", "", []
|
| 368 |
+
|
| 369 |
+
batch = int(max(1, min(3, int(batch))))
|
| 370 |
+
seeds = [
|
| 371 |
+
random.randint(0, 2**31 - 1) if int(seed) < 0 else int(seed) + i
|
| 372 |
+
for i in range(batch)
|
| 373 |
+
]
|
| 374 |
+
enhanced = enhance_prompt(prompt, styles)
|
| 375 |
+
neg = BASE_NEGATIVES
|
| 376 |
+
if negative and str(negative).strip():
|
| 377 |
+
neg = f"{BASE_NEGATIVES}, {negative.strip()}"
|
| 378 |
+
|
| 379 |
+
LAST_GEN_TIME[user_key] = now
|
| 380 |
+
gallery = []
|
| 381 |
+
jobs = []
|
| 382 |
|
| 383 |
try:
|
| 384 |
+
device = "cuda" if torch.cuda.is_available() else "cpu"
|
| 385 |
+
|
| 386 |
+
if engine == ENGINE_FLUX:
|
| 387 |
+
pipe = get_flux_pipe()
|
| 388 |
+
# Schnell: low steps, guidance often ~0
|
| 389 |
+
use_steps = int(max(1, min(8, int(steps) if int(steps) <= 8 else 4)))
|
| 390 |
+
for s in seeds:
|
| 391 |
+
gen = torch.Generator(device=device).manual_seed(int(s))
|
| 392 |
+
out = pipe(
|
| 393 |
+
prompt=enhanced,
|
| 394 |
+
num_inference_steps=use_steps,
|
| 395 |
+
guidance_scale=0.0,
|
| 396 |
+
width=int(width),
|
| 397 |
+
height=int(height),
|
| 398 |
+
generator=gen,
|
| 399 |
+
max_sequence_length=256,
|
| 400 |
+
).images[0]
|
| 401 |
+
label = f"FLUX · seed {s} · {datetime.now().strftime('%I:%M:%S %p')}"
|
| 402 |
+
gallery.append((out, label))
|
| 403 |
+
jobs.append((out, s, "FLUX.1-schnell", prompt, username, width, height, use_steps, 0.0))
|
| 404 |
+
|
| 405 |
+
elif engine == ENGINE_SD35:
|
| 406 |
+
pipe = get_sd35_pipe()
|
| 407 |
+
use_steps = int(max(15, min(40, int(steps))))
|
| 408 |
+
for s in seeds:
|
| 409 |
+
gen = torch.Generator(device=device).manual_seed(int(s))
|
| 410 |
+
out = pipe(
|
| 411 |
+
prompt=enhanced,
|
| 412 |
+
negative_prompt=neg,
|
| 413 |
+
num_inference_steps=use_steps,
|
| 414 |
+
guidance_scale=float(cfg),
|
| 415 |
+
width=int(width),
|
| 416 |
+
height=int(height),
|
| 417 |
+
generator=gen,
|
| 418 |
+
).images[0]
|
| 419 |
+
label = f"SD3.5 · seed {s} · {datetime.now().strftime('%I:%M:%S %p')}"
|
| 420 |
+
gallery.append((out, label))
|
| 421 |
+
jobs.append((out, s, "SD3.5-medium", prompt, username, width, height, use_steps, cfg))
|
| 422 |
|
| 423 |
+
else:
|
| 424 |
+
pipe = get_sdxl_pipe(sdxl_model)
|
| 425 |
+
use_steps = int(max(10, min(40, int(steps))))
|
| 426 |
+
for s in seeds:
|
| 427 |
+
gen = torch.Generator(device=device).manual_seed(int(s))
|
| 428 |
+
out = pipe(
|
| 429 |
+
prompt=enhanced,
|
| 430 |
+
negative_prompt=neg,
|
| 431 |
+
num_inference_steps=use_steps,
|
| 432 |
+
guidance_scale=float(cfg),
|
| 433 |
+
width=int(width),
|
| 434 |
+
height=int(height),
|
| 435 |
+
generator=gen,
|
| 436 |
+
).images[0]
|
| 437 |
+
label = f"SDXL · {sdxl_model[:28]} · seed {s}"
|
| 438 |
+
gallery.append((out, label))
|
| 439 |
+
jobs.append((out, s, sdxl_model, prompt, username, width, height, use_steps, cfg))
|
| 440 |
+
|
| 441 |
+
info = f"{engine} | seeds {seeds} | {width}x{height} | enhanced prompt ready"
|
| 442 |
+
return gallery, info, enhanced, jobs
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 443 |
|
| 444 |
+
except Exception as e:
|
| 445 |
+
logger.exception("generate failed")
|
| 446 |
+
msg = str(e)
|
| 447 |
+
if "gated" in msg.lower() or "401" in msg or "403" in msg:
|
| 448 |
+
msg += " — open the model page on HF, accept the license, ensure Space secret HF_TOKEN can read it (or use HF login)."
|
| 449 |
+
return None, f"❌ {msg}", "", []
|
| 450 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 451 |
|
| 452 |
+
# ---------------------------------------------------------------------------
|
| 453 |
+
# UI
|
| 454 |
+
# ---------------------------------------------------------------------------
|
| 455 |
+
js_visitor = """
|
| 456 |
function() {
|
| 457 |
+
let id = localStorage.getItem('adult_ai_visitor_id');
|
| 458 |
+
if (!id) {
|
| 459 |
+
id = 'Visitor_' + Math.floor(1000 + Math.random() * 9000);
|
| 460 |
+
localStorage.setItem('adult_ai_visitor_id', id);
|
| 461 |
+
}
|
| 462 |
+
return id;
|
|
|
|
| 463 |
}
|
| 464 |
"""
|
| 465 |
|
| 466 |
css = """
|
| 467 |
+
.gradio-container, html, body { background:#0b0b19 !important; color:#f3f4f6 !important; }
|
| 468 |
+
.block { background:#111827 !important; border:1px solid #1f2937 !important; border-radius:8px !important; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 469 |
"""
|
| 470 |
|
| 471 |
THEME = gr.themes.Default(primary_hue="purple", secondary_hue="indigo", neutral_hue="slate")
|
| 472 |
|
| 473 |
|
| 474 |
+
def create_ui():
|
| 475 |
+
with gr.Blocks() as demo:
|
| 476 |
+
user_id = gr.Textbox(visible=False)
|
| 477 |
+
gr.Markdown("# ⚡ Next-Gen Image Lab")
|
|
|
|
| 478 |
gr.Markdown(
|
| 479 |
+
"### FLUX.1 Schnell · SD 3.5 Medium · Adult SDXL NSFW chefs · ZeroGPU\\n\\n"
|
| 480 |
+
"18+ only. FLUX = newest fast tech. SD3.5 = newest quality (may need HF license accept). "
|
| 481 |
+
"Adult SDXL = your proven smut chefs."
|
| 482 |
)
|
| 483 |
|
| 484 |
with gr.Row():
|
| 485 |
with gr.Column(scale=2):
|
| 486 |
+
prompt = gr.Textbox(label="Prompt", value=random.choice(RANDOM_PROMPTS), lines=7)
|
| 487 |
+
negative = gr.Textbox(label="Negatives (SD3.5 / SDXL; ignored by FLUX)", value=BASE_NEGATIVES, lines=4)
|
| 488 |
+
styles = gr.CheckboxGroup(
|
| 489 |
+
label="Style packs (stackable)",
|
| 490 |
+
choices=list(STYLE_PACKS.keys()),
|
| 491 |
+
value=["Men Only", "Photo Gloss"],
|
| 492 |
)
|
| 493 |
+
go = gr.Button("GENERATE", variant="primary", size="lg")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 494 |
with gr.Column(scale=1):
|
| 495 |
+
engine = gr.Radio(label="Engine", choices=ENGINE_CHOICES, value=ENGINE_FLUX)
|
| 496 |
+
sdxl_model = gr.Dropdown(
|
| 497 |
+
label="Adult SDXL chef (only used on Adult SDXL engine)",
|
| 498 |
+
choices=SDXL_CHOICES,
|
| 499 |
+
value="OhMySister Beautiful Asian Woman",
|
| 500 |
+
)
|
| 501 |
+
seed = gr.Number(label="Seed (-1 random)", value=-1, precision=0)
|
| 502 |
+
width = gr.Slider(512, 1536, value=1024, step=64, label="Width")
|
| 503 |
+
height = gr.Slider(512, 1536, value=1024, step=64, label="Height")
|
| 504 |
+
steps = gr.Slider(1, 40, value=4, step=1, label="Steps (FLUX~4, SD3.5~28, SDXL~28)")
|
| 505 |
+
cfg = gr.Slider(0.0, 12.0, value=4.5, step=0.5, label="CFG (FLUX uses 0)")
|
| 506 |
+
batch = gr.Slider(1, 3, value=1, step=1, label="Batch (max 3)")
|
| 507 |
+
|
| 508 |
+
gallery = gr.Gallery(label="Outputs", columns=3, height="auto")
|
| 509 |
+
info = gr.Textbox(label="Run info", visible=True)
|
| 510 |
+
meta = gr.Textbox(label="Enhanced prompt", visible=True)
|
| 511 |
+
|
| 512 |
+
demo.load(fn=None, inputs=None, outputs=user_id, js=js_visitor)
|
| 513 |
+
|
| 514 |
+
def on_go(*args):
|
| 515 |
+
images, run_info, enhanced, jobs = generate(*args)
|
| 516 |
+
if jobs:
|
| 517 |
+
for job in jobs:
|
| 518 |
+
threading.Thread(target=async_log_upload, args=job, daemon=True).start()
|
| 519 |
+
if images is None:
|
| 520 |
+
return None, run_info, enhanced or ""
|
| 521 |
+
return images, run_info, enhanced
|
| 522 |
+
|
| 523 |
+
go.click(
|
| 524 |
+
fn=on_go,
|
| 525 |
+
inputs=[engine, prompt, styles, negative, sdxl_model, steps, cfg, seed, width, height, batch, user_id],
|
| 526 |
+
outputs=[gallery, info, meta],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 527 |
)
|
| 528 |
+
return demo
|
| 529 |
|
| 530 |
|
| 531 |
if __name__ == "__main__":
|
|
|
|
| 532 |
os.environ["GRADIO_ANALYTICS_ENABLED"] = "False"
|
| 533 |
+
app = create_ui()
|
| 534 |
+
app.queue(max_size=12)
|
| 535 |
+
app.launch(ssr_mode=False, theme=THEME, css=css)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|