-
Notifications
You must be signed in to change notification settings - Fork 143
Expand file tree
/
Copy pathutils.py
More file actions
307 lines (237 loc) · 10.5 KB
/
utils.py
File metadata and controls
307 lines (237 loc) · 10.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
"""
Utility functions for the Minions Story Teller app
"""
import re
import json
from typing import Dict, Any, List, Optional
def enhance_image_prompt(chapter_content: str, chapter_title: str, story_theme: str) -> str:
"""
Create an enhanced image prompt based on chapter content and story theme.
Args:
chapter_content: The text content of the chapter
chapter_title: The title of the chapter
story_theme: The overall theme/idea of the story
Returns:
Enhanced image prompt suitable for FLUX.1-schnell
"""
# Analyze chapter content for specific visual elements
content_lower = chapter_content.lower()
# Start with base prompt
prompt_parts = ["Children's book illustration"]
# Identify main character (Whiskers the cat)
main_character = "orange tabby cat"
if 'whiskers' in content_lower:
prompt_parts.append(f"featuring {main_character}")
# Determine scene based on chapter content
scene_description = ""
# Chapter 1 scenes - Morning, window, neighborhood
if any(word in content_lower for word in ['woke up', 'morning', 'window', 'stretched', 'yawned']):
scene_description = "waking up in a cozy bedroom, stretching and looking out a sunny window"
elif any(word in content_lower for word in ['neighborhood', 'decorations', 'flags', 'outside']):
scene_description = "looking out window at a neighborhood decorated with colorful American flags and patriotic bunting"
elif any(word in content_lower for word in ['bella', 'rabbit', 'hopping', 'street']):
scene_description = "meeting a friendly white rabbit on a sunny street with red, white and blue decorations"
# Chapter 2 scenes - Planning, backyard, friends
elif any(word in content_lower for word in ['planning', 'backyard', 'basket', 'decorations']):
scene_description = "in a sunny backyard with colorful balloons and picnic tables, planning a party"
elif any(word in content_lower for word in ['friends', 'gathering', 'max', 'dog']):
scene_description = "gathered with animal friends including a wise old dog and playful rabbit in a backyard"
elif any(word in content_lower for word in ['craft', 'making', 'hats', 'flags']):
scene_description = "doing arts and crafts, making patriotic hats and decorations with friends"
# Chapter 3 scenes - Celebration, sparklers, fireworks
elif any(word in content_lower for word in ['sparkler', 'parade', 'lighting', 'twinkling']):
scene_description = "holding sparklers that create magical twinkling lights in the evening"
elif any(word in content_lower for word in ['fireworks', 'sky', 'night', 'burst']):
scene_description = "watching colorful fireworks bursting in the dark night sky"
elif any(word in content_lower for word in ['celebration', 'party', 'dancing', 'music']):
scene_description = "celebrating at a joyful outdoor party with music and dancing"
# Story time scenes
elif any(word in content_lower for word in ['story', 'listening', 'tree', 'gathered']):
scene_description = "sitting in a circle with friends under a shady tree listening to stories"
# Playing/games scenes
elif any(word in content_lower for word in ['playing', 'tag', 'games', 'running']):
scene_description = "playing tag and games with friends in a colorful backyard"
# Default scene
else:
scene_description = "in a cheerful outdoor setting with patriotic decorations"
# Add the scene description
if scene_description:
prompt_parts.append(scene_description)
# Add 4th of July theme elements if relevant
if any(word in story_theme.lower() for word in ['july', '4th', 'patriotic', 'independence']):
prompt_parts.append("with American flags and red, white, and blue decorations")
# Add other characters mentioned
if 'bella' in content_lower and 'rabbit' in content_lower:
prompt_parts.append("with a friendly white rabbit companion")
if 'max' in content_lower and 'dog' in content_lower:
prompt_parts.append("with a wise golden retriever dog")
# Style specifications - emphasize NO TEXT
style_specs = [
"bright vibrant colors",
"warm sunny lighting",
"cartoon illustration style",
"whimsical and friendly",
"suitable for children ages 4-8",
"storybook art style",
"clean composition",
"cheerful atmosphere",
"NO TEXT OR WORDS visible in the image",
"no speech bubbles or letters"
]
# Combine all parts
prompt = ", ".join(prompt_parts) + ", " + ", ".join(style_specs)
# Ensure it's not too long (FLUX has token limits)
if len(prompt) > 450:
prompt = prompt[:447] + "..."
return prompt
def extract_story_title(story_text: str) -> str:
"""
Extract the story title from the generated text.
Args:
story_text: The full story text
Returns:
Extracted title or a default title
"""
# Look for explicit title patterns
title_patterns = [
r'Title:\s*(.+?)(?:\n|$)',
r'# (.+?)(?:\n|$)',
r'## (.+?)(?:\n|$)',
r'^(.+?)(?:\n|$)', # First line as title
]
for pattern in title_patterns:
match = re.search(pattern, story_text, re.IGNORECASE | re.MULTILINE)
if match:
title = match.group(1).strip()
if title and not title.lower().startswith('chapter'):
return title
return "A Wonderful Story"
def split_into_chapters(story_text: str) -> List[Dict[str, str]]:
"""
Split story text into chapters with titles and content.
Args:
story_text: The full story text
Returns:
List of chapter dictionaries with 'title' and 'content' keys
"""
chapters = []
# Split by chapter markers
chapter_pattern = r'(Chapter\s+\d+[^\n]*)'
parts = re.split(chapter_pattern, story_text, flags=re.IGNORECASE)
current_chapter = None
for i, part in enumerate(parts):
part = part.strip()
if not part:
continue
# Check if this is a chapter title
if re.match(r'Chapter\s+\d+', part, re.IGNORECASE):
# Save previous chapter if exists
if current_chapter and current_chapter['content']:
chapters.append(current_chapter)
# Start new chapter
current_chapter = {
'title': part,
'content': ''
}
elif current_chapter:
# Add content to current chapter
current_chapter['content'] += part + '\n'
# Add the last chapter
if current_chapter and current_chapter['content']:
chapters.append(current_chapter)
# If no chapters found, create chapters from paragraphs
if not chapters:
paragraphs = [p.strip() for p in story_text.split('\n\n') if p.strip()]
if paragraphs:
# Group paragraphs into chapters (2-3 paragraphs per chapter)
chapter_size = max(1, len(paragraphs) // 3)
for i in range(0, len(paragraphs), chapter_size):
chapter_paragraphs = paragraphs[i:i+chapter_size]
chapters.append({
'title': f'Chapter {len(chapters) + 1}',
'content': '\n\n'.join(chapter_paragraphs)
})
return chapters
def clean_story_text(story_text: str) -> str:
"""
Clean and format story text for better presentation.
Args:
story_text: Raw story text
Returns:
Cleaned and formatted story text
"""
# Remove excessive whitespace
story_text = re.sub(r'\n\s*\n', '\n\n', story_text)
story_text = re.sub(r'[ \t]+', ' ', story_text)
# Fix common formatting issues
story_text = story_text.replace('\n\n\n', '\n\n')
story_text = story_text.strip()
return story_text
def validate_story_structure(story_data: Dict[str, Any]) -> bool:
"""
Validate that the story structure is complete and properly formatted.
Args:
story_data: Story data dictionary
Returns:
True if valid, False otherwise
"""
if not isinstance(story_data, dict):
return False
if 'title' not in story_data or not story_data['title']:
return False
if 'chapters' not in story_data or not story_data['chapters']:
return False
chapters = story_data['chapters']
if not isinstance(chapters, list) or len(chapters) < 1:
return False
# Check each chapter
for chapter in chapters:
if not isinstance(chapter, dict):
return False
if 'title' not in chapter or not chapter['title']:
return False
if 'content' not in chapter or not chapter['content']:
return False
return True
def generate_story_metadata(story_data: Dict[str, Any]) -> Dict[str, Any]:
"""
Generate metadata for the story.
Args:
story_data: Story data dictionary
Returns:
Metadata dictionary
"""
if not validate_story_structure(story_data):
return {}
chapters = story_data['chapters']
total_words = sum(len(chapter['content'].split()) for chapter in chapters)
metadata = {
'title': story_data['title'],
'chapter_count': len(chapters),
'total_words': total_words,
'estimated_reading_time': max(1, total_words // 100), # Assume 100 words per minute for children
'suitable_age': '4-8 years',
'genre': 'Children\'s Picture Book'
}
return metadata
def format_story_for_display(story_data: Dict[str, Any]) -> str:
"""
Format story data for display in the UI.
Args:
story_data: Story data dictionary
Returns:
Formatted story string
"""
if not validate_story_structure(story_data):
return "Invalid story structure"
formatted_parts = []
# Add title
formatted_parts.append(f"# {story_data['title']}\n")
# Add chapters
for i, chapter in enumerate(story_data['chapters']):
formatted_parts.append(f"## {chapter['title']}\n")
formatted_parts.append(f"{chapter['content']}\n")
# Add separator between chapters (except for the last one)
if i < len(story_data['chapters']) - 1:
formatted_parts.append("---\n")
return '\n'.join(formatted_parts)