Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -1,225 +1,191 @@
|
|
1 |
import streamlit as st
|
2 |
-
import
|
3 |
-
import
|
|
|
|
|
|
|
|
|
|
|
|
|
4 |
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
st.title("🎭 The Art of Storytelling: Master Guide 🎭")
|
9 |
-
|
10 |
-
# -----------------------------
|
11 |
-
# 1. The Storyteller's Song
|
12 |
-
# -----------------------------
|
13 |
-
st.markdown("## 🎵 The Storyteller's Journey Song 🎵")
|
14 |
-
st.markdown("""
|
15 |
-
**Verse 1 – Location**
|
16 |
-
*I'm in a quiet town, where twilight meets the dawn,*
|
17 |
-
*A simple street and weathered walls, where whispered dreams are drawn.*
|
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 |
-
# 2. The Storytelling Points Outline
|
44 |
-
# -----------------------------
|
45 |
-
storytelling_points = [
|
46 |
-
{"number": 1, "emoji": "🏠", "keyword": "location", "description": "Set a scene that sparks the imagination: a place that's simply stated but vividly felt."},
|
47 |
-
{"number": 2, "emoji": "🏃", "keyword": "actions", "description": "Show the kinetic energy—whether it’s walking, biking, or a spontaneous burst of movement."},
|
48 |
-
{"number": 3, "emoji": "🧠", "keyword": "thoughts", "description": "Reveal those raw, neurotic inner monologues that make you real and relatable."},
|
49 |
-
{"number": 4, "emoji": "😲", "keyword": "emotions", "description": "Let your body speak the language of your feelings—every twitch and tear is a story."},
|
50 |
-
{"number": 5, "emoji": "💬", "keyword": "dialogue", "description": "Capture crisp, memorable dialogue that slices through the moment like lightning."},
|
51 |
-
# Additional points can be used for further storytelling techniques…
|
52 |
-
]
|
53 |
-
|
54 |
-
# -----------------------------
|
55 |
-
# 3. Random Literary Wit – Ten Poetic Lines
|
56 |
-
# -----------------------------
|
57 |
-
random_poetry = [
|
58 |
-
"🌅 Location: Where the horizon kisses the edge of dreams. 🌌",
|
59 |
-
"🚴 Action: Pedaling through the city's heartbeat, every turn a burst of rhythm. 🎶",
|
60 |
-
"💭 Thoughts: Whispering secrets of a chaotic mind under a tranquil sky. 🌒",
|
61 |
-
"😊 Emotions: A smile that trembles like morning dew on a rose. 🌈",
|
62 |
-
"🗣 Dialogue: Words crackling like fire in the midnight hush. 🔥",
|
63 |
-
"🌲 Nature: The forest murmurs ancient tales to those who dare to listen. 🍃",
|
64 |
-
"🌊 Waves: Dancing to the eternal hymn of the deep blue. 🐚",
|
65 |
-
"🌟 Dreams: Starlight scattered over the canvas of restless nights. ✨",
|
66 |
-
"🔥 Passion: A flame burning fierce in the silence of despair. ❤️",
|
67 |
-
"🍂 Reflection: Leaves falling like whispered memories in autumn’s embrace. 🕊️"
|
68 |
-
]
|
69 |
-
|
70 |
-
st.markdown("## ✨ Random Literary Wit")
|
71 |
-
st.code("\n".join(random_poetry), language="python")
|
72 |
-
|
73 |
-
# -----------------------------
|
74 |
-
# 4. Interactive Story Element Explorer (Vertical Flow and Tabs)
|
75 |
-
# -----------------------------
|
76 |
-
st.markdown("## 📚 The Art of Storytelling: 5 Essential Elements")
|
77 |
-
|
78 |
-
st.markdown("> \"The universe is made of stories, not of atoms.\" — Muriel Rukeyser")
|
79 |
-
|
80 |
-
for point in storytelling_points:
|
81 |
-
st.markdown(f"{point['number']}. {point['emoji']} **{point['keyword'].capitalize()}** - {point['description']}")
|
82 |
-
|
83 |
-
# -----------------------------
|
84 |
-
# 5. Star Layout for the Five Pillars
|
85 |
-
# -----------------------------
|
86 |
-
st.markdown("## ⭐ Five Pillars in a Star Layout ⭐")
|
87 |
-
|
88 |
-
# Using absolute positioning in a relative container to arrange five points on a circle
|
89 |
-
# Calculated using basic trigonometry (center = 200, radius = 150)
|
90 |
-
star_html = """
|
91 |
-
<div style="position: relative; width: 400px; height: 400px; margin: auto; border: 1px dashed #aaa; border-radius: 50%;">
|
92 |
-
<!-- Point 1: Location at angle 90° -->
|
93 |
-
<div style="position: absolute; top: 50px; left: 200px; transform: translate(-50%, -50%); text-align: center;">
|
94 |
-
<div style="font-size: 2em;">🏠</div>
|
95 |
-
<div><b>Location</b></div>
|
96 |
-
</div>
|
97 |
-
<!-- Point 2: Actions at angle ~162° -->
|
98 |
-
<div style="position: absolute; top: 154px; left: 57px; transform: translate(-50%, -50%); text-align: center;">
|
99 |
-
<div style="font-size: 2em;">🏃</div>
|
100 |
-
<div><b>Actions</b></div>
|
101 |
-
</div>
|
102 |
-
<!-- Point 3: Thoughts at angle ~234° -->
|
103 |
-
<div style="position: absolute; top: 321px; left: 112px; transform: translate(-50%, -50%); text-align: center;">
|
104 |
-
<div style="font-size: 2em;">🧠</div>
|
105 |
-
<div><b>Thoughts</b></div>
|
106 |
-
</div>
|
107 |
-
<!-- Point 4: Emotions at angle ~306° -->
|
108 |
-
<div style="position: absolute; top: 321px; left: 288px; transform: translate(-50%, -50%); text-align: center;">
|
109 |
-
<div style="font-size: 2em;">😲</div>
|
110 |
-
<div><b>Emotions</b></div>
|
111 |
-
</div>
|
112 |
-
<!-- Point 5: Dialogue at angle ~18° -->
|
113 |
-
<div style="position: absolute; top: 154px; left: 343px; transform: translate(-50%, -50%); text-align: center;">
|
114 |
-
<div style="font-size: 2em;">💬</div>
|
115 |
-
<div><b>Dialogue</b></div>
|
116 |
-
</div>
|
117 |
-
</div>
|
118 |
-
"""
|
119 |
-
components.html(star_html, height=450)
|
120 |
-
|
121 |
-
# -----------------------------
|
122 |
-
# 6. Additional Interactive Elements (Tabs & Dropdown)
|
123 |
-
# -----------------------------
|
124 |
-
tab1, tab2, tab3 = st.tabs(["Vertical Flow", "Circular Layout", "Categorical Groups"])
|
125 |
-
|
126 |
-
with tab1:
|
127 |
-
st.markdown("### Vertical Flow Diagram")
|
128 |
-
mermaid_vertical = generate_vertical_diagram(storytelling_points)
|
129 |
-
st.markdown(mermaid_vertical, unsafe_allow_html=True)
|
130 |
-
|
131 |
-
with tab2:
|
132 |
-
st.markdown("### Circular Storytelling Process")
|
133 |
-
mermaid_circular = generate_circular_diagram(storytelling_points)
|
134 |
-
st.markdown(mermaid_circular, unsafe_allow_html=True)
|
135 |
-
|
136 |
-
with tab3:
|
137 |
-
st.markdown("### Categorical Groups")
|
138 |
-
mermaid_categories = generate_categorical_diagram(storytelling_points)
|
139 |
-
st.markdown(mermaid_categories, unsafe_allow_html=True)
|
140 |
-
|
141 |
-
st.markdown("## 🔍 Explore Individual Storytelling Elements")
|
142 |
-
col1, col2 = st.columns([1, 3])
|
143 |
-
|
144 |
-
with col1:
|
145 |
-
options = [f"{point['number']}. {point['emoji']} {point['keyword'].capitalize()}" for point in storytelling_points]
|
146 |
-
selected_option = st.selectbox("Select an element to explore:", options)
|
147 |
-
selected_number = int(selected_option.split('.')[0])
|
148 |
-
selected_point = next(point for point in storytelling_points if point['number'] == selected_number)
|
149 |
-
|
150 |
-
with col2:
|
151 |
-
st.markdown(f"### {selected_option}")
|
152 |
-
st.markdown(f"**Description:** {selected_point['description']}")
|
153 |
-
examples = {
|
154 |
-
1: "'In the stillness of a small town, I take in the crisp air...'",
|
155 |
-
2: "'I lace up my sneakers and step boldly into the unknown, each stride a heartbeat.'",
|
156 |
-
3: "'I muse: What if these fleeting moments are the stitches of my very soul?'",
|
157 |
-
4: "'My eyes shimmer with unspoken stories, my face a canvas of turbulent joy.'",
|
158 |
-
5: "'Then, a voice cuts through the silence: ‘Keep pushing, the dawn awaits!’'"
|
159 |
-
}
|
160 |
-
st.markdown(f"**Example:** {examples.get(selected_number, 'No example available.')}")
|
161 |
-
|
162 |
-
st.markdown("> \"There is no greater agony than bearing an untold story inside you.\" — Maya Angelou")
|
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 |
-
diagram.extend(styles)
|
192 |
-
diagram.append("```")
|
193 |
-
return "\n".join(diagram)
|
194 |
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
node_id = point['number']
|
200 |
-
node_label = f"{node_id}. {point['emoji']} {point['keyword'].capitalize()}"
|
201 |
-
diagram.append(f" {node_id}[\"{node_label}\"]")
|
202 |
-
diagram.extend([
|
203 |
-
" 1 --> 2 --> 3 --> 4 --> 5 --> 1",
|
204 |
-
" %% Style definitions",
|
205 |
-
" classDef q1 fill:#ff9900,stroke:#333,stroke-width:2px,color:#000;",
|
206 |
-
" class 1,2,3,4,5 q1;"
|
207 |
-
])
|
208 |
-
diagram.append("```")
|
209 |
-
return "\n".join(diagram)
|
210 |
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
223 |
|
224 |
if __name__ == "__main__":
|
225 |
-
main()
|
|
|
1 |
import streamlit as st
|
2 |
+
import spacy
|
3 |
+
import random
|
4 |
+
import re
|
5 |
+
from gtts import gTTS
|
6 |
+
from PIL import Image, ImageDraw, ImageFont
|
7 |
+
import io
|
8 |
+
import numpy as np
|
9 |
+
import base64
|
10 |
|
11 |
+
# Load spaCy model for NLP
|
12 |
+
nlp = spacy.load("en_core_web_sm")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
13 |
|
14 |
+
# Default word lists for storytelling classes
|
15 |
+
default_word_lists = {
|
16 |
+
"Characters": ["Hero", "Villain", "Protagonist", "Antagonist", "Wizard", "Knight"],
|
17 |
+
"Settings": ["Forest", "City", "Castle", "Beach", "Mountain", "Space"],
|
18 |
+
"Events": ["Adventure", "Romance", "Mystery", "Conflict", "Discovery", "Escape"],
|
19 |
+
"Objects": ["Sword", "Magic potion", "Treasure chest", "Book", "Ring", "Spaceship"],
|
20 |
+
"Dialogues": ["\"Hello, how are you?\"", "\"Let's go!\"", "\"Beware the danger!\"", "\"I love you.\""]
|
21 |
+
}
|
22 |
|
23 |
+
# Suit properties for narrative flavor
|
24 |
+
suit_properties = {
|
25 |
+
"Hearts": "emotional or romantic",
|
26 |
+
"Diamonds": "wealthy or luxurious",
|
27 |
+
"Clubs": "conflict or struggle",
|
28 |
+
"Spades": "mysterious or dangerous"
|
29 |
+
}
|
30 |
|
31 |
+
# Sentence templates for story generation
|
32 |
+
sentence_templates = {
|
33 |
+
"Characters": "There was a {word} who was {property}.",
|
34 |
+
"Settings": "The {word} was {property}, stretching far into the distance.",
|
35 |
+
"Events": "Then, a {property} {word} unfolded.",
|
36 |
+
"Objects": "A {property} {word} appeared before them.",
|
37 |
+
"Dialogues": "One of them said, {word}, with a {property} tone."
|
38 |
+
}
|
39 |
|
40 |
+
# Function to process user input and augment word lists
|
41 |
+
def augment_word_lists(user_input):
|
42 |
+
doc = nlp(user_input)
|
43 |
+
augmented_lists = {key: list(set(val)) for key, val in default_word_lists.items()} # Start with defaults
|
44 |
+
|
45 |
+
# Extract elements from user input
|
46 |
+
for ent in doc.ents:
|
47 |
+
if ent.label_ == "PERSON":
|
48 |
+
augmented_lists["Characters"].append(ent.text)
|
49 |
+
elif ent.label_ in ["GPE", "LOC"]:
|
50 |
+
augmented_lists["Settings"].append(ent.text)
|
51 |
+
|
52 |
+
for token in doc:
|
53 |
+
if token.pos_ == "VERB" and token.text not in augmented_lists["Events"]:
|
54 |
+
augmented_lists["Events"].append(token.text)
|
55 |
+
elif token.pos_ == "NOUN" and token.text not in augmented_lists["Characters"] + augmented_lists["Settings"]:
|
56 |
+
augmented_lists["Objects"].append(token.text)
|
57 |
+
|
58 |
+
dialogues = re.findall(r'"[^"]*"', user_input)
|
59 |
+
augmented_lists["Dialogues"].extend(dialogues)
|
60 |
+
|
61 |
+
return augmented_lists
|
62 |
|
63 |
+
# Function to create a 52-card deck
|
64 |
+
def create_deck():
|
65 |
+
suits = ["Hearts", "Diamonds", "Clubs", "Spades"]
|
66 |
+
ranks = list(range(1, 14)) # 1-13 for each suit
|
67 |
+
deck = [(suit, rank) for suit in suits for rank in ranks]
|
68 |
+
random.shuffle(deck)
|
69 |
+
return deck
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
70 |
|
71 |
+
# Assign cards to classes based on position
|
72 |
+
def assign_card_to_class(card_index):
|
73 |
+
if 0 <= card_index < 10:
|
74 |
+
return "Characters"
|
75 |
+
elif 10 <= card_index < 20:
|
76 |
+
return "Settings"
|
77 |
+
elif 20 <= card_index < 30:
|
78 |
+
return "Events"
|
79 |
+
elif 30 <= card_index < 40:
|
80 |
+
return "Objects"
|
81 |
+
else:
|
82 |
+
return "Dialogues"
|
83 |
+
|
84 |
+
# Generate a card image with details
|
85 |
+
def generate_card_image(suit, rank, story_class, word, property):
|
86 |
+
img = Image.new("RGB", (200, 300), color="white")
|
87 |
+
draw = ImageDraw.Draw(img)
|
88 |
+
font = ImageFont.load_default()
|
89 |
+
|
90 |
+
draw.text((10, 10), f"{rank} of {suit}", fill="black", font=font)
|
91 |
+
draw.text((10, 50), f"Class: {story_class}", fill="black", font=font)
|
92 |
+
draw.text((10, 90), f"Word: {word}", fill="black", font=font)
|
93 |
+
draw.text((10, 130), f"Property: {property}", fill="black", font=font)
|
94 |
+
|
95 |
+
buffer = io.BytesIO()
|
96 |
+
img.save(buffer, format="PNG")
|
97 |
+
return buffer.getvalue()
|
|
|
|
|
|
|
98 |
|
99 |
+
# Generate story sentence based on card
|
100 |
+
def generate_story_sentence(story_class, word, property):
|
101 |
+
template = sentence_templates[story_class]
|
102 |
+
return template.format(word=word, property=property)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
103 |
|
104 |
+
# Generate song lyrics from story
|
105 |
+
def generate_song_lyrics(story_text):
|
106 |
+
doc = nlp(story_text)
|
107 |
+
key_elements = [token.text for token in doc if token.pos_ in ["NOUN", "VERB", "ADJ"]][:10] # Top 10 elements
|
108 |
+
lyrics = " ".join(key_elements) # Simple concatenation for 60-second duration
|
109 |
+
return lyrics
|
110 |
+
|
111 |
+
# Main Streamlit app
|
112 |
+
def main():
|
113 |
+
st.title("StoryForge: Card-Based Story Generator")
|
114 |
+
|
115 |
+
# User input for story seed
|
116 |
+
user_input = st.text_area("Paste your story seed here (large text accepted):", height=200)
|
117 |
+
|
118 |
+
if "augmented_lists" not in st.session_state:
|
119 |
+
st.session_state.augmented_lists = default_word_lists
|
120 |
+
if "deck" not in st.session_state:
|
121 |
+
st.session_state.deck = create_deck()
|
122 |
+
if "story" not in st.session_state:
|
123 |
+
st.session_state.story = []
|
124 |
+
if "drawn_cards" not in st.session_state:
|
125 |
+
st.session_state.drawn_cards = 0
|
126 |
+
|
127 |
+
# Process input and augment word lists
|
128 |
+
if st.button("Process Input"):
|
129 |
+
if user_input:
|
130 |
+
st.session_state.augmented_lists = augment_word_lists(user_input)
|
131 |
+
st.session_state.deck = create_deck() # Reset deck
|
132 |
+
st.session_state.story = []
|
133 |
+
st.session_state.drawn_cards = 0
|
134 |
+
st.success("Input processed! Ready to draw cards.")
|
135 |
+
|
136 |
+
# Draw card button
|
137 |
+
if st.button("Draw a Card") and st.session_state.drawn_cards < 52:
|
138 |
+
card_index = st.session_state.drawn_cards
|
139 |
+
suit, rank = st.session_state.deck[card_index]
|
140 |
+
story_class = assign_card_to_class(card_index)
|
141 |
+
word_list = st.session_state.augmented_lists[story_class]
|
142 |
+
word = random.choice(word_list)
|
143 |
+
property = suit_properties[suit]
|
144 |
+
|
145 |
+
# Generate and display card image
|
146 |
+
card_image = generate_card_image(suit, rank, story_class, word, property)
|
147 |
+
st.image(card_image, caption=f"Card {card_index + 1}")
|
148 |
+
|
149 |
+
# Generate and append story sentence
|
150 |
+
sentence = generate_story_sentence(story_class, word, property)
|
151 |
+
st.session_state.story.append(sentence)
|
152 |
+
st.session_state.drawn_cards += 1
|
153 |
+
|
154 |
+
# Display current story
|
155 |
+
st.write("### Current Story")
|
156 |
+
st.write("\n".join(st.session_state.story))
|
157 |
+
|
158 |
+
# Generate song when deck is fully drawn
|
159 |
+
if st.session_state.drawn_cards == 52:
|
160 |
+
st.write("### Full Story Generated!")
|
161 |
+
full_story = "\n".join(st.session_state.story)
|
162 |
+
st.write(full_story)
|
163 |
+
|
164 |
+
# Generate and play song
|
165 |
+
lyrics = generate_song_lyrics(full_story)
|
166 |
+
st.write("### Song Lyrics")
|
167 |
+
st.write(lyrics)
|
168 |
+
|
169 |
+
tts = gTTS(text=lyrics, lang="en")
|
170 |
+
audio_file = "song.mp3"
|
171 |
+
tts.save(audio_file)
|
172 |
+
audio_bytes = open(audio_file, "rb").read()
|
173 |
+
st.audio(audio_bytes, format="audio/mp3")
|
174 |
+
|
175 |
+
# Basic p5.js integration for interactivity (placeholder)
|
176 |
+
st.components.v1.html("""
|
177 |
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.2/p5.min.js"></script>
|
178 |
+
<div id="sketch-holder"></div>
|
179 |
+
<script>
|
180 |
+
function setup() {
|
181 |
+
let canvas = createCanvas(400, 100);
|
182 |
+
canvas.parent('sketch-holder');
|
183 |
+
background(220);
|
184 |
+
textSize(16);
|
185 |
+
text('Draw cards to build your story!', 10, 50);
|
186 |
+
}
|
187 |
+
</script>
|
188 |
+
""", height=120)
|
189 |
|
190 |
if __name__ == "__main__":
|
191 |
+
main()
|