Spaces:
Sleeping
Sleeping
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() |