awacke1's picture
Update app.py
e211a6f verified
raw
history blame
7.19 kB
import streamlit as st
import spacy
import random
import re
from gtts import gTTS
from PIL import Image, ImageDraw, ImageFont
import io
import numpy as np
import base64
# Load spaCy model for NLP
nlp = spacy.load("en_core_web_sm")
# Default word lists for storytelling classes
default_word_lists = {
"Characters": ["Hero", "Villain", "Protagonist", "Antagonist", "Wizard", "Knight"],
"Settings": ["Forest", "City", "Castle", "Beach", "Mountain", "Space"],
"Events": ["Adventure", "Romance", "Mystery", "Conflict", "Discovery", "Escape"],
"Objects": ["Sword", "Magic potion", "Treasure chest", "Book", "Ring", "Spaceship"],
"Dialogues": ["\"Hello, how are you?\"", "\"Let's go!\"", "\"Beware the danger!\"", "\"I love you.\""]
}
# Suit properties for narrative flavor
suit_properties = {
"Hearts": "emotional or romantic",
"Diamonds": "wealthy or luxurious",
"Clubs": "conflict or struggle",
"Spades": "mysterious or dangerous"
}
# Sentence templates for story generation
sentence_templates = {
"Characters": "There was a {word} who was {property}.",
"Settings": "The {word} was {property}, stretching far into the distance.",
"Events": "Then, a {property} {word} unfolded.",
"Objects": "A {property} {word} appeared before them.",
"Dialogues": "One of them said, {word}, with a {property} tone."
}
# Function to process user input and augment word lists
def augment_word_lists(user_input):
doc = nlp(user_input)
augmented_lists = {key: list(set(val)) for key, val in default_word_lists.items()} # Start with defaults
# Extract elements from user input
for ent in doc.ents:
if ent.label_ == "PERSON":
augmented_lists["Characters"].append(ent.text)
elif ent.label_ in ["GPE", "LOC"]:
augmented_lists["Settings"].append(ent.text)
for token in doc:
if token.pos_ == "VERB" and token.text not in augmented_lists["Events"]:
augmented_lists["Events"].append(token.text)
elif token.pos_ == "NOUN" and token.text not in augmented_lists["Characters"] + augmented_lists["Settings"]:
augmented_lists["Objects"].append(token.text)
dialogues = re.findall(r'"[^"]*"', user_input)
augmented_lists["Dialogues"].extend(dialogues)
return augmented_lists
# Function to create a 52-card deck
def create_deck():
suits = ["Hearts", "Diamonds", "Clubs", "Spades"]
ranks = list(range(1, 14)) # 1-13 for each suit
deck = [(suit, rank) for suit in suits for rank in ranks]
random.shuffle(deck)
return deck
# Assign cards to classes based on position
def assign_card_to_class(card_index):
if 0 <= card_index < 10:
return "Characters"
elif 10 <= card_index < 20:
return "Settings"
elif 20 <= card_index < 30:
return "Events"
elif 30 <= card_index < 40:
return "Objects"
else:
return "Dialogues"
# Generate a card image with details
def generate_card_image(suit, rank, story_class, word, property):
img = Image.new("RGB", (200, 300), color="white")
draw = ImageDraw.Draw(img)
font = ImageFont.load_default()
draw.text((10, 10), f"{rank} of {suit}", fill="black", font=font)
draw.text((10, 50), f"Class: {story_class}", fill="black", font=font)
draw.text((10, 90), f"Word: {word}", fill="black", font=font)
draw.text((10, 130), f"Property: {property}", fill="black", font=font)
buffer = io.BytesIO()
img.save(buffer, format="PNG")
return buffer.getvalue()
# Generate story sentence based on card
def generate_story_sentence(story_class, word, property):
template = sentence_templates[story_class]
return template.format(word=word, property=property)
# Generate song lyrics from story
def generate_song_lyrics(story_text):
doc = nlp(story_text)
key_elements = [token.text for token in doc if token.pos_ in ["NOUN", "VERB", "ADJ"]][:10] # Top 10 elements
lyrics = " ".join(key_elements) # Simple concatenation for 60-second duration
return lyrics
# Main Streamlit app
def main():
st.title("StoryForge: Card-Based Story Generator")
# User input for story seed
user_input = st.text_area("Paste your story seed here (large text accepted):", height=200)
if "augmented_lists" not in st.session_state:
st.session_state.augmented_lists = default_word_lists
if "deck" not in st.session_state:
st.session_state.deck = create_deck()
if "story" not in st.session_state:
st.session_state.story = []
if "drawn_cards" not in st.session_state:
st.session_state.drawn_cards = 0
# Process input and augment word lists
if st.button("Process Input"):
if user_input:
st.session_state.augmented_lists = augment_word_lists(user_input)
st.session_state.deck = create_deck() # Reset deck
st.session_state.story = []
st.session_state.drawn_cards = 0
st.success("Input processed! Ready to draw cards.")
# Draw card button
if st.button("Draw a Card") and st.session_state.drawn_cards < 52:
card_index = st.session_state.drawn_cards
suit, rank = st.session_state.deck[card_index]
story_class = assign_card_to_class(card_index)
word_list = st.session_state.augmented_lists[story_class]
word = random.choice(word_list)
property = suit_properties[suit]
# Generate and display card image
card_image = generate_card_image(suit, rank, story_class, word, property)
st.image(card_image, caption=f"Card {card_index + 1}")
# Generate and append story sentence
sentence = generate_story_sentence(story_class, word, property)
st.session_state.story.append(sentence)
st.session_state.drawn_cards += 1
# Display current story
st.write("### Current Story")
st.write("\n".join(st.session_state.story))
# Generate song when deck is fully drawn
if st.session_state.drawn_cards == 52:
st.write("### Full Story Generated!")
full_story = "\n".join(st.session_state.story)
st.write(full_story)
# Generate and play song
lyrics = generate_song_lyrics(full_story)
st.write("### Song Lyrics")
st.write(lyrics)
tts = gTTS(text=lyrics, lang="en")
audio_file = "song.mp3"
tts.save(audio_file)
audio_bytes = open(audio_file, "rb").read()
st.audio(audio_bytes, format="audio/mp3")
# Basic p5.js integration for interactivity (placeholder)
st.components.v1.html("""
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.2/p5.min.js"></script>
<div id="sketch-holder"></div>
<script>
function setup() {
let canvas = createCanvas(400, 100);
canvas.parent('sketch-holder');
background(220);
textSize(16);
text('Draw cards to build your story!', 10, 50);
}
</script>
""", height=120)
if __name__ == "__main__":
main()