AnesSAD's picture
Fresh start - Streamlit on Python 3.11
6dc2e46
Raw History Blame Contribute Delete
5.94 kB
import streamlit as st
import os
import whisper
import tempfile
import time
import requests
import fal_client
from openai import OpenAI
from moviepy import VideoFileClip, concatenate_videoclips, AudioFileClip
from dotenv import load_dotenv
# --- CONFIGURATION INITIALE ---
load_dotenv()
client = OpenAI(api_key=os.environ.get("OPENAI_KEY"))
# On fixe la durée cible pour coller aux 129 frames à 24 fps de Fal.ai
TARGET_DURATION = 5.375
STORY_SEED = 42
st.set_page_config(page_title="AI Movie Meme Generator", page_icon="🎬")
st.title("🎬 AI Video Meme Generator")
st.info("Transformez vos audios en histoires animées cohérentes.")
# --- CHARGEMENT DU MODÈLE WHISPER (Cache pour éviter de recharger) ---
@st.cache_resource
def load_whisper():
return whisper.load_model("base")
model_whisper = load_whisper()
# --- FONCTIONS DE TRANSCRIPTION ---
def get_segments(audio_path):
result = model_whisper.transcribe(audio_path)
segments_list = []
current_text = ""
current_start = 0
for segment in result["segments"]:
current_text += segment["text"] + " "
if segment["end"] - current_start >= TARGET_DURATION:
segments_list.append(current_text.strip())
current_text = ""
current_start = segment["end"]
if current_text.strip():
segments_list.append(current_text.strip())
return segments_list
# --- FONCTIONS GPT (PROMPTS) ---
def generate_bible_and_prompts(segments):
full_text = " ".join(segments)
# 1. Bible Visuelle
bible_resp = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": "You are a Concept Artist. Define a consistent visual style and characters for this story in 3 sentences (English)."},
{"role": "user", "content": full_text}
]
)
bible = bible_resp.choices[0].message.content.strip()
# 2. Prompts des scènes
prompts = []
for seg in segments:
resp = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": f"VISUAL BIBLE: {bible}\nCreate a single fluid video prompt in English for this scene. Include '720p', 'high detail', 'smooth motion'."},
{"role": "user", "content": seg}
]
)
prompts.append(resp.choices[0].message.content.strip())
return bible, prompts
# --- FONCTIONS VIDÉO (FAL.AI) ---
def generate_all_clips(prompts, temp_dir):
video_paths = []
for i, p in enumerate(prompts):
st.write(f"⏳ Génération du segment {i+1}/{len(prompts)}...")
handler = fal_client.submit(
"fal-ai/hunyuan-video",
arguments={
"prompt": p,
"video_size": "720p_portrait",
"num_frames": 129,
"fps": 24,
"seed": STORY_SEED,
"guidance_scale": 7.0
}
)
result = fal_client.result("fal-ai/hunyuan-video", handler.request_id)
# Téléchargement
path = os.path.join(temp_dir, f"part_{i}.mp4")
with open(path, "wb") as f:
f.write(requests.get(result['video']['url']).content)
video_paths.append(path)
return video_paths
# --- INTERFACE UTILISATEUR (STREAMLIT) ---
uploaded_audio = st.file_uploader("Étape 1 : Importez votre audio (mp3, wav)", type=["mp3", "wav"])
if uploaded_audio:
# Sauvegarde temporaire de l'audio pour Whisper et MoviePy
with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as tmp_audio:
tmp_audio.write(uploaded_audio.read())
st.session_state.audio_path = tmp_audio.name
# BOUTON 1 : TRANSCRIPTION
if st.button("Étape 2 : Analyser l'histoire"):
with st.spinner("Transcription et découpage..."):
st.session_state.segments = get_segments(st.session_state.audio_path)
st.success(f"Histoire découpée en {len(st.session_state.segments)} scènes.")
for s in st.session_state.segments:
st.write(f"- {s}")
# BOUTON 2 : PROMPTS
if "segments" in st.session_state and st.button("Étape 3 : Créer le scénario visuel"):
with st.spinner("GPT-4o-mini travaille sur la cohérence..."):
bible, prompts = generate_bible_and_prompts(st.session_state.segments)
st.session_state.bible = bible
st.session_state.prompts = prompts
st.write(f"🎨 **Style défini :** {bible}")
st.success("Prompts générés avec succès.")
# BOUTON 3 : VIDÉO FINALE
if "prompts" in st.session_state and st.button("🚀 Étape 4 : Générer le film (Fal.ai)"):
with tempfile.TemporaryDirectory() as tmp_video_dir:
with st.status("Production de la vidéo en cours...", expanded=True) as status:
# Génération
clips = generate_all_clips(st.session_state.prompts, tmp_video_dir)
# Assemblage
status.update(label="Montage final et synchronisation audio...")
video_clips = [VideoFileClip(c) for c in clips]
final_v = concatenate_videoclips(video_clips, method="compose")
# Ajout de l'audio
audio_bg = AudioFileClip(st.session_state.audio_path)
final_v = final_v.set_audio(audio_bg.subclip(0, final_v.duration))
output_path = "final_meme_video.mp4"
final_v.write_videofile(output_path, codec="libx264", audio_codec="aac")
status.update(label="Film terminé !", state="complete")
# Affichage final
st.video(output_path)
with open(output_path, "rb") as f:
st.download_button("💾 Télécharger ma vidéo", f, "mon_film_ia.mp4")