yukee1992 commited on
Commit
b35ca83
Β·
verified Β·
1 Parent(s): 1cdf72b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +252 -63
app.py CHANGED
@@ -23,6 +23,11 @@ import random
23
  # External OCI API URL - YOUR BUCKET SAVING API
24
  OCI_API_BASE_URL = "https://yukee1992-oci-story-book.hf.space"
25
 
 
 
 
 
 
26
  # Initialize FastAPI app
27
  app = FastAPI(title="Storybook Generator API")
28
 
@@ -202,6 +207,87 @@ def generate_high_quality_image(prompt, model_choice="dreamshaper-8", style="chi
202
  print(f"❌ HQ Generation failed: {str(e)}")
203
  raise
204
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
205
  def save_to_oci_bucket(image, text_content, story_title, page_number, file_type="image"):
206
  """Save both images and text to OCI bucket via your OCI API"""
207
  try:
@@ -242,7 +328,7 @@ def save_to_oci_bucket(image, text_content, story_title, page_number, file_type=
242
  except Exception as e:
243
  raise Exception(f"OCI upload failed: {str(e)}")
244
 
245
- # JOB MANAGEMENT FUNCTIONS
246
  def create_job(story_request: StorybookRequest) -> str:
247
  job_id = str(uuid.uuid4())
248
 
@@ -303,7 +389,7 @@ def update_job_status(job_id: str, status: JobStatus, progress: int, message: st
303
 
304
  return True
305
 
306
- # BACKGROUND TASK FOR COMPLETE STORYBOOK GENERATION
307
  def generate_storybook_background(job_id: str):
308
  """Background task to generate complete storybook with images and text"""
309
  try:
@@ -415,7 +501,7 @@ def generate_storybook_background(job_id: str):
415
  print(f"❌ {error_msg}")
416
  update_job_status(job_id, JobStatus.FAILED, 0, error_msg)
417
 
418
- # FASTAPI ENDPOINTS
419
  @app.post("/api/generate-storybook")
420
  async def generate_storybook(request: dict, background_tasks: BackgroundTasks):
421
  """Main endpoint for n8n integration - generates complete storybook"""
@@ -443,7 +529,7 @@ async def generate_storybook(request: dict, background_tasks: BackgroundTasks):
443
  "story_title": story_request.story_title,
444
  "total_scenes": len(story_request.scenes),
445
  "callback_url": story_request.callback_url,
446
- "estimated_time_seconds": len(story_request.scenes) * 30, # ~30s per image
447
  "timestamp": datetime.now().isoformat()
448
  }
449
 
@@ -485,27 +571,31 @@ async def api_health():
485
  "oci_api_connected": OCI_API_BASE_URL
486
  }
487
 
488
- @app.get("/")
489
- async def root():
490
- return {
491
- "message": "Storybook Generator API is running!",
492
- "endpoints": {
493
- "generate_storybook": "POST /api/generate-storybook",
494
- "check_status": "GET /api/job-status/{job_id}",
495
- "health": "GET /api/health"
496
- },
497
- "oci_bucket_api": OCI_API_BASE_URL
498
- }
 
 
 
 
499
 
500
- # GRADIO INTERFACE FOR TESTING
501
  def create_gradio_interface():
502
- """Create Gradio interface for testing image generation"""
503
 
504
  def generate_test_image(prompt, model_choice, style_choice):
505
- """Generate a single image for testing"""
506
  try:
507
  if not prompt.strip():
508
- return None, "❌ Please enter a prompt"
509
 
510
  print(f"🎨 Generating test image with prompt: {prompt}")
511
 
@@ -520,27 +610,81 @@ def create_gradio_interface():
520
  negative_prompt
521
  )
522
 
 
 
 
 
 
 
 
 
523
  status_msg = f"""βœ… Success! Generated: {prompt}
524
 
 
 
525
  🎨 Enhanced prompt: {enhanced_prompt}
526
 
527
- πŸ’‘ This is a test image. For complete storybook generation, use the API endpoint:
528
- POST /api/generate-storybook"""
529
 
530
- return image, status_msg
531
 
532
  except Exception as e:
533
  error_msg = f"❌ Generation failed: {str(e)}"
534
  print(error_msg)
535
- return None, error_msg
536
 
537
- with gr.Blocks(title="Storybook Generator - Test Interface", theme="soft") as demo:
538
- gr.Markdown("# 🎨 Storybook Generator - Test Interface")
539
- gr.Markdown("**Test image generation β€’ Full storybooks via API**")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
540
 
541
  with gr.Row():
542
  with gr.Column(scale=1):
543
- gr.Markdown("### βš™οΈ Test Settings")
544
 
545
  model_dropdown = gr.Dropdown(
546
  label="AI Model",
@@ -562,12 +706,17 @@ POST /api/generate-storybook"""
562
 
563
  generate_btn = gr.Button("✨ Generate Test Image", variant="primary")
564
 
565
- gr.Markdown("### πŸ“š API Usage")
 
 
 
 
 
566
  gr.Markdown("""
567
- **For complete storybooks with multiple images:**
568
- - Use: `POST /api/generate-storybook`
569
- - Send: story_title, scenes[], characters[]
570
- - Returns: job_id for status tracking
571
  """)
572
 
573
  with gr.Column(scale=2):
@@ -597,59 +746,99 @@ POST /api/generate-storybook"""
597
  outputs=prompt_input
598
  )
599
 
600
- # API info section
601
- with gr.Accordion("πŸ”§ API Documentation", open=False):
602
- gr.Markdown("""
603
- ### API Endpoints
604
 
605
- **Generate Storybook**
606
- ```bash
607
- POST /api/generate-storybook
608
- {
609
- "story_title": "Your Story",
610
- "scenes": [
611
- {
612
- "visual": "Scene description",
613
- "text": "Story text"
614
- }
615
- ],
616
- "callback_url": "your-webhook-url"
617
- }
618
- ```
619
 
620
- **Check Status**
621
- ```bash
622
- GET /api/job-status/{job_id}
623
- ```
 
 
 
624
 
625
- **Health Check**
626
- ```bash
627
- GET /api/health
628
- ```
629
- """)
630
 
 
631
  generate_btn.click(
632
  fn=generate_test_image,
633
  inputs=[prompt_input, model_dropdown, style_dropdown],
634
- outputs=[image_output, status_output]
 
 
 
 
 
 
635
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
636
 
637
  return demo
638
 
639
  # Create Gradio app
640
  gradio_app = create_gradio_interface()
641
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
642
  # For Hugging Face Spaces deployment
643
  def get_app():
644
  return app
645
 
646
  if __name__ == "__main__":
647
  import uvicorn
648
- print("πŸš€ Starting Storybook Generator API + Gradio Interface...")
649
- print("πŸ“š Features: Multi-image generation + Text saving + OCI bucket storage")
650
  print("πŸ”— OCI API:", OCI_API_BASE_URL)
 
651
  print("🌐 Web Interface: http://localhost:7860")
652
  print("πŸ”Œ API Endpoints: http://localhost:7860/api/")
653
 
654
- # Launch both FastAPI and Gradio
655
  gradio_app.launch(server_name="0.0.0.0", server_port=7860)
 
23
  # External OCI API URL - YOUR BUCKET SAVING API
24
  OCI_API_BASE_URL = "https://yukee1992-oci-story-book.hf.space"
25
 
26
+ # Create local directories for test images
27
+ PERSISTENT_IMAGE_DIR = "generated_test_images"
28
+ os.makedirs(PERSISTENT_IMAGE_DIR, exist_ok=True)
29
+ print(f"πŸ“ Created local image directory: {PERSISTENT_IMAGE_DIR}")
30
+
31
  # Initialize FastAPI app
32
  app = FastAPI(title="Storybook Generator API")
33
 
 
207
  print(f"❌ HQ Generation failed: {str(e)}")
208
  raise
209
 
210
+ # LOCAL FILE MANAGEMENT FUNCTIONS
211
+ def save_image_to_local(image, prompt, style="test"):
212
+ """Save image to local persistent storage"""
213
+ try:
214
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
215
+ safe_prompt = "".join(c for c in prompt[:50] if c.isalnum() or c in (' ', '-', '_')).rstrip()
216
+ filename = f"image_{safe_prompt}_{timestamp}.png"
217
+
218
+ # Create style subfolder
219
+ style_dir = os.path.join(PERSISTENT_IMAGE_DIR, style)
220
+ os.makedirs(style_dir, exist_ok=True)
221
+ filepath = os.path.join(style_dir, filename)
222
+
223
+ # Save the image
224
+ image.save(filepath)
225
+ print(f"πŸ’Ύ Image saved locally: {filepath}")
226
+
227
+ return filepath, filename
228
+
229
+ except Exception as e:
230
+ print(f"❌ Failed to save locally: {e}")
231
+ return None, None
232
+
233
+ def delete_local_image(filepath):
234
+ """Delete an image from local storage"""
235
+ try:
236
+ if os.path.exists(filepath):
237
+ os.remove(filepath)
238
+ print(f"πŸ—‘οΈ Deleted local image: {filepath}")
239
+ return True, f"βœ… Deleted: {os.path.basename(filepath)}"
240
+ else:
241
+ return False, f"❌ File not found: {filepath}"
242
+ except Exception as e:
243
+ return False, f"❌ Error deleting: {str(e)}"
244
+
245
+ def get_local_storage_info():
246
+ """Get information about local storage usage"""
247
+ try:
248
+ total_size = 0
249
+ file_count = 0
250
+ images_list = []
251
+
252
+ for root, dirs, files in os.walk(PERSISTENT_IMAGE_DIR):
253
+ for file in files:
254
+ if file.endswith(('.png', '.jpg', '.jpeg')):
255
+ filepath = os.path.join(root, file)
256
+ if os.path.exists(filepath):
257
+ file_size = os.path.getsize(filepath)
258
+ total_size += file_size
259
+ file_count += 1
260
+ images_list.append({
261
+ 'path': filepath,
262
+ 'filename': file,
263
+ 'size_kb': round(file_size / 1024, 1),
264
+ 'created': os.path.getctime(filepath)
265
+ })
266
+
267
+ return {
268
+ "total_files": file_count,
269
+ "total_size_mb": round(total_size / (1024 * 1024), 2),
270
+ "images": sorted(images_list, key=lambda x: x['created'], reverse=True)
271
+ }
272
+ except Exception as e:
273
+ return {"error": str(e)}
274
+
275
+ def refresh_local_images():
276
+ """Get list of all locally saved images"""
277
+ try:
278
+ image_files = []
279
+ for root, dirs, files in os.walk(PERSISTENT_IMAGE_DIR):
280
+ for file in files:
281
+ if file.endswith(('.png', '.jpg', '.jpeg')):
282
+ filepath = os.path.join(root, file)
283
+ if os.path.exists(filepath):
284
+ image_files.append(filepath)
285
+ return image_files
286
+ except Exception as e:
287
+ print(f"Error refreshing local images: {e}")
288
+ return []
289
+
290
+ # OCI BUCKET FUNCTIONS (for n8n API)
291
  def save_to_oci_bucket(image, text_content, story_title, page_number, file_type="image"):
292
  """Save both images and text to OCI bucket via your OCI API"""
293
  try:
 
328
  except Exception as e:
329
  raise Exception(f"OCI upload failed: {str(e)}")
330
 
331
+ # JOB MANAGEMENT FUNCTIONS (for n8n API)
332
  def create_job(story_request: StorybookRequest) -> str:
333
  job_id = str(uuid.uuid4())
334
 
 
389
 
390
  return True
391
 
392
+ # BACKGROUND TASK FOR COMPLETE STORYBOOK GENERATION (n8n API)
393
  def generate_storybook_background(job_id: str):
394
  """Background task to generate complete storybook with images and text"""
395
  try:
 
501
  print(f"❌ {error_msg}")
502
  update_job_status(job_id, JobStatus.FAILED, 0, error_msg)
503
 
504
+ # FASTAPI ENDPOINTS (for n8n)
505
  @app.post("/api/generate-storybook")
506
  async def generate_storybook(request: dict, background_tasks: BackgroundTasks):
507
  """Main endpoint for n8n integration - generates complete storybook"""
 
529
  "story_title": story_request.story_title,
530
  "total_scenes": len(story_request.scenes),
531
  "callback_url": story_request.callback_url,
532
+ "estimated_time_seconds": len(story_request.scenes) * 30,
533
  "timestamp": datetime.now().isoformat()
534
  }
535
 
 
571
  "oci_api_connected": OCI_API_BASE_URL
572
  }
573
 
574
+ @app.get("/api/local-images")
575
+ async def get_local_images():
576
+ """API endpoint to get locally saved test images"""
577
+ storage_info = get_local_storage_info()
578
+ return storage_info
579
+
580
+ @app.delete("/api/local-images/{filename:path}")
581
+ async def delete_local_image_api(filename: str):
582
+ """API endpoint to delete a local image"""
583
+ try:
584
+ filepath = os.path.join(PERSISTENT_IMAGE_DIR, filename)
585
+ success, message = delete_local_image(filepath)
586
+ return {"status": "success" if success else "error", "message": message}
587
+ except Exception as e:
588
+ return {"status": "error", "message": str(e)}
589
 
590
+ # GRADIO INTERFACE WITH LOCAL FILE MANAGEMENT
591
  def create_gradio_interface():
592
+ """Create Gradio interface with complete local file management"""
593
 
594
  def generate_test_image(prompt, model_choice, style_choice):
595
+ """Generate a single image for testing and save locally"""
596
  try:
597
  if not prompt.strip():
598
+ return None, "❌ Please enter a prompt", None
599
 
600
  print(f"🎨 Generating test image with prompt: {prompt}")
601
 
 
610
  negative_prompt
611
  )
612
 
613
+ # Save to local storage
614
+ filepath, filename = save_image_to_local(image, prompt, style_choice)
615
+
616
+ if filepath:
617
+ save_info = f"πŸ’Ύ **Saved locally:** `{filepath}`"
618
+ else:
619
+ save_info = "⚠️ Could not save to local storage"
620
+
621
  status_msg = f"""βœ… Success! Generated: {prompt}
622
 
623
+ {save_info}
624
+
625
  🎨 Enhanced prompt: {enhanced_prompt}
626
 
627
+ πŸ“ **Local file:** {filename if filename else 'Not saved'}"""
 
628
 
629
+ return image, status_msg, filepath
630
 
631
  except Exception as e:
632
  error_msg = f"❌ Generation failed: {str(e)}"
633
  print(error_msg)
634
+ return None, error_msg, None
635
 
636
+ def delete_current_image(filepath):
637
+ """Delete the currently displayed image"""
638
+ if not filepath:
639
+ return "❌ No image to delete", None, None, refresh_local_images()
640
+
641
+ success, message = delete_local_image(filepath)
642
+ updated_files = refresh_local_images()
643
+ storage_info = get_local_storage_info()
644
+
645
+ if success:
646
+ status_msg = f"βœ… {message}"
647
+ return status_msg, None, "Image deleted successfully!", updated_files
648
+ else:
649
+ return f"❌ {message}", None, "Delete failed", updated_files
650
+
651
+ def clear_all_images():
652
+ """Delete all local images"""
653
+ try:
654
+ storage_info = get_local_storage_info()
655
+ deleted_count = 0
656
+
657
+ if "images" in storage_info:
658
+ for image_info in storage_info["images"]:
659
+ success, _ = delete_local_image(image_info["path"])
660
+ if success:
661
+ deleted_count += 1
662
+
663
+ updated_files = refresh_local_images()
664
+ return f"βœ… Deleted {deleted_count} images", updated_files
665
+ except Exception as e:
666
+ return f"❌ Error: {str(e)}", refresh_local_images()
667
+
668
+ with gr.Blocks(title="Storybook Generator - Complete Interface", theme="soft") as demo:
669
+ gr.Markdown("# 🎨 Storybook Generator - Complete Interface")
670
+ gr.Markdown("**Test images (local) β€’ Storybooks (OCI) β€’ File management**")
671
+
672
+ # Storage info display
673
+ storage_info = gr.Textbox(
674
+ label="πŸ“Š Local Storage Information",
675
+ interactive=False,
676
+ lines=2
677
+ )
678
+
679
+ def update_storage_info():
680
+ info = get_local_storage_info()
681
+ if "error" not in info:
682
+ return f"πŸ“ Local Storage: {info['total_files']} images, {info['total_size_mb']} MB used"
683
+ return "πŸ“ Local Storage: Unable to calculate"
684
 
685
  with gr.Row():
686
  with gr.Column(scale=1):
687
+ gr.Markdown("### βš™οΈ Test Image Generation")
688
 
689
  model_dropdown = gr.Dropdown(
690
  label="AI Model",
 
706
 
707
  generate_btn = gr.Button("✨ Generate Test Image", variant="primary")
708
 
709
+ # Current image management
710
+ current_file_path = gr.State()
711
+ delete_btn = gr.Button("πŸ—‘οΈ Delete This Image", variant="stop")
712
+ delete_status = gr.Textbox(label="Delete Status", interactive=False, lines=2)
713
+
714
+ gr.Markdown("### πŸ“š API Usage for n8n")
715
  gr.Markdown("""
716
+ **For complete storybooks (OCI bucket):**
717
+ - Endpoint: `POST /api/generate-storybook`
718
+ - Input: `story_title`, `scenes[]`, `characters[]`
719
+ - Output: Saves to OCI bucket automatically
720
  """)
721
 
722
  with gr.Column(scale=2):
 
746
  outputs=prompt_input
747
  )
748
 
749
+ # Local file management section
750
+ with gr.Accordion("πŸ“ Manage Local Test Images", open=True):
751
+ gr.Markdown("### Locally Saved Images")
 
752
 
753
+ with gr.Row():
754
+ refresh_btn = gr.Button("πŸ”„ Refresh List")
755
+ clear_all_btn = gr.Button("πŸ—‘οΈ Clear All Images", variant="stop")
 
 
 
 
 
 
 
 
 
 
 
756
 
757
+ file_gallery = gr.Gallery(
758
+ label="Local Images",
759
+ show_label=True,
760
+ elem_id="gallery",
761
+ columns=4,
762
+ height="auto"
763
+ )
764
 
765
+ clear_status = gr.Textbox(label="Clear Status", interactive=False)
 
 
 
 
766
 
767
+ # Connect events
768
  generate_btn.click(
769
  fn=generate_test_image,
770
  inputs=[prompt_input, model_dropdown, style_dropdown],
771
+ outputs=[image_output, status_output, current_file_path]
772
+ ).then(
773
+ fn=refresh_local_images,
774
+ outputs=file_gallery
775
+ ).then(
776
+ fn=update_storage_info,
777
+ outputs=storage_info
778
  )
779
+
780
+ delete_btn.click(
781
+ fn=delete_current_image,
782
+ inputs=current_file_path,
783
+ outputs=[delete_status, image_output, status_output, file_gallery]
784
+ ).then(
785
+ fn=update_storage_info,
786
+ outputs=storage_info
787
+ )
788
+
789
+ refresh_btn.click(
790
+ fn=refresh_local_images,
791
+ outputs=file_gallery
792
+ ).then(
793
+ fn=update_storage_info,
794
+ outputs=storage_info
795
+ )
796
+
797
+ clear_all_btn.click(
798
+ fn=clear_all_images,
799
+ outputs=[clear_status, file_gallery]
800
+ ).then(
801
+ fn=update_storage_info,
802
+ outputs=storage_info
803
+ )
804
+
805
+ # Initialize on load
806
+ demo.load(fn=refresh_local_images, outputs=file_gallery)
807
+ demo.load(fn=update_storage_info, outputs=storage_info)
808
 
809
  return demo
810
 
811
  # Create Gradio app
812
  gradio_app = create_gradio_interface()
813
 
814
+ @app.get("/")
815
+ async def root():
816
+ return {
817
+ "message": "Storybook Generator API + UI is running!",
818
+ "features": [
819
+ "API: Complete storybook generation to OCI bucket",
820
+ "UI: Test image generation with local file management",
821
+ "File Management: View, download, delete local images"
822
+ ],
823
+ "endpoints": {
824
+ "generate_storybook": "POST /api/generate-storybook",
825
+ "check_status": "GET /api/job-status/{job_id}",
826
+ "local_images": "GET /api/local-images"
827
+ }
828
+ }
829
+
830
  # For Hugging Face Spaces deployment
831
  def get_app():
832
  return app
833
 
834
  if __name__ == "__main__":
835
  import uvicorn
836
+ print("πŸš€ Starting Complete Storybook Generator...")
837
+ print("πŸ“š Features: API (n8n) + UI (Gradio) + Local File Management")
838
  print("πŸ”— OCI API:", OCI_API_BASE_URL)
839
+ print("πŸ“ Local storage:", PERSISTENT_IMAGE_DIR)
840
  print("🌐 Web Interface: http://localhost:7860")
841
  print("πŸ”Œ API Endpoints: http://localhost:7860/api/")
842
 
843
+ # Launch Gradio interface
844
  gradio_app.launch(server_name="0.0.0.0", server_port=7860)