INVIDEO_BASIC / app.py
gnosticdev's picture
Update app.py
38ff849 verified
raw
history blame
8.06 kB
import os
import subprocess
import requests
import gradio as gr
from moviepy.editor import *
from datetime import datetime
import tempfile
import logging
from transformers import pipeline
# Configuraci贸n inicial
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
PEXELS_API_KEY = os.getenv("PEXELS_API_KEY") # Configurar en Hugging Face
# Lista de voces v谩lidas (puedes a帽adir m谩s)
VOICES = [
"es-MX-DaliaNeural", "es-ES-ElviraNeural", "es-AR-ElenaNeural",
"en-US-JennyNeural", "fr-FR-DeniseNeural", "de-DE-KatjaNeural",
"it-IT-ElsaNeural", "pt-BR-FranciscaNeural", "ja-JP-NanamiNeural"
]
# Inicializar el generador de texto
try:
script_generator = pipeline("text-generation", model="facebook/mbart-large-50")
except:
logger.warning("No se pudo cargar el modelo de generaci贸n de texto")
script_generator = None
def generar_guion(prompt):
"""Genera un guion autom谩tico usando IA"""
if script_generator:
try:
result = script_generator(
f"Genera un guion breve para un video sobre '{prompt}' con 3 puntos principales:",
max_length=250,
num_return_sequences=1
)
return result[0]['generated_text']
except Exception as e:
logger.error(f"Error generando guion: {str(e)}")
# Fallback si falla la generaci贸n
return f"1. Primer punto sobre {prompt}\n2. Segundo punto\n3. Tercer punto"
def descargar_video(url, output_path):
"""Descarga un video y lo guarda localmente"""
try:
response = requests.get(url, stream=True, timeout=20)
response.raise_for_status()
with open(output_path, 'wb') as f:
for chunk in response.iter_content(chunk_size=1024*1024): # 1MB chunks
if chunk:
f.write(chunk)
return True
except Exception as e:
logger.error(f"Error descargando video: {str(e)}")
return False
def crear_video(prompt, custom_script, voz_seleccionada, musica=None):
try:
# 1. Generar o usar guion
guion = custom_script if custom_script else generar_guion(prompt)
logger.info(f"Guion: {guion[:100]}...")
# 2. Generar voz
voz_archivo = "voz.mp3"
subprocess.run([
'edge-tts',
'--voice', voz_seleccionada,
'--text', guion,
'--write-media', voz_archivo
], check=True)
# 3. Buscar videos en Pexels
headers = {"Authorization": PEXELS_API_KEY}
response = requests.get(
f"https://api.pexels.com/videos/search?query={prompt[:50]}&per_page=3",
headers=headers,
timeout=15
)
videos_data = response.json().get("videos", [])
if not videos_data:
raise Exception("No se encontraron videos en Pexels")
# 4. Descargar y preparar clips de video
clips = []
for i, video in enumerate(videos_data[:3]):
# Seleccionar la mejor calidad de video disponible
video_files = sorted(
[vf for vf in video['video_files'] if vf.get('width')],
key=lambda x: x['width'],
reverse=True
)
if not video_files:
continue
video_url = video_files[0]['link']
temp_video_path = f"temp_video_{i}.mp4"
if descargar_video(video_url, temp_video_path):
clip = VideoFileClip(temp_video_path)
# Calcular duraci贸n proporcional
clip_duration = min(10, clip.duration) # M谩ximo 10 segundos por clip
clips.append(clip.subclip(0, clip_duration))
if not clips:
raise Exception("No se pudieron cargar videos v谩lidos")
# 5. Procesar audio
audio = AudioFileClip(voz_archivo)
total_duration = audio.duration
if musica:
musica_clip = AudioFileClip(musica.name)
if musica_clip.duration < total_duration:
# Crear loop si la m煤sica es m谩s corta
looped_music = musica_clip.loop(duration=total_duration)
else:
looped_music = musica_clip.subclip(0, total_duration)
audio = CompositeAudioClip([audio, looped_music.volumex(0.25)])
# 6. Crear video final
# Calcular duraci贸n por clip
clip_durations = [c.duration for c in clips]
total_clip_duration = sum(clip_durations)
scale_factor = total_duration / total_clip_duration if total_clip_duration > 0 else 1
# Ajustar velocidad de los clips para que coincidan con el audio
adjusted_clips = [c.fx(vfx.speedx, scale_factor) for c in clips]
final_clip = concatenate_videoclips(adjusted_clips, method="compose")
# Aplicar transici贸n suave entre clips
final_clip = final_clip.fx(vfx.fadein, 0.5).fx(vfx.fadeout, 0.5)
# Ajustar duraci贸n exacta
final_clip = final_clip.set_duration(total_duration).set_audio(audio)
# 7. Guardar video final
output_path = f"video_{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp4"
final_clip.write_videofile(
output_path,
codec="libx264",
audio_codec="aac",
threads=2,
preset='fast',
fps=24
)
return output_path
except Exception as e:
logger.error(f"ERROR: {str(e)}")
return None
finally:
# Limpieza de archivos temporales
if os.path.exists(voz_archivo):
os.remove(voz_archivo)
for i in range(3):
if os.path.exists(f"temp_video_{i}.mp4"):
os.remove(f"temp_video_{i}.mp4")
# Interfaz Gradio mejorada
with gr.Blocks(theme=gr.themes.Soft(), title="Generador de Videos Profesional") as app:
gr.Markdown("# 馃幀 GENERADOR DE VIDEOS AUTOM脕TICO")
with gr.Row():
with gr.Column(scale=1):
gr.Markdown("### Configuraci贸n del Video")
prompt = gr.Textbox(label="Tema principal", placeholder="Ej: 'Lugares misteriosos de Espa帽a'")
custom_script = gr.TextArea(
label="Guion personalizado (opcional)",
placeholder="Pega aqu铆 tu propio guion...",
lines=5
)
voz = gr.Dropdown(
label="Selecciona una voz",
choices=VOICES,
value="es-ES-ElviraNeural",
interactive=True
)
musica = gr.File(
label="M煤sica de fondo (opcional)",
file_types=[".mp3", ".wav"],
type="filepath"
)
btn = gr.Button("馃殌 GENERAR VIDEO", variant="primary", size="lg")
with gr.Column(scale=2):
output = gr.Video(
label="Video Resultante",
format="mp4",
interactive=False,
elem_id="video-player"
)
gr.Examples(
examples=[
["Lugares hist贸ricos de Roma", "", "it-IT-ElsaNeural", None],
["Tecnolog铆as del futuro", "", "en-US-JennyNeural", None],
["Playas paradis铆acas del Caribe", "", "es-MX-DaliaNeural", None]
],
inputs=[prompt, custom_script, voz, musica],
label="Ejemplos para probar"
)
btn.click(
fn=crear_video,
inputs=[prompt, custom_script, voz, musica],
outputs=output
)
# CSS para mejorar la visualizaci贸n
app.css = """
#video-player {
max-width: 100%;
border-radius: 10px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}
"""
if __name__ == "__main__":
app.launch(server_name="0.0.0.0", server_port=7860)