yukee1992 commited on
Commit
4c07f93
Β·
verified Β·
1 Parent(s): a6956eb

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +188 -151
app.py CHANGED
@@ -12,18 +12,36 @@ import time
12
  import base64
13
  import json
14
  from typing import Dict, List, Tuple
 
 
 
15
 
16
  # External OCI API URL
17
- OCI_API_BASE_URL = "https://yukee1992-oci-story-book.hf.space"
18
 
19
- # Try to import your existing OCI connector for direct access
20
- try:
21
- from app import oci_connector
22
- DIRECT_OCI_ACCESS = True
23
- print("βœ… Direct OCI access available - using existing OCI connector")
24
- except ImportError:
25
- DIRECT_OCI_ACCESS = False
26
- print("⚠️ Direct OCI access not available - using external API endpoint")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
 
28
  # HIGH-QUALITY MODEL SELECTION
29
  MODEL_CHOICES = {
@@ -34,10 +52,7 @@ MODEL_CHOICES = {
34
  "sd-2.1": "stabilityai/stable-diffusion-2-1",
35
  }
36
 
37
- # Story tracking for sequence numbering
38
- story_registry: Dict[str, int] = {} # {story_title: current_sequence}
39
-
40
- # Initialize the HIGH-QUALITY Stable Diffusion model
41
  def load_model(model_name="dreamshaper-8"):
42
  """Load and return a high-quality Stable Diffusion model"""
43
  print(f"πŸ”„ Loading {model_name} model...")
@@ -51,6 +66,7 @@ def load_model(model_name="dreamshaper-8"):
51
  requires_safety_checker=False
52
  )
53
 
 
54
  pipe.scheduler = EulerAncestralDiscreteScheduler.from_config(pipe.scheduler.config)
55
  pipe = pipe.to("cpu")
56
 
@@ -58,6 +74,7 @@ def load_model(model_name="dreamshaper-8"):
58
  return pipe
59
  except Exception as e:
60
  print(f"❌ Model loading failed: {e}")
 
61
  return StableDiffusionPipeline.from_pretrained(
62
  "runwayml/stable-diffusion-v1-5",
63
  torch_dtype=torch.float32,
@@ -96,20 +113,24 @@ def enhance_prompt(prompt, style="childrens_book"):
96
  ]
97
  }
98
 
 
99
  templates = style_templates.get(style, style_templates["childrens_book"])
100
- style_prompt = templates[0]
101
 
 
102
  enhanced = f"{style_prompt}, {prompt}"
103
 
 
104
  quality_boosters = [
105
  "intricate details", "beautiful composition", "perfect lighting",
106
  "professional artwork", "award winning", "trending on artstation"
107
  ]
108
 
109
- import random
110
  boosters = random.sample(quality_boosters, 2)
111
  enhanced += ", " + ", ".join(boosters)
112
 
 
113
  negative_prompt = (
114
  "blurry, low quality, low resolution, ugly, deformed, poorly drawn, "
115
  "bad anatomy, wrong anatomy, extra limb, missing limb, floating limbs, "
@@ -119,82 +140,49 @@ def enhance_prompt(prompt, style="childrens_book"):
119
 
120
  return enhanced, negative_prompt
121
 
122
- def get_next_sequence_number(story_title: str) -> int:
123
- """Get the next sequence number for a story"""
124
- # Clean the story title for use in filenames
125
- clean_title = re.sub(r'[^a-zA-Z0-9_\-]', '', story_title.strip().replace(' ', '_'))
126
-
127
- if clean_title not in story_registry:
128
- story_registry[clean_title] = 0
129
-
130
- story_registry[clean_title] += 1
131
- return story_registry[clean_title], clean_title
132
-
133
- def save_to_oci_via_api(image, prompt, story_title, sequence_number):
134
- """Save image with organized storybook structure"""
135
  try:
136
  # Convert image to bytes
137
  img_bytes = io.BytesIO()
138
  image.save(img_bytes, format='PNG')
139
  img_data = img_bytes.getvalue()
140
 
141
- # Get sequence number and clean title
142
- seq_num, clean_title = get_next_sequence_number(story_title)
143
- if sequence_number is not None:
144
- seq_num = sequence_number # Use provided sequence number if available
145
-
146
- # Create organized filename with sequence
147
- filename = f"page_{seq_num:03d}_{clean_title}.png"
148
-
149
- # Use the EXTERNAL API URL
150
- api_url = f"{OCI_API_BASE_URL}/api/upload"
151
 
152
- print(f"πŸ“ Saving to: stories/{clean_title}/{filename}")
 
 
153
 
154
- # Prepare form data for API request
155
- files = {
156
- 'file': (filename, img_data, 'image/png')
157
- }
 
158
 
159
- data = {
 
 
160
  'project_id': 'storybook-library',
161
- 'subfolder': f'stories/{clean_title}'
 
 
162
  }
 
163
 
164
- # Make the API request
165
- response = requests.post(api_url, files=files, data=data, timeout=30)
166
-
167
- if response.status_code == 200:
168
- result = response.json()
169
- if result['status'] == 'success':
170
- return f"βœ… Saved: {filename} | Story: {clean_title}"
171
- else:
172
- return f"❌ API Error: {result.get('message', 'Unknown error')}"
173
- else:
174
- return f"❌ HTTP Error: {response.status_code}"
175
 
176
  except Exception as e:
177
- return f"❌ Upload failed: {str(e)}"
178
 
179
- def generate_storybook_page(prompt, story_title, sequence_number=None, model_choice="dreamshaper-8", style="childrens_book"):
180
- """Generate a storybook page with organized naming"""
181
  try:
182
- if not prompt or not prompt.strip():
183
- return None, "❌ Please enter a scene description"
184
-
185
- if not story_title or not story_title.strip():
186
- return None, "❌ Please enter a story title"
187
 
188
- # Reload model if different choice
189
- global pipe
190
- if model_choice != "dreamshaper-8":
191
- pipe = load_model(model_choice)
192
-
193
- # Professional prompt enhancement
194
- enhanced_prompt, negative_prompt = enhance_prompt(prompt, style)
195
-
196
- print(f"πŸ“– Generating page for: {story_title}")
197
- print(f"🎨 Prompt: {enhanced_prompt}")
198
 
199
  # Generate high-quality image
200
  image = pipe(
@@ -207,52 +195,123 @@ def generate_storybook_page(prompt, story_title, sequence_number=None, model_cho
207
  generator=torch.Generator(device="cpu").manual_seed(int(time.time()))
208
  ).images[0]
209
 
210
- print("βœ… Page generated successfully!")
211
-
212
- # Save with organized structure
213
- save_status = save_to_oci_via_api(image, prompt, story_title, sequence_number)
214
- print(f"πŸ’Ύ {save_status}")
215
 
216
  return image, save_status
217
 
218
  except Exception as e:
219
- error_msg = f"❌ Generation failed: {str(e)}"
220
- print(error_msg)
221
- return None, error_msg
222
 
223
- def batch_generate_storybook(story_title, scenes_text, model_choice="dreamshaper-8", style="childrens_book"):
224
- """Generate multiple pages for a complete storybook"""
225
- if not story_title or not scenes_text:
226
- return [], "❌ Please provide story title and scenes"
227
-
228
- scenes = [scene.strip() for scene in scenes_text.split('\n') if scene.strip()]
229
  results = []
230
  status_messages = []
231
 
232
- for i, scene in enumerate(scenes, 1):
233
- print(f"πŸ“– Generating page {i}/{len(scenes)} for: {story_title}")
 
 
234
  image, status = generate_storybook_page(
235
- scene, story_title, i, model_choice, style
236
  )
237
 
238
  if image:
239
- results.append((f"Page {i}: {scene}", image))
240
  status_messages.append(f"Page {i}: {status}")
241
 
242
  return results, "\n".join(status_messages)
243
 
244
- # Create the enhanced Gradio interface
245
- with gr.Blocks(title="Professional Storybook Generator", theme="soft") as demo:
246
- gr.Markdown("# πŸ“š Professional Storybook Generator")
247
- gr.Markdown("Create organized storybooks with sequential page numbering")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
248
 
249
  with gr.Row():
250
  with gr.Column(scale=1):
251
  gr.Markdown("### πŸ“– Story Information")
252
 
253
- story_title = gr.Textbox(
254
  label="Story Title",
255
- placeholder="Enter your story title...\nExample: The Dragon's Reading Adventure",
256
  lines=1
257
  )
258
 
@@ -274,14 +333,20 @@ with gr.Blocks(title="Professional Storybook Generator", theme="soft") as demo:
274
  gr.Markdown("### 🎨 Single Page Generation")
275
 
276
  prompt_input = gr.Textbox(
277
- label="Page Description",
278
- placeholder="Describe this page's scene...\nExample: The dragon discovers a magical library in the forest",
 
 
 
 
 
 
279
  lines=2
280
  )
281
 
282
  generate_btn = gr.Button("✨ Generate Single Page", variant="primary")
283
- image_output = gr.Image(label="Generated Page", height=400, show_download_button=True)
284
- status_output = gr.Textbox(label="Status", interactive=False, lines=2)
285
 
286
  with gr.Row():
287
  gr.Markdown("### πŸ“š Complete Storybook Generation")
@@ -289,72 +354,44 @@ with gr.Blocks(title="Professional Storybook Generator", theme="soft") as demo:
289
  with gr.Row():
290
  with gr.Column():
291
  scenes_input = gr.Textbox(
292
- label="Story Scenes (One per line = One per page)",
293
- placeholder="Enter each page's scene on a separate line...\nExample:\nThe dragon finds a mysterious book\nHe learns to read with owl friend\nThey discover hidden treasure map\nFriends celebrate with magical feast",
294
  lines=6
295
  )
296
 
297
  batch_btn = gr.Button("πŸ“– Generate Complete Storybook", variant="primary")
298
 
299
  with gr.Column():
300
- batch_status = gr.Textbox(label="Generation Status", interactive=False, lines=8)
301
  batch_gallery = gr.Gallery(label="Storybook Pages", columns=2, height=600)
302
-
303
- with gr.Accordion("πŸ“ Folder Structure Preview", open=True):
304
- gr.Markdown("""
305
- **Your story will be organized as:**
306
- ```
307
- storybook-library/
308
- └── stories/
309
- └── The_Dragons_Reading_Adventure/
310
- β”œβ”€β”€ page_001_The_Dragons_Reading_Adventure.png
311
- β”œβ”€β”€ page_002_The_Dragons_Reading_Adventure.png
312
- β”œβ”€β”€ page_003_The_Dragons_Reading_Adventure.png
313
- └── page_004_The_Dragons_Reading_Adventure.png
314
- ```
315
-
316
- **Perfect for:**
317
- - βœ… Easy PDF compilation later
318
- - βœ… Automatic sequencing
319
- - βœ… Organized by story title
320
- - βœ… Ready for n8n automation
321
- """)
322
-
323
- with gr.Accordion("πŸ’‘ API Usage for n8n", open=False):
324
- gr.Markdown("""
325
- **For n8n automation, use this JSON format:**
326
- ```json
327
- {
328
- "story_title": "The Dragon's Reading Adventure",
329
- "scenes": [
330
- "The dragon finds a mysterious old book in the forest",
331
- "He meets an owl who teaches him to read",
332
- "They discover a hidden treasure map in the book",
333
- "All animal friends celebrate with a magical feast"
334
- ],
335
- "model_choice": "dreamshaper-8",
336
- "style": "childrens_book"
337
- }
338
- ```
339
- """)
340
 
341
  # Connect buttons to functions
342
  generate_btn.click(
343
- fn=generate_storybook_page,
344
- inputs=[prompt_input, story_title, gr.Number(visible=False), model_choice, style_choice],
345
  outputs=[image_output, status_output]
346
  )
347
 
348
  batch_btn.click(
349
- fn=batch_generate_storybook,
350
- inputs=[story_title, scenes_input, model_choice, style_choice],
351
  outputs=[batch_gallery, batch_status]
352
  )
353
 
 
 
 
 
354
  def get_app():
355
- return demo
356
 
357
  if __name__ == "__main__":
358
- print("πŸš€ Starting Professional Storybook Generator...")
359
- print("πŸ“ Feature: Organized story sequencing enabled")
360
- demo.launch(server_name="0.0.0.0", server_port=7860)
 
 
 
 
 
 
 
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
18
 
19
  # External OCI API URL
20
+ OCI_API_BASE_URL = "https://yukee1992-oci-video-storage.hf.space"
21
 
22
+ # Initialize FastAPI app
23
+ app = FastAPI(title="Storybook Generator API")
24
+
25
+ # Add CORS middleware
26
+ from fastapi.middleware.cors import CORSMiddleware
27
+ app.add_middleware(
28
+ CORSMiddleware,
29
+ allow_origins=["*"],
30
+ allow_credentials=True,
31
+ allow_methods=["*"],
32
+ allow_headers=["*"],
33
+ )
34
+
35
+ # Story scene model
36
+ 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
 
46
  # HIGH-QUALITY MODEL SELECTION
47
  MODEL_CHOICES = {
 
52
  "sd-2.1": "stabilityai/stable-diffusion-2-1",
53
  }
54
 
55
+ # Initialize the Stable Diffusion model
 
 
 
56
  def load_model(model_name="dreamshaper-8"):
57
  """Load and return a high-quality Stable Diffusion model"""
58
  print(f"πŸ”„ Loading {model_name} model...")
 
66
  requires_safety_checker=False
67
  )
68
 
69
+ # Use better scheduler for quality
70
  pipe.scheduler = EulerAncestralDiscreteScheduler.from_config(pipe.scheduler.config)
71
  pipe = pipe.to("cpu")
72
 
 
74
  return pipe
75
  except Exception as e:
76
  print(f"❌ Model loading failed: {e}")
77
+ # Fallback to SD 1.5
78
  return StableDiffusionPipeline.from_pretrained(
79
  "runwayml/stable-diffusion-v1-5",
80
  torch_dtype=torch.float32,
 
113
  ]
114
  }
115
 
116
+ # Choose style template
117
  templates = style_templates.get(style, style_templates["childrens_book"])
118
+ style_prompt = templates[0] # Use the first template
119
 
120
+ # Enhanced prompt construction
121
  enhanced = f"{style_prompt}, {prompt}"
122
 
123
+ # Add quality boosters
124
  quality_boosters = [
125
  "intricate details", "beautiful composition", "perfect lighting",
126
  "professional artwork", "award winning", "trending on artstation"
127
  ]
128
 
129
+ # Add 2-3 random quality boosters
130
  boosters = random.sample(quality_boosters, 2)
131
  enhanced += ", " + ", ".join(boosters)
132
 
133
+ # Negative prompt to avoid bad quality
134
  negative_prompt = (
135
  "blurry, low quality, low resolution, ugly, deformed, poorly drawn, "
136
  "bad anatomy, wrong anatomy, extra limb, missing limb, floating limbs, "
 
140
 
141
  return enhanced, negative_prompt
142
 
143
+ def save_complete_storybook_page(image, story_title, sequence_number, scene_text):
144
+ """Save image AND text to OCI with organized structure"""
 
 
 
 
 
 
 
 
 
 
 
145
  try:
146
  # Convert image to bytes
147
  img_bytes = io.BytesIO()
148
  image.save(img_bytes, format='PNG')
149
  img_data = img_bytes.getvalue()
150
 
151
+ # Clean title for filenames
152
+ clean_title = re.sub(r'[^a-zA-Z0-9_\-]', '', story_title.strip().replace(' ', '_'))
 
 
 
 
 
 
 
 
153
 
154
+ # Create filenames
155
+ image_filename = f"page_{sequence_number:03d}_{clean_title}.png"
156
+ text_filename = f"page_{sequence_number:03d}_{clean_title}.txt"
157
 
158
+ # Save image to OCI
159
+ image_api_url = f"{OCI_API_BASE_URL}/api/upload"
160
+ files = {'file': (image_filename, img_data, 'image/png')}
161
+ data = {'project_id': 'storybook-library', 'subfolder': f'stories/{clean_title}'}
162
+ response = requests.post(image_api_url, files=files, data=data, timeout=30)
163
 
164
+ # Save text to OCI
165
+ text_api_url = f"{OCI_API_BASE_URL}/api/upload-text"
166
+ text_data = {
167
  'project_id': 'storybook-library',
168
+ 'subfolder': f'stories/{clean_title}',
169
+ 'filename': text_filename,
170
+ 'content': scene_text
171
  }
172
+ text_response = requests.post(text_api_url, data=text_data, timeout=30)
173
 
174
+ return f"βœ… Page {sequence_number}: Image & Text saved"
 
 
 
 
 
 
 
 
 
 
175
 
176
  except Exception as e:
177
+ return f"❌ Save failed: {str(e)}"
178
 
179
+ def generate_storybook_page(scene_visual, story_title, sequence_number, scene_text, model_choice="dreamshaper-8", style="childrens_book"):
180
+ """Generate a storybook page with both image and text"""
181
  try:
182
+ # Enhanced prompt for better quality
183
+ enhanced_prompt, negative_prompt = enhance_prompt(scene_visual, style)
 
 
 
184
 
185
+ print(f"πŸ“– Generating page {sequence_number} for: {story_title}")
 
 
 
 
 
 
 
 
 
186
 
187
  # Generate high-quality image
188
  image = pipe(
 
195
  generator=torch.Generator(device="cpu").manual_seed(int(time.time()))
196
  ).images[0]
197
 
198
+ # Save both image and text
199
+ save_status = save_complete_storybook_page(image, story_title, sequence_number, scene_text)
 
 
 
200
 
201
  return image, save_status
202
 
203
  except Exception as e:
204
+ return None, f"❌ Generation failed: {str(e)}"
 
 
205
 
206
+ def batch_generate_complete_storybook(story_title, scenes_data, model_choice="dreamshaper-8", style="childrens_book"):
207
+ """Generate complete storybook with images and text"""
 
 
 
 
208
  results = []
209
  status_messages = []
210
 
211
+ for i, scene_data in enumerate(scenes_data, 1):
212
+ scene_visual = scene_data.get('visual', '')
213
+ scene_text = scene_data.get('text', '')
214
+
215
  image, status = generate_storybook_page(
216
+ scene_visual, story_title, i, scene_text, model_choice, style
217
  )
218
 
219
  if image:
220
+ results.append((f"Page {i}", image, scene_text))
221
  status_messages.append(f"Page {i}: {status}")
222
 
223
  return results, "\n".join(status_messages)
224
 
225
+ # FastAPI endpoint for n8n
226
+ @app.post("/api/generate-storybook")
227
+ async def api_generate_storybook(request: StorybookRequest):
228
+ """API endpoint for n8n automation"""
229
+ try:
230
+ print(f"πŸ“š Received storybook request: {request.story_title}")
231
+ print(f"πŸ“– Pages to generate: {len(request.scenes)}")
232
+
233
+ # Convert to scene data format
234
+ scenes_data = [{"visual": scene.visual, "text": scene.text} for scene in request.scenes]
235
+
236
+ # Generate storybook
237
+ results, status = batch_generate_complete_storybook(
238
+ request.story_title,
239
+ scenes_data,
240
+ request.model_choice,
241
+ request.style
242
+ )
243
+
244
+ return {
245
+ "status": "success",
246
+ "story_title": request.story_title,
247
+ "total_pages": len(request.scenes),
248
+ "generated_pages": len(results),
249
+ "message": status,
250
+ "folder_path": f"storybook-library/stories/{request.story_title.replace(' ', '_')}/",
251
+ "pages": [
252
+ {
253
+ "page_number": i+1,
254
+ "image_file": f"page_{i+1:03d}_{request.story_title.replace(' ', '_')}.png",
255
+ "text_file": f"page_{i+1:03d}_{request.story_title.replace(' ', '_')}.txt"
256
+ } for i in range(len(request.scenes))
257
+ ]
258
+ }
259
+
260
+ except Exception as e:
261
+ raise HTTPException(status_code=500, detail=f"Storybook generation failed: {str(e)}")
262
+
263
+ # Health check endpoint
264
+ @app.get("/api/health")
265
+ async def health_check():
266
+ return {
267
+ "status": "healthy",
268
+ "service": "Storybook Generator API",
269
+ "timestamp": datetime.now().isoformat(),
270
+ "models_available": list(MODEL_CHOICES.keys())
271
+ }
272
+
273
+ # Gradio Interface Functions
274
+ def generate_single_page(prompt, story_title, scene_text, model_choice, style):
275
+ """Generate a single page for Gradio interface"""
276
+ if not prompt or not story_title:
277
+ return None, "❌ Please enter both scene description and story title"
278
+
279
+ image, status = generate_storybook_page(
280
+ prompt, story_title, 1, scene_text or "", model_choice, style
281
+ )
282
+ return image, status
283
+
284
+ def generate_full_storybook(story_title, scenes_text, model_choice, style):
285
+ """Generate full storybook for Gradio interface"""
286
+ if not story_title or not scenes_text:
287
+ return [], "❌ Please provide story title and scenes"
288
+
289
+ # Parse scenes from text input
290
+ scenes = []
291
+ for i, line in enumerate(scenes_text.split('\n')):
292
+ if line.strip():
293
+ scenes.append({
294
+ "visual": line.strip(),
295
+ "text": f"Page {i+1} text description"
296
+ })
297
+
298
+ results, status = batch_generate_complete_storybook(
299
+ story_title, scenes, model_choice, style
300
+ )
301
+ return results, status
302
+
303
+ # Create the Gradio interface
304
+ with gr.Blocks(title="Storybook Generator", theme="soft") as demo:
305
+ gr.Markdown("# πŸ“š Storybook Generator")
306
+ gr.Markdown("Create beautiful storybooks with images and text")
307
 
308
  with gr.Row():
309
  with gr.Column(scale=1):
310
  gr.Markdown("### πŸ“– Story Information")
311
 
312
+ story_title_input = gr.Textbox(
313
  label="Story Title",
314
+ placeholder="Enter your story title...",
315
  lines=1
316
  )
317
 
 
333
  gr.Markdown("### 🎨 Single Page Generation")
334
 
335
  prompt_input = gr.Textbox(
336
+ label="Visual Description",
337
+ placeholder="Describe the scene for image generation...",
338
+ lines=3
339
+ )
340
+
341
+ text_input = gr.Textbox(
342
+ label="Story Text (Optional)",
343
+ placeholder="Enter the story text for this page...",
344
  lines=2
345
  )
346
 
347
  generate_btn = gr.Button("✨ Generate Single Page", variant="primary")
348
+ image_output = gr.Image(label="Generated Page", height=400)
349
+ status_output = gr.Textbox(label="Status", interactive=False)
350
 
351
  with gr.Row():
352
  gr.Markdown("### πŸ“š Complete Storybook Generation")
 
354
  with gr.Row():
355
  with gr.Column():
356
  scenes_input = gr.Textbox(
357
+ label="Visual Descriptions (One per line)",
358
+ placeholder="Enter each page's visual description on separate lines...",
359
  lines=6
360
  )
361
 
362
  batch_btn = gr.Button("πŸ“– Generate Complete Storybook", variant="primary")
363
 
364
  with gr.Column():
365
+ batch_status = gr.Textbox(label="Generation Status", interactive=False, lines=6)
366
  batch_gallery = gr.Gallery(label="Storybook Pages", columns=2, height=600)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
367
 
368
  # Connect buttons to functions
369
  generate_btn.click(
370
+ fn=generate_single_page,
371
+ inputs=[prompt_input, story_title_input, text_input, model_choice, style_choice],
372
  outputs=[image_output, status_output]
373
  )
374
 
375
  batch_btn.click(
376
+ fn=generate_full_storybook,
377
+ inputs=[story_title_input, scenes_input, model_choice, style_choice],
378
  outputs=[batch_gallery, batch_status]
379
  )
380
 
381
+ # Mount Gradio app to FastAPI
382
+ app = gr.mount_gradio_app(app, demo, path="/")
383
+
384
+ # For Hugging Face Spaces deployment
385
  def get_app():
386
+ return app
387
 
388
  if __name__ == "__main__":
389
+ print("πŸš€ Starting Storybook Generator API...")
390
+ print("πŸ“š Available models:", list(MODEL_CHOICES.keys()))
391
+ print("🌐 API endpoints:")
392
+ print(" - POST /api/generate-storybook")
393
+ print(" - GET /api/health")
394
+ print(" - GET / (Gradio UI)")
395
+
396
+ import uvicorn
397
+ uvicorn.run(app, host="0.0.0.0", port=7860)