Spaces:
Running
Running
import gradio as gr | |
import random | |
from datetime import datetime | |
import tempfile | |
import os | |
import edge_tts | |
import asyncio | |
import warnings | |
import pytz | |
import re | |
import json | |
import pandas as pd | |
from pathlib import Path | |
from gradio_client import Client | |
warnings.filterwarnings('ignore') | |
# Initialize story starters with added comedy section | |
STORY_STARTERS = [ | |
['Adventure', 'In a hidden temple deep in the Amazon...'], | |
['Mystery', 'The detective found an unusual note...'], | |
['Romance', 'Two strangers meet on a rainy evening...'], | |
['Sci-Fi', 'The space station received an unexpected signal...'], | |
['Fantasy', 'A magical portal appeared in the garden...'], | |
['Comedy-Sitcom', 'The new roommate arrived with seven emotional support animals...'], | |
['Comedy-Workplace', 'The office printer started sending mysterious messages...'], | |
['Comedy-Family', 'Grandma decided to become a social media influencer...'], | |
['Comedy-Supernatural', 'The ghost haunting the house was absolutely terrible at scaring people...'], | |
['Comedy-Travel', 'The GPS insisted on giving directions in interpretive dance descriptions...'] | |
] | |
# Initialize client outside of interface definition | |
arxiv_client = None | |
def init_client(): | |
global arxiv_client | |
if arxiv_client is None: | |
arxiv_client = Client("awacke1/Arxiv-Paper-Search-And-QA-RAG-Pattern") | |
return arxiv_client | |
def save_story(story, audio_path): | |
"""Save story and audio to gallery with markdown formatting""" | |
try: | |
# Create gallery directory if it doesn't exist | |
gallery_dir = Path("gallery") | |
gallery_dir.mkdir(exist_ok=True) | |
# Generate timestamp and sanitize first line for filename | |
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") | |
first_line = story.split('\n')[0].strip() | |
safe_name = re.sub(r'[^\w\s-]', '', first_line)[:50] # First 50 chars, sanitized | |
# Save story text as markdown | |
story_path = gallery_dir / f"story_{timestamp}_{safe_name}.md" | |
with open(story_path, "w") as f: | |
f.write(f"# {first_line}\n\n{story}") | |
# Copy audio file to gallery with matching name | |
new_audio_path = None | |
if audio_path: | |
new_audio_path = gallery_dir / f"audio_{timestamp}_{safe_name}.mp3" | |
os.system(f"cp {audio_path} {str(new_audio_path)}") | |
return str(story_path), str(new_audio_path) if new_audio_path else None | |
except Exception as e: | |
print(f"Error saving to gallery: {str(e)}") | |
return None, None | |
def load_gallery(): | |
"""Load all stories and audio from gallery with markdown support""" | |
try: | |
gallery_dir = Path("gallery") | |
if not gallery_dir.exists(): | |
return [] | |
files = [] | |
for story_file in sorted(gallery_dir.glob("story_*.md"), reverse=True): | |
# Extract timestamp and name from filename | |
parts = story_file.stem.split('_', 2) | |
timestamp = f"{parts[1]}" | |
# Find matching audio file | |
audio_pattern = f"audio_{timestamp}_*.mp3" | |
audio_files = list(gallery_dir.glob(audio_pattern)) | |
audio_file = audio_files[0] if audio_files else None | |
# Read story content and get preview | |
with open(story_file) as f: | |
content = f.read() | |
# Skip markdown header and get preview | |
preview = content.split('\n\n', 1)[1][:100] + "..." | |
files.append([ | |
timestamp, | |
f"[{preview}]({str(story_file)})", # Markdown link to story | |
str(story_file), | |
str(audio_file) if audio_file else None | |
]) | |
return files | |
except Exception as e: | |
print(f"Error loading gallery: {str(e)}") | |
return [] | |
# Keep all other functions unchanged | |
def generate_story(prompt, model_choice): | |
"""Generate story using specified model""" | |
try: | |
client = init_client() | |
if client is None: | |
return "Error: Story generation service is not available." | |
result = client.predict( | |
prompt=prompt, | |
llm_model_picked=model_choice, | |
stream_outputs=True, | |
api_name="/ask_llm" | |
) | |
return result | |
except Exception as e: | |
return f"Error generating story: {str(e)}" | |
async def generate_speech(text, voice="en-US-AriaNeural"): | |
"""Generate speech from text""" | |
try: | |
communicate = edge_tts.Communicate(text, voice) | |
with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as tmp_file: | |
tmp_path = tmp_file.name | |
await communicate.save(tmp_path) | |
return tmp_path | |
except Exception as e: | |
print(f"Error in text2speech: {str(e)}") | |
return None | |
def process_story_and_audio(prompt, model_choice): | |
"""Process story, generate audio, and save to gallery""" | |
try: | |
# Generate story | |
story = generate_story(prompt, model_choice) | |
if isinstance(story, str) and story.startswith("Error"): | |
return story, None, None | |
# Generate audio | |
audio_path = asyncio.run(generate_speech(story)) | |
# Save to gallery | |
story_path, saved_audio_path = save_story(story, audio_path) | |
return story, audio_path, load_gallery() | |
except Exception as e: | |
return f"Error: {str(e)}", None, None | |
def play_gallery_audio(evt: gr.SelectData, gallery_data): | |
"""Play audio from gallery selection""" | |
try: | |
selected_row = gallery_data[evt.index[0]] | |
audio_path = selected_row[3] # Audio path is the fourth element | |
if audio_path and os.path.exists(audio_path): | |
return audio_path | |
return None | |
except Exception as e: | |
print(f"Error playing gallery audio: {str(e)}") | |
return None | |
# Create the Gradio interface (keep unchanged) | |
with gr.Blocks(title="AI Story Generator") as demo: | |
gr.Markdown(""" | |
# π AI Story Generator & Narrator | |
Generate creative stories, listen to them, and build your gallery! | |
""") | |
with gr.Row(): | |
with gr.Column(scale=3): | |
with gr.Row(): | |
prompt_input = gr.Textbox( | |
label="Story Concept", | |
placeholder="Enter your story idea...", | |
lines=3 | |
) | |
with gr.Row(): | |
model_choice = gr.Dropdown( | |
label="Model", | |
choices=[ | |
"mistralai/Mixtral-8x7B-Instruct-v0.1", | |
"mistralai/Mistral-7B-Instruct-v0.2" | |
], | |
value="mistralai/Mixtral-8x7B-Instruct-v0.1" | |
) | |
generate_btn = gr.Button("Generate Story") | |
with gr.Row(): | |
story_output = gr.Textbox( | |
label="Generated Story", | |
lines=10, | |
interactive=False | |
) | |
with gr.Row(): | |
audio_output = gr.Audio( | |
label="Story Narration", | |
type="filepath" | |
) | |
# Sidebar with Story Starters and Gallery | |
with gr.Column(scale=1): | |
gr.Markdown("### π Story Starters") | |
story_starters = gr.Dataframe( | |
value=STORY_STARTERS, | |
headers=["Category", "Starter"], | |
interactive=False | |
) | |
gr.Markdown("### π¬ Gallery") | |
gallery = gr.Dataframe( | |
value=load_gallery(), | |
headers=["Timestamp", "Preview", "Story Path", "Audio Path"], | |
interactive=False | |
) | |
# Event handlers | |
def update_prompt(evt: gr.SelectData): | |
return STORY_STARTERS[evt.index[0]][1] | |
story_starters.select(update_prompt, None, prompt_input) | |
generate_btn.click( | |
fn=process_story_and_audio, | |
inputs=[prompt_input, model_choice], | |
outputs=[story_output, audio_output, gallery] | |
) | |
gallery.select( | |
fn=play_gallery_audio, | |
inputs=[gallery], | |
outputs=[audio_output] | |
) | |
if __name__ == "__main__": | |
demo.launch() |