JustinD commited on
Commit
de82d04
·
verified ·
1 Parent(s): bda6f07

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +42 -22
app.py CHANGED
@@ -1,3 +1,4 @@
 
1
  # ===== LINE 1: MUST BE SPACES FOR ZEROGPU =====
2
  try:
3
  import spaces
@@ -127,20 +128,28 @@ SCENES = [
127
  "bedroom with white sheets and linen curtains",
128
  ]
129
 
130
- OUTFITS = [
 
131
  "black lace lingerie set",
132
- "unbuttoned oversized white shirt, nothing underneath",
133
- "open silk robe",
134
  "black latex corset and sheer stockings",
135
  "sheer slip dress",
136
- "white towel slipping",
137
  "cropped knit sweater and panties",
138
  "string bikini",
139
- "black blazer with nothing underneath",
140
- "topless, denim shorts",
141
- "fully nude",
142
- "fully nude, gold body chain",
143
  ]
 
 
 
 
 
 
 
 
 
144
 
145
  POSES = [
146
  "caught mid task, glancing at the camera",
@@ -185,7 +194,7 @@ FACES = [
185
  "monolid eyes, glossy lips", "heavy lashes, sultry stare", "freckles, bright eyes",
186
  "beauty mark under one eye", "strong brows, calm gaze",
187
  ]
188
- DEFAULT_FACE = "adult Japanese woman late 20s, long straight black hair, soft almond eyes, full lips"
189
 
190
  # Tease then reveal: frame 1 states
191
  TEASE_STATES = [
@@ -195,15 +204,16 @@ TEASE_STATES = [
195
  "towel slipping down",
196
  ]
197
 
198
- FIELDS = ["chef", "style", "scene", "outfit", "pose", "light", "camera", "woman"]
199
 
200
  # ---------------------------------------------------------------------------
201
  # Hard locks
202
  # ---------------------------------------------------------------------------
203
  ADULT_TAGS = "masterpiece, best quality, 1girl, solo, adult woman 21+, late 20s, fictional character"
 
204
  BODY_LOCK = (
205
  "tall, slim body, small frame, tiny waist, "
206
- "large breasts, full breasts, heavy breasts, cleavage, slender thighs"
207
  )
208
 
209
  BASE_NEGATIVES = (
@@ -214,7 +224,8 @@ BASE_NEGATIVES = (
214
  "fat, chubby, obese, overweight, belly fat, thick thighs, thick body, "
215
  "beard, mustache, penis, dick, balls, "
216
  "bad hands, extra fingers, deformed, "
217
- "blurry, lowres, watermark, text, censored, ugly, celebrity, real person"
 
218
  )
219
 
220
  FORBIDDEN_KEYWORDS = []
@@ -254,7 +265,7 @@ def check_prompt_safety(prompt: str):
254
  # ---------------------------------------------------------------------------
255
  def random_woman(rng=random):
256
  return (
257
- f"adult {rng.choice(ETHNICITIES)} woman late 20s, "
258
  f"{rng.choice(HAIRS)}, {rng.choice(FACES)}"
259
  )
260
 
@@ -283,7 +294,8 @@ def roll_recipe(prev=None, locks=None, new_woman=True, rng=random):
283
  else:
284
  recipe["chef"] = rng.choice(chef_pool_for_style(recipe["style"]))
285
  recipe["scene"] = prev["scene"] if keep("scene") else rng.choice(SCENES)
286
- recipe["outfit"] = prev["outfit"] if keep("outfit") else rng.choice(OUTFITS)
 
287
  recipe["pose"] = prev["pose"] if keep("pose") else rng.choice(POSES)
288
  recipe["light"] = prev["light"] if keep("light") else rng.choice(LIGHTS)
289
  recipe["camera"] = prev["camera"] if keep("camera") else rng.choice(CAMERAS)
@@ -305,12 +317,19 @@ def build_frames(recipe, tease_reveal=False, extra=""):
305
  """Return list of (label, positive, negative). Adult tags + body lock always present."""
306
  style = STYLES.get(recipe.get("style"), STYLES["Photoreal"])
307
  negative = _join(BASE_NEGATIVES, style["neg"])
308
- common_head = _join(style["prefix"], ADULT_TAGS, recipe.get("woman") or DEFAULT_FACE, BODY_LOCK)
309
  tail = _join(recipe.get("scene"), recipe.get("light"), "confident eye contact", extra)
310
 
311
  if not tease_reveal:
312
- pos = _join(common_head, recipe.get("outfit"), recipe.get("pose"), recipe.get("camera"), tail)
313
- return [("Roll", pos, negative)]
 
 
 
 
 
 
 
314
 
315
  # Same seed, two frames, both full body so the reveal reads as a pair
316
  frame1 = _join(
@@ -329,7 +348,7 @@ def recipe_card(recipe, seed=None):
329
  return "_No roll yet. Hit **Spin**._"
330
  lines = ["### Recipe"]
331
  for field in FIELDS:
332
- lines.append(f"- **{field.title()}**: {recipe.get(field, '')}")
333
  if seed is not None:
334
  lines.append(f"- **Seed**: {seed}")
335
  return "\n".join(lines)
@@ -586,7 +605,7 @@ def create_ui():
586
  with gr.Blocks(title="Roulette Lab") as demo:
587
  gr.Markdown(
588
  "# Roulette Lab\n"
589
- "Spin a random recipe. Lock what you like, spin again, and only the unlocked slots change. "
590
  "Fictional adult women 21+ only. Visitor images are not saved unless the Space secret "
591
  "`LOG_VISITOR_UPLOADS=1` is set."
592
  )
@@ -601,9 +620,9 @@ def create_ui():
601
  preview_btn = gr.Button("Spin card only (no render)")
602
  card = gr.Markdown(recipe_card({}))
603
  gr.Markdown("**Lock slots** (locked slots survive the next spin)")
604
- lock_boxes = [gr.Checkbox(label=f"Lock {f}", value=False) for f in FIELDS]
605
  new_woman = gr.Checkbox(label="New woman each roll", value=True)
606
- tease_reveal = gr.Checkbox(label="Tease then reveal (same seed, 2 frames)", value=False)
607
  extra = gr.Textbox(label="Extra tags (optional)", placeholder="e.g. glossy lips, rain on skin")
608
  with gr.Accordion("Settings", open=False):
609
  size_name = gr.Radio(list(SIZE_PRESETS), value="Portrait 832x1216", label="Size")
@@ -647,4 +666,5 @@ if __name__ == "__main__":
647
  os.environ["GRADIO_ANALYTICS_ENABLED"] = "False"
648
  app = create_ui()
649
  app.queue(max_size=12)
650
- app.launch(ssr_mode=False)
 
 
1
+ ```python
2
  # ===== LINE 1: MUST BE SPACES FOR ZEROGPU =====
3
  try:
4
  import spaces
 
128
  "bedroom with white sheets and linen curtains",
129
  ]
130
 
131
+ # Every spin makes a pair with the same seed: one clothed, one nude
132
+ CLOTHED_OUTFITS = [
133
  "black lace lingerie set",
134
+ "unbuttoned oversized white shirt, deep cleavage",
135
+ "silk robe, deep cleavage",
136
  "black latex corset and sheer stockings",
137
  "sheer slip dress",
138
+ "white towel wrapped around her",
139
  "cropped knit sweater and panties",
140
  "string bikini",
141
+ "black blazer, deep cleavage",
142
+ "tight low cut red dress",
 
 
143
  ]
144
+ NUDE_OUTFITS = [
145
+ "fully nude, bare breasts, nipples",
146
+ "fully nude, gold body chain, bare breasts",
147
+ "topless, bare breasts, denim shorts",
148
+ "completely naked, bare breasts, nipples, nothing covering her",
149
+ "nude except black thigh high stockings, bare breasts",
150
+ "fully nude, wet skin, bare breasts",
151
+ ]
152
+ OUTFITS = CLOTHED_OUTFITS
153
 
154
  POSES = [
155
  "caught mid task, glancing at the camera",
 
194
  "monolid eyes, glossy lips", "heavy lashes, sultry stare", "freckles, bright eyes",
195
  "beauty mark under one eye", "strong brows, calm gaze",
196
  ]
197
+ DEFAULT_FACE = "adult Japanese Asian woman late 20s, long straight black hair, soft almond eyes, full lips"
198
 
199
  # Tease then reveal: frame 1 states
200
  TEASE_STATES = [
 
204
  "towel slipping down",
205
  ]
206
 
207
+ FIELDS = ["chef", "style", "scene", "outfit", "nude_look", "pose", "light", "camera", "woman"]
208
 
209
  # ---------------------------------------------------------------------------
210
  # Hard locks
211
  # ---------------------------------------------------------------------------
212
  ADULT_TAGS = "masterpiece, best quality, 1girl, solo, adult woman 21+, late 20s, fictional character"
213
+ ASIAN_LOCK = "beautiful Asian woman, Asian facial features, Asian eyes, dark hair"
214
  BODY_LOCK = (
215
  "tall, slim body, small frame, tiny waist, "
216
+ "huge breasts, very large breasts, heavy full round breasts, busty, deep cleavage, slender thighs"
217
  )
218
 
219
  BASE_NEGATIVES = (
 
224
  "fat, chubby, obese, overweight, belly fat, thick thighs, thick body, "
225
  "beard, mustache, penis, dick, balls, "
226
  "bad hands, extra fingers, deformed, "
227
+ "blurry, lowres, watermark, text, censored, ugly, celebrity, real person, "
228
+ "caucasian, white woman, european, blonde hair, small breasts, flat chest"
229
  )
230
 
231
  FORBIDDEN_KEYWORDS = []
 
265
  # ---------------------------------------------------------------------------
266
  def random_woman(rng=random):
267
  return (
268
+ f"adult {rng.choice(ETHNICITIES)} Asian woman late 20s, "
269
  f"{rng.choice(HAIRS)}, {rng.choice(FACES)}"
270
  )
271
 
 
294
  else:
295
  recipe["chef"] = rng.choice(chef_pool_for_style(recipe["style"]))
296
  recipe["scene"] = prev["scene"] if keep("scene") else rng.choice(SCENES)
297
+ recipe["outfit"] = prev["outfit"] if keep("outfit") else rng.choice(CLOTHED_OUTFITS)
298
+ recipe["nude_look"] = prev["nude_look"] if keep("nude_look") else rng.choice(NUDE_OUTFITS)
299
  recipe["pose"] = prev["pose"] if keep("pose") else rng.choice(POSES)
300
  recipe["light"] = prev["light"] if keep("light") else rng.choice(LIGHTS)
301
  recipe["camera"] = prev["camera"] if keep("camera") else rng.choice(CAMERAS)
 
317
  """Return list of (label, positive, negative). Adult tags + body lock always present."""
318
  style = STYLES.get(recipe.get("style"), STYLES["Photoreal"])
319
  negative = _join(BASE_NEGATIVES, style["neg"])
320
+ common_head = _join(style["prefix"], ADULT_TAGS, ASIAN_LOCK, recipe.get("woman") or DEFAULT_FACE, BODY_LOCK)
321
  tail = _join(recipe.get("scene"), recipe.get("light"), "confident eye contact", extra)
322
 
323
  if not tease_reveal:
324
+ # Pair: same seed, same woman, same pose. Frame 1 clothed, frame 2 nude.
325
+ clothed = _join(common_head, recipe.get("outfit") or CLOTHED_OUTFITS[0], "clothed, covered nipples",
326
+ recipe.get("pose"), recipe.get("camera"), tail)
327
+ nude = _join(common_head, recipe.get("nude_look") or NUDE_OUTFITS[0],
328
+ recipe.get("pose"), recipe.get("camera"), tail)
329
+ return [
330
+ ("Clothed", clothed, _join(negative, "nude, naked, topless, nipples, bare breasts")),
331
+ ("Nude", nude, negative),
332
+ ]
333
 
334
  # Same seed, two frames, both full body so the reveal reads as a pair
335
  frame1 = _join(
 
348
  return "_No roll yet. Hit **Spin**._"
349
  lines = ["### Recipe"]
350
  for field in FIELDS:
351
+ lines.append(f"- **{field.replace('_', ' ').title()}**: {recipe.get(field, '')}")
352
  if seed is not None:
353
  lines.append(f"- **Seed**: {seed}")
354
  return "\n".join(lines)
 
605
  with gr.Blocks(title="Roulette Lab") as demo:
606
  gr.Markdown(
607
  "# Roulette Lab\n"
608
+ "Spin a random recipe. Every spin makes two pictures with the same seed: one clothed, one nude. Lock what you like, spin again, and only the unlocked slots change. "
609
  "Fictional adult women 21+ only. Visitor images are not saved unless the Space secret "
610
  "`LOG_VISITOR_UPLOADS=1` is set."
611
  )
 
620
  preview_btn = gr.Button("Spin card only (no render)")
621
  card = gr.Markdown(recipe_card({}))
622
  gr.Markdown("**Lock slots** (locked slots survive the next spin)")
623
+ lock_boxes = [gr.Checkbox(label=f"Lock {f.replace('_', ' ')}", value=False) for f in FIELDS]
624
  new_woman = gr.Checkbox(label="New woman each roll", value=True)
625
+ tease_reveal = gr.Checkbox(label="Tease then reveal instead of clothed and nude (same seed, 2 frames)", value=False)
626
  extra = gr.Textbox(label="Extra tags (optional)", placeholder="e.g. glossy lips, rain on skin")
627
  with gr.Accordion("Settings", open=False):
628
  size_name = gr.Radio(list(SIZE_PRESETS), value="Portrait 832x1216", label="Size")
 
666
  os.environ["GRADIO_ANALYTICS_ENABLED"] = "False"
667
  app = create_ui()
668
  app.queue(max_size=12)
669
+ app.launch(ssr_mode=False)
670
+ ```