Spaces:
Sleeping
Sleeping
# storyverse_weaver/core/story_engine.py | |
from PIL import Image # Make sure Pillow is in requirements.txt | |
class Scene: | |
def __init__(self, scene_number: int, user_prompt: str, narrative_text: str = None, | |
image: Image.Image = None, image_style_prompt: str = None, | |
image_provider: str = None, error_message: str = None): # Added error_message here | |
self.scene_number = scene_number | |
self.user_prompt = user_prompt | |
self.narrative_text = narrative_text | |
self.image_style_prompt = image_style_prompt | |
self.image = image | |
self.image_provider = image_provider | |
self.error_message = error_message # Store errors specific to this scene | |
def __str__(self): | |
img_status = "Yes" if self.image else ("Error" if self.error_message and not self.image else "No") | |
narr_status = str(self.narrative_text)[:50] if self.narrative_text and "Error" not in self.narrative_text else ("Error" if "Error" in str(self.narrative_text) else "N/A") | |
return f"Scene {self.scene_number}: {self.user_prompt[:50]}... Narrative: {narr_status}... Image: {img_status}" | |
class Story: | |
def __init__(self, title: str = "Untitled StoryVerse"): | |
self.title = title | |
self.scenes: list[Scene] = [] | |
self.current_scene_number = 0 | |
self.global_style_prompt: str = None | |
self.global_negative_prompt: str = None | |
def add_scene_from_elements(self, user_prompt: str, narrative_text: str, | |
image: Image.Image, image_style_prompt: str, | |
image_provider: str, error_message: str = None) -> Scene: # Added error_message | |
self.current_scene_number += 1 | |
scene = Scene( | |
scene_number=self.current_scene_number, | |
user_prompt=user_prompt, | |
narrative_text=narrative_text, | |
image=image, | |
image_style_prompt=image_style_prompt, | |
image_provider=image_provider, | |
error_message=error_message # Store any error that occurred during this scene's generation | |
) | |
self.scenes.append(scene) | |
print(f"DEBUG: story_engine.py - Added scene {self.current_scene_number}: {scene.user_prompt[:30]}") | |
return scene | |
# This method might be redundant if add_scene_from_elements can handle errors | |
# def add_scene_with_error(self, user_prompt: str, error_message: str) -> Scene: | |
# self.current_scene_number += 1 | |
# scene = Scene(scene_number=self.current_scene_number, user_prompt=user_prompt, error_message=error_message) | |
# self.scenes.append(scene) | |
# print(f"DEBUG: story_engine.py - Added scene {self.current_scene_number} WITH ERROR: {error_message}") | |
# return scene | |
def get_last_scene_narrative(self) -> str: | |
if self.scenes and self.scenes[-1].narrative_text and "Error" not in self.scenes[-1].narrative_text: | |
return self.scenes[-1].narrative_text | |
return "" # Return empty if last scene had error or no narrative | |
def get_all_scenes_for_gallery_display(self) -> list[tuple[Image.Image, str]]: | |
"""Prepares scenes for Gradio Gallery: list of (image, caption)""" | |
gallery_items = [] | |
if not self.scenes: | |
# Return a placeholder if no scenes, so gallery doesn't error out | |
placeholder_img = Image.new('RGB', (100,100), color='lightgrey') | |
gallery_items.append((placeholder_img, "No scenes yet. Weave your story!")) | |
return gallery_items | |
for scene in self.scenes: | |
caption = f"S{scene.scene_number}: {scene.user_prompt[:40]}..." | |
if scene.image_style_prompt: caption += f"\nStyle: {scene.image_style_prompt}" | |
if scene.error_message: | |
# Create a placeholder image for errors | |
# error_img = Image.new('RGB', (100,100), color='#FFCCCB') # Light red for error | |
# For gallery, might be better to use None for image if error occurred, or a default "error" image | |
# Gradio gallery might handle None gracefully, or you might need a placeholder PIL image. | |
# Let's try None and see how Gradio handles it. If it errors, use a placeholder image. | |
gallery_items.append((None, f"{caption}\n⚠️ Error: {scene.error_message[:100]}...")) | |
elif scene.image: | |
gallery_items.append((scene.image, caption)) | |
else: # No image, but no specific error reported for the image part of this scene | |
placeholder_img = Image.new('RGB', (100,100), color='whitesmoke') | |
gallery_items.append((placeholder_img, f"{caption}\n(No image generated for this scene)")) | |
return gallery_items | |
def get_latest_scene_details_for_display(self) -> tuple[Image.Image, str]: | |
"""Returns (PIL.Image or None, markdown_string_for_narrative) for the latest scene.""" | |
if not self.scenes: | |
return None, "No scenes yet. Describe your first scene idea above!" | |
ls = self.scenes[-1] | |
latest_image = ls.image if ls.image else None # Will be None if error or no image | |
narrative_display = f"## Scene {ls.scene_number}: {ls.user_prompt}\n\n" | |
if ls.error_message: | |
narrative_display += f"<p style='color:red;'><strong>Generation Error for this scene:</strong><br>{ls.error_message}</p>\n" | |
if ls.image and not ls.error_message : # Only show style if image was successful | |
narrative_display += f"**Style:** {ls.image_style_prompt}\n\n" | |
if ls.narrative_text: | |
if "Error" in ls.narrative_text: # Check if narrative itself is an error message | |
narrative_display += f"<p style='color:orange;'>{ls.narrative_text}</p>" | |
else: | |
narrative_display += ls.narrative_text | |
elif not ls.error_message: # No narrative but no major scene error | |
narrative_display += "(No narrative generated for this scene)." | |
return latest_image, narrative_display | |
def clear_story(self): | |
self.scenes = [] | |
self.current_scene_number = 0 | |
print("DEBUG: story_engine.py - Story cleared.") | |
print("DEBUG: core.story_engine (for StoryVerseWeaver) - Module defined.") |