UpVoice / src /streamlit_app.py
breddaz's picture
fix union type error for python < 3.12
3901002
import streamlit as st
import asyncio
from dotenv import load_dotenv
load_dotenv()
from typing import Union
from config import languages, voice_names, default_speaker_1, default_speaker_2, articles
from script_generation import MarkdownToScrip, ScriptConfig, PodScript
from audio_generation import ScriptToAudio, AudioConfig
# Generators config
script_config = ScriptConfig()
audio_config = AudioConfig()
# UI initial state
for key in ["generation_started", "script_ready", "audio_ready", "pending_generation", "audio_generation_started"]:
st.session_state.setdefault(key, False)
# Async helpers
async def generate_script(article: str, language: str, voice1: str, voice2: str):
generator = MarkdownToScrip(script_config)
return await generator.run(article, language, voice1, voice2)
async def generate_audio(script: PodScript, voice1: str, voice2: str):
generator = ScriptToAudio(audio_config)
return await generator.run(script, voice1, voice2)
# Sync wrappers for Streamlit
def generate_script_sync(article: str, language: str, voice1: str, voice2: str) -> Union[PodScript, None]:
return asyncio.run(generate_script(article, language, voice1, voice2))
def generate_audio_sync(script: PodScript, voice1: str, voice2: str):
return asyncio.run(generate_audio(script,voice1, voice2))
def render_form():
"""Render the form for article selection and voice configuration."""
with st.form("generation_form"):
article = st.selectbox("Article", options=articles, format_func=lambda x: x[0])
language = st.selectbox("Langue de sortie", languages, index=0)
voice1 = st.selectbox("Voix Speaker 1", voice_names, index=default_speaker_1)
voice2 = st.selectbox("Voix Speaker 2", voice_names, index=default_speaker_2)
submitted = st.form_submit_button("📜 Générer le script")
if submitted:
st.session_state["generation_started"] = True
st.session_state["pending_generation"] = True
st.session_state["article"] = article[1]
st.session_state["language"] = language
st.session_state["voice1"] = voice1
st.session_state["voice2"] = voice2
st.rerun()
def render_script_panel():
"""Render the panel displaying the generated podcast script."""
st.markdown("### 🎧 Script du podcast")
podscript: PodScript = st.session_state["script"]
html = """
<div style='
height: 400px;
overflow-y: auto;
padding: 1em;
border: 1px solid #ccc;
border-radius: 8px;
margin-bottom: 1em;
'>
"""
for line in podscript.conversation:
html += f"<p><strong>{line.speaker}</strong> : {line.text}</p>"
html += "</div>"
st.markdown(html, unsafe_allow_html=True)
def render_audio_panel():
"""Render the panel for audio generation and playback."""
is_generating = st.session_state.get("audio_generation_started", False)
is_done = st.session_state.get("audio_ready", False)
# Disable the button if already generating or done
generate_audio_disabled = is_generating or is_done
# Display the button to generate audio
col1, col2, col3 = st.columns([1, 2, 1])
with col2:
clicked = st.button("🎙️ Générer l'audio", key="generate_audio_btn", disabled=generate_audio_disabled)
if clicked:
st.session_state["audio_generation_started"] = True
st.rerun()
# Generate audio if not already done
if is_generating and not is_done:
with st.spinner("Génération de l’audio en cours...", show_time=True):
st.markdown("""
> Cela peut prendre un minute, merci de patienter 🙂.
""")
audio_path = generate_audio_sync(
st.session_state["script"],
st.session_state["voice1"],
st.session_state["voice2"],
)
st.session_state["audio_path"] = audio_path
st.session_state["audio_ready"] = True
st.session_state["audio_generation_started"] = False
st.rerun()
if is_done:
st.markdown("### 🔊 Podcast généré")
st.audio(st.session_state["audio_path"])
def render_sidebar():
with st.sidebar:
st.title("🎙️ UpVoice")
st.markdown("Génèrez un podcast à partir d’un article tech")
content = """
* Pour le moment la génération est limitée à un podscast de ~3 minutes.
* L'audio est générée via [ gpt4o-mini-tts](https://platform.openai.com/docs/models/gpt-4o-mini-tts), d'autres modèles de génération seront bientôt proposés.
* N'hésitez pas à visiter 👉 [Le Blog by Younup](https://www.younup.fr/blog) pour plus de contenu tech !.
"""
st.markdown(f"""
<div style="
border: 1px solid rgba(250, 250, 250, 0.2);
border-radius: 10px;
padding: 1em;
box-shadow: 1px 1px 5px rgba(0,0,0,0.05);
margin-bottom: 1em;
">
<p style="margin: 0;">{content}</p>
</div>
""", unsafe_allow_html=True)
# Reset
if st.session_state["generation_started"]:
if st.button("🔄 Nouvelle génération"):
for key in ["generation_started", "script_ready", "audio_ready", "pending_generation", "script", "audio_path", "audio_generation_started"]:
st.session_state.pop(key, None)
st.rerun()
def main():
st.set_page_config(page_title="UpVoice 🎙️", layout="wide")
render_sidebar()
# Affiche formulaire si aucune génération encore
if not st.session_state["generation_started"]:
render_form()
# Si soumission validée, mais script pas encore généré
if st.session_state.get("pending_generation"):
with st.spinner("Génération du script en cours...", show_time=True):
st.markdown("""
> Cela peut prendre une trentaine de seconde, merci de patienter 🙂.
""")
st.session_state["script"] = generate_script_sync(
st.session_state["article"],
st.session_state["language"],
st.session_state["voice1"],
st.session_state["voice2"]
)
st.session_state["script_ready"] = True
st.session_state["pending_generation"] = False
st.rerun()
# Affichage script et bouton audio
if st.session_state.get("script_ready"):
col1, col2 = st.columns([1, 1])
with col1:
render_script_panel()
with col2:
render_audio_panel()
if __name__ == "__main__":
main()