yukee1992 commited on
Commit
dbad725
Β·
verified Β·
1 Parent(s): b272d86

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +77 -66
app.py CHANGED
@@ -11,7 +11,7 @@ import tempfile
11
  import time
12
  import base64
13
  import json
14
- from typing import Dict, List, Tuple
15
  from fastapi import FastAPI, HTTPException
16
  from pydantic import BaseModel
17
  import random
@@ -37,9 +37,14 @@ class StoryScene(BaseModel):
37
  visual: str
38
  text: str
39
 
 
 
 
 
40
  class StorybookRequest(BaseModel):
41
  story_title: str
42
  scenes: List[StoryScene]
 
43
  model_choice: str = "dreamshaper-8"
44
  style: str = "childrens_book"
45
 
@@ -57,6 +62,10 @@ model_cache = {}
57
  current_model_name = None
58
  pipe = None
59
 
 
 
 
 
60
  def load_model(model_name="dreamshaper-8"):
61
  """Load model into global cache - runs only once per model"""
62
  global model_cache, current_model_name, pipe
@@ -189,8 +198,28 @@ def save_complete_storybook_page(image, story_title, sequence_number, scene_text
189
  except Exception as e:
190
  return f"❌ Save failed: {str(e)}"
191
 
192
- def generate_storybook_page(scene_visual, story_title, sequence_number, scene_text, model_choice="dreamshaper-8", style="childrens_book"):
193
- """Generate a storybook page with both image and text"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
194
  global pipe, current_model_name
195
 
196
  try:
@@ -199,10 +228,27 @@ def generate_storybook_page(scene_visual, story_title, sequence_number, scene_te
199
  print(f"πŸ”„ Switching to model: {model_choice}")
200
  pipe = load_model(model_choice)
201
 
202
- # Enhanced prompt for better quality
203
- enhanced_prompt, negative_prompt = enhance_prompt(scene_visual, style)
 
 
 
 
 
 
204
 
205
  print(f"πŸ“– Generating page {sequence_number} for: {story_title}")
 
 
 
 
 
 
 
 
 
 
 
206
 
207
  # Generate high-quality image
208
  image = pipe(
@@ -212,7 +258,7 @@ def generate_storybook_page(scene_visual, story_title, sequence_number, scene_te
212
  guidance_scale=8.5,
213
  width=768,
214
  height=768,
215
- generator=torch.Generator(device="cpu").manual_seed(int(time.time()))
216
  ).images[0]
217
 
218
  # Save both image and text
@@ -223,15 +269,23 @@ def generate_storybook_page(scene_visual, story_title, sequence_number, scene_te
223
  except Exception as e:
224
  return None, f"❌ Generation failed: {str(e)}"
225
 
226
- def batch_generate_complete_storybook(story_title, scenes_data, model_choice="dreamshaper-8", style="childrens_book"):
227
  """Generate complete storybook with images and text - MODEL LOADS ONLY ONCE"""
 
 
228
  results = []
229
  status_messages = []
230
 
231
  print(f"πŸ“š Starting batch generation for: {story_title}")
232
  print(f"πŸ“– Total pages: {len(scenes_data)}")
 
233
  print(f"🎨 Using model: {model_choice}")
234
 
 
 
 
 
 
235
  # Load model once at the beginning
236
  global pipe
237
  pipe = load_model(model_choice)
@@ -244,7 +298,7 @@ def batch_generate_complete_storybook(story_title, scenes_data, model_choice="dr
244
 
245
  print(f"πŸ”„ Generating page {i}/{len(scenes_data)}...")
246
  image, status = generate_storybook_page(
247
- scene_visual, story_title, i, scene_text, model_choice, style
248
  )
249
 
250
  if image:
@@ -260,12 +314,15 @@ def batch_generate_complete_storybook(story_title, scenes_data, model_choice="dr
260
  # FastAPI endpoint for n8n
261
  @app.post("/api/generate-storybook")
262
  async def api_generate_storybook(request: StorybookRequest):
263
- """API endpoint for n8n automation - OPTIMIZED"""
264
  try:
265
  print(f"πŸ“š Received storybook request: {request.story_title}")
266
  print(f"πŸ“– Pages to generate: {len(request.scenes)}")
267
- print(f"🎨 Model: {request.model_choice}")
268
- print(f"🎨 Style: {request.style}")
 
 
 
269
 
270
  # Convert to scene data format
271
  scenes_data = [{"visual": scene.visual, "text": scene.text} for scene in request.scenes]
@@ -274,6 +331,7 @@ async def api_generate_storybook(request: StorybookRequest):
274
  results, status = batch_generate_complete_storybook(
275
  request.story_title,
276
  scenes_data,
 
277
  request.model_choice,
278
  request.style
279
  )
@@ -282,6 +340,7 @@ async def api_generate_storybook(request: StorybookRequest):
282
  "status": "success",
283
  "story_title": request.story_title,
284
  "total_pages": len(request.scenes),
 
285
  "generated_pages": len(results),
286
  "message": status,
287
  "folder_path": f"storybook-library/stories/{request.story_title.replace(' ', '_')}/",
@@ -297,6 +356,8 @@ async def api_generate_storybook(request: StorybookRequest):
297
  except Exception as e:
298
  error_msg = f"Storybook generation failed: {str(e)}"
299
  print(f"❌ {error_msg}")
 
 
300
  raise HTTPException(status_code=500, detail=error_msg)
301
 
302
  # Health check endpoint
@@ -308,58 +369,34 @@ async def health_check():
308
  "timestamp": datetime.now().isoformat(),
309
  "models_loaded": list(model_cache.keys()),
310
  "current_model": current_model_name,
311
- "cached_models_count": len(model_cache)
 
312
  }
313
 
314
- # ... (keep the rest of your Gradio interface code the same) ...
315
-
316
- # Gradio Interface Functions
317
  def generate_single_page(prompt, story_title, scene_text, model_choice, style):
318
  """Generate a single page for Gradio interface"""
319
  if not prompt or not story_title:
320
  return None, "❌ Please enter both scene description and story title"
321
 
322
  image, status = generate_storybook_page(
323
- prompt, story_title, 1, scene_text or "", model_choice, style
324
  )
325
  return image, status
326
 
327
- def generate_full_storybook(story_title, scenes_text, model_choice, style):
328
- """Generate full storybook for Gradio interface"""
329
- if not story_title or not scenes_text:
330
- return [], "❌ Please provide story title and scenes"
331
-
332
- # Parse scenes from text input
333
- scenes = []
334
- for i, line in enumerate(scenes_text.split('\n')):
335
- if line.strip():
336
- scenes.append({
337
- "visual": line.strip(),
338
- "text": f"Page {i+1} text description"
339
- })
340
-
341
- results, status = batch_generate_complete_storybook(
342
- story_title, scenes, model_choice, style
343
- )
344
- return results, status
345
-
346
  # Create the Gradio interface
347
  with gr.Blocks(title="Storybook Generator", theme="soft") as demo:
348
  gr.Markdown("# πŸ“š Storybook Generator")
349
- gr.Markdown("Create beautiful storybooks with images and text")
350
 
351
  with gr.Row():
352
  with gr.Column(scale=1):
353
- gr.Markdown("### πŸ“– Story Information")
354
-
355
  story_title_input = gr.Textbox(
356
  label="Story Title",
357
  placeholder="Enter your story title...",
358
  lines=1
359
  )
360
 
361
- gr.Markdown("### 🎯 Quality Settings")
362
-
363
  model_choice = gr.Dropdown(
364
  label="AI Model",
365
  choices=list(MODEL_CHOICES.keys()),
@@ -373,8 +410,6 @@ with gr.Blocks(title="Storybook Generator", theme="soft") as demo:
373
  )
374
 
375
  with gr.Column(scale=2):
376
- gr.Markdown("### 🎨 Single Page Generation")
377
-
378
  prompt_input = gr.Textbox(
379
  label="Visual Description",
380
  placeholder="Describe the scene for image generation...",
@@ -391,35 +426,11 @@ with gr.Blocks(title="Storybook Generator", theme="soft") as demo:
391
  image_output = gr.Image(label="Generated Page", height=400)
392
  status_output = gr.Textbox(label="Status", interactive=False)
393
 
394
- with gr.Row():
395
- gr.Markdown("### πŸ“š Complete Storybook Generation")
396
-
397
- with gr.Row():
398
- with gr.Column():
399
- scenes_input = gr.Textbox(
400
- label="Visual Descriptions (One per line)",
401
- placeholder="Enter each page's visual description on separate lines...",
402
- lines=6
403
- )
404
-
405
- batch_btn = gr.Button("πŸ“– Generate Complete Storybook", variant="primary")
406
-
407
- with gr.Column():
408
- batch_status = gr.Textbox(label="Generation Status", interactive=False, lines=6)
409
- batch_gallery = gr.Gallery(label="Storybook Pages", columns=2, height=600)
410
-
411
- # Connect buttons to functions
412
  generate_btn.click(
413
  fn=generate_single_page,
414
  inputs=[prompt_input, story_title_input, text_input, model_choice, style_choice],
415
  outputs=[image_output, status_output]
416
  )
417
-
418
- batch_btn.click(
419
- fn=generate_full_storybook,
420
- inputs=[story_title_input, scenes_input, model_choice, style_choice],
421
- outputs=[batch_gallery, batch_status]
422
- )
423
 
424
  # Mount Gradio app to FastAPI
425
  app = gr.mount_gradio_app(app, demo, path="/")
 
11
  import time
12
  import base64
13
  import json
14
+ from typing import Dict, List, Tuple, Optional
15
  from fastapi import FastAPI, HTTPException
16
  from pydantic import BaseModel
17
  import random
 
37
  visual: str
38
  text: str
39
 
40
+ class CharacterDescription(BaseModel):
41
+ name: str
42
+ description: str
43
+
44
  class StorybookRequest(BaseModel):
45
  story_title: str
46
  scenes: List[StoryScene]
47
+ characters: List[CharacterDescription] = []
48
  model_choice: str = "dreamshaper-8"
49
  style: str = "childrens_book"
50
 
 
62
  current_model_name = None
63
  pipe = None
64
 
65
+ # Character consistency tracking
66
+ character_descriptions = {}
67
+ character_seeds = {} # Store seeds for consistent character generation
68
+
69
  def load_model(model_name="dreamshaper-8"):
70
  """Load model into global cache - runs only once per model"""
71
  global model_cache, current_model_name, pipe
 
198
  except Exception as e:
199
  return f"❌ Save failed: {str(e)}"
200
 
201
+ def enhance_with_character_context(scene_visual, story_title, characters):
202
+ """Add character descriptions to maintain consistency"""
203
+ if characters:
204
+ character_context = " ".join([f"{char.name}: {char.description}" for char in characters])
205
+ return f"Character descriptions: {character_context}. {scene_visual}"
206
+ return scene_visual
207
+
208
+ def get_character_seed(story_title, character_name):
209
+ """Get consistent seed for character generation"""
210
+ if story_title not in character_seeds:
211
+ character_seeds[story_title] = {}
212
+
213
+ if character_name not in character_seeds[story_title]:
214
+ # Generate a stable seed based on character name and story title
215
+ seed_value = hash(f"{story_title}_{character_name}") % 1000000
216
+ character_seeds[story_title][character_name] = seed_value
217
+ print(f"🌱 Seed for {character_name}: {seed_value}")
218
+
219
+ return character_seeds[story_title][character_name]
220
+
221
+ def generate_storybook_page(scene_visual, story_title, sequence_number, scene_text, characters, model_choice="dreamshaper-8", style="childrens_book"):
222
+ """Generate a storybook page with character consistency"""
223
  global pipe, current_model_name
224
 
225
  try:
 
228
  print(f"πŸ”„ Switching to model: {model_choice}")
229
  pipe = load_model(model_choice)
230
 
231
+ # ENHANCE PROMPT WITH CHARACTER CONTEXT
232
+ enhanced_visual = enhance_with_character_context(scene_visual, story_title, characters)
233
+
234
+ # Add scene continuity context
235
+ if sequence_number > 1:
236
+ enhanced_visual = f"Scene {sequence_number}, maintain character consistency from previous scenes. {enhanced_visual}"
237
+
238
+ enhanced_prompt, negative_prompt = enhance_prompt(enhanced_visual, style)
239
 
240
  print(f"πŸ“– Generating page {sequence_number} for: {story_title}")
241
+ if characters:
242
+ print(f"πŸ‘€ Characters: {[char.name for char in characters]}")
243
+
244
+ # Use consistent seed for character generation
245
+ generator = torch.Generator(device="cpu")
246
+ if characters:
247
+ # Use seed from main character for consistency
248
+ main_char_seed = get_character_seed(story_title, characters[0].name)
249
+ generator.manual_seed(main_char_seed)
250
+ else:
251
+ generator.manual_seed(int(time.time()))
252
 
253
  # Generate high-quality image
254
  image = pipe(
 
258
  guidance_scale=8.5,
259
  width=768,
260
  height=768,
261
+ generator=generator
262
  ).images[0]
263
 
264
  # Save both image and text
 
269
  except Exception as e:
270
  return None, f"❌ Generation failed: {str(e)}"
271
 
272
+ def batch_generate_complete_storybook(story_title, scenes_data, characters, model_choice="dreamshaper-8", style="childrens_book"):
273
  """Generate complete storybook with images and text - MODEL LOADS ONLY ONCE"""
274
+ global character_descriptions
275
+
276
  results = []
277
  status_messages = []
278
 
279
  print(f"πŸ“š Starting batch generation for: {story_title}")
280
  print(f"πŸ“– Total pages: {len(scenes_data)}")
281
+ print(f"πŸ‘€ Characters: {len(characters)}")
282
  print(f"🎨 Using model: {model_choice}")
283
 
284
+ # Store character descriptions for this story
285
+ if characters:
286
+ character_descriptions[story_title] = characters
287
+ print(f"βœ… Character context stored for {story_title}")
288
+
289
  # Load model once at the beginning
290
  global pipe
291
  pipe = load_model(model_choice)
 
298
 
299
  print(f"πŸ”„ Generating page {i}/{len(scenes_data)}...")
300
  image, status = generate_storybook_page(
301
+ scene_visual, story_title, i, scene_text, characters, model_choice, style
302
  )
303
 
304
  if image:
 
314
  # FastAPI endpoint for n8n
315
  @app.post("/api/generate-storybook")
316
  async def api_generate_storybook(request: StorybookRequest):
317
+ """API endpoint for n8n automation - OPTIMIZED with character consistency"""
318
  try:
319
  print(f"πŸ“š Received storybook request: {request.story_title}")
320
  print(f"πŸ“– Pages to generate: {len(request.scenes)}")
321
+ print(f"πŸ‘€ Characters received: {len(request.characters)}")
322
+
323
+ if request.characters:
324
+ for char in request.characters:
325
+ print(f" - {char.name}: {char.description[:50]}...")
326
 
327
  # Convert to scene data format
328
  scenes_data = [{"visual": scene.visual, "text": scene.text} for scene in request.scenes]
 
331
  results, status = batch_generate_complete_storybook(
332
  request.story_title,
333
  scenes_data,
334
+ request.characters,
335
  request.model_choice,
336
  request.style
337
  )
 
340
  "status": "success",
341
  "story_title": request.story_title,
342
  "total_pages": len(request.scenes),
343
+ "characters_used": len(request.characters),
344
  "generated_pages": len(results),
345
  "message": status,
346
  "folder_path": f"storybook-library/stories/{request.story_title.replace(' ', '_')}/",
 
356
  except Exception as e:
357
  error_msg = f"Storybook generation failed: {str(e)}"
358
  print(f"❌ {error_msg}")
359
+ import traceback
360
+ traceback.print_exc()
361
  raise HTTPException(status_code=500, detail=error_msg)
362
 
363
  # Health check endpoint
 
369
  "timestamp": datetime.now().isoformat(),
370
  "models_loaded": list(model_cache.keys()),
371
  "current_model": current_model_name,
372
+ "cached_models_count": len(model_cache),
373
+ "stories_tracked": len(character_descriptions)
374
  }
375
 
376
+ # Gradio Interface Functions (simplified)
 
 
377
  def generate_single_page(prompt, story_title, scene_text, model_choice, style):
378
  """Generate a single page for Gradio interface"""
379
  if not prompt or not story_title:
380
  return None, "❌ Please enter both scene description and story title"
381
 
382
  image, status = generate_storybook_page(
383
+ prompt, story_title, 1, scene_text or "", [], model_choice, style
384
  )
385
  return image, status
386
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
387
  # Create the Gradio interface
388
  with gr.Blocks(title="Storybook Generator", theme="soft") as demo:
389
  gr.Markdown("# πŸ“š Storybook Generator")
390
+ gr.Markdown("Create beautiful storybooks with consistent characters")
391
 
392
  with gr.Row():
393
  with gr.Column(scale=1):
 
 
394
  story_title_input = gr.Textbox(
395
  label="Story Title",
396
  placeholder="Enter your story title...",
397
  lines=1
398
  )
399
 
 
 
400
  model_choice = gr.Dropdown(
401
  label="AI Model",
402
  choices=list(MODEL_CHOICES.keys()),
 
410
  )
411
 
412
  with gr.Column(scale=2):
 
 
413
  prompt_input = gr.Textbox(
414
  label="Visual Description",
415
  placeholder="Describe the scene for image generation...",
 
426
  image_output = gr.Image(label="Generated Page", height=400)
427
  status_output = gr.Textbox(label="Status", interactive=False)
428
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
429
  generate_btn.click(
430
  fn=generate_single_page,
431
  inputs=[prompt_input, story_title_input, text_input, model_choice, style_choice],
432
  outputs=[image_output, status_output]
433
  )
 
 
 
 
 
 
434
 
435
  # Mount Gradio app to FastAPI
436
  app = gr.mount_gradio_app(app, demo, path="/")