gnosticdev's picture
Update app.py
fb4e1f4 verified
raw
history blame
6.15 kB
import os
import math
import tempfile
import logging
from PIL import Image
from pydub import AudioSegment
from moviepy.editor import (
VideoFileClip, AudioFileClip, ImageClip,
concatenate_videoclips, CompositeVideoClip, CompositeAudioClip
)
import edge_tts
import gradio as gr
import asyncio
# PATCH PARA PILLOW 10+
Image.ANTIALIAS = Image.Resampling.LANCZOS
# CONFIGURACI脫N
INTRO_VIDEO = "introvideo.mp4"
OUTRO_VIDEO = "outrovideo.mp4"
MUSIC_BG = "musicafondo.mp3"
FX_SOUND = "fxsound.mp3"
WATERMARK = "watermark.png"
# Validar archivos
for file in [INTRO_VIDEO, OUTRO_VIDEO, MUSIC_BG, FX_SOUND, WATERMARK]:
if not os.path.exists(file):
raise FileNotFoundError(f"Falta: {file}")
async def procesar_audio(texto, voz):
try:
communicate = edge_tts.Communicate(texto, voz)
with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as tmp:
await communicate.save(tmp.name)
return tmp.name # Retorna solo el TTS sin procesar
except Exception as e:
logging.error(f"TTS fallido: {e}")
raise
def mezclar_audio(tts_path, duracion_total):
try:
# Cargar TTS
tts = AudioSegment.from_mp3(tts_path)
# Preparar m煤sica de fondo en loop
bg = AudioSegment.from_mp3(MUSIC_BG)
bg = bg - 10 # 10% volumen (ajusta seg煤n necesidad)
# Calcular repeticiones necesarias
repeticiones = math.ceil(duracion_total * 1000 / len(bg))
bg_loop = bg * repeticiones
# Recortar a duraci贸n exacta
bg_final = bg_loop[:duracion_total*1000].fade_out(3000)
# Combinar TTS y m煤sica
audio_final = bg_final.overlay(tts, position=0)
# Guardar temporal
with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as tmp:
audio_final.export(tmp.name, format="mp3")
return tmp.name
except Exception as e:
logging.error(f"Error mezclando audio: {e}")
raise
def cortar_video(video_path, metodo="inteligente", duracion=10):
try:
video = VideoFileClip(video_path)
if metodo == "manual":
return [
video.subclip(i*duracion, (i+1)*duracion)
for i in range(math.ceil(video.duration/duracion))
]
# Simulaci贸n de cortes autom谩ticos
clips = []
ultimo_corte = 0
for i in range(1, math.ceil(video.duration)):
if i % 5 == 0: # Simulaci贸n de pausas
clips.append(video.subclip(ultimo_corte, i))
ultimo_corte = i
return clips if clips else [video]
except Exception as e:
logging.error(f"Error cortando video: {e}")
raise
def agregar_transiciones(clips):
try:
fx_audio = AudioFileClip(FX_SOUND).set_duration(2.5)
transicion = ImageClip(WATERMARK).set_duration(2.5).resize(height=clips[0].h).set_position(("center", 0.1))
clips_con_fx = []
for i, clip in enumerate(clips):
# Agregar watermark
clip_watermarked = CompositeVideoClip([clip, transicion])
clips_con_fx.append(clip_watermarked)
# Agregar transici贸n entre clips
if i < len(clips)-1:
clips_con_fx.append(
CompositeVideoClip([transicion.set_position("center")])
.set_audio(fx_audio)
)
return concatenate_videoclips(clips_con_fx)
except Exception as e:
logging.error(f"Error en transiciones: {e}")
raise
async def procesar_video(
video_input,
texto_tts,
voz_seleccionada,
metodo_corte,
duracion_corte
):
temp_files = []
try:
# Procesar video
clips = cortar_video(video_input, metodo_corte, duracion_corte)
video_editado = agregar_transiciones(clips)
# Agregar intro/outro
intro = VideoFileClip(INTRO_VIDEO)
outro = VideoFileClip(OUTRO_VIDEO)
video_final = concatenate_videoclips([intro, video_editado, outro])
# Generar TTS
tts_path = await procesar_audio(texto_tts, voz_seleccionada)
duracion_tts = AudioFileClip(tts_path).duration
# Calcular duraci贸n total (intro + contenido + outro)
duracion_total = intro.duration + video_editado.duration + outro.duration
# Mezclar audio
audio_mix_path = mezclar_audio(tts_path, duracion_total)
# Combinar video y audio
video_final = video_final.set_audio(AudioFileClip(audio_mix_path))
# Guardar resultado final
with tempfile.NamedTemporaryFile(delete=False, suffix=".mp4") as tmp:
video_final.write_videofile(tmp.name, codec="libx264", fps=24)
temp_files.append(tmp.name)
return tmp.name
except Exception as e:
logging.error(f"Error general: {e}")
raise
finally:
# Eliminar temporales
for file in temp_files + [tts_path, audio_mix_path]:
try:
os.remove(file)
except:
pass
# Interfaz Gradio
with gr.Blocks() as demo:
gr.Markdown("# Video Editor IA")
with gr.Tab("Principal"):
video_input = gr.Video(label="Subir video")
texto_tts = gr.Textbox(label="Texto para TTS", lines=3)
voz_seleccionada = gr.Dropdown(
label="Voz",
choices=["es-ES-AlvaroNeural", "es-MX-BeatrizNeural"]
)
procesar_btn = gr.Button("Generar")
video_output = gr.Video(label="Resultado")
with gr.Tab("Ajustes"):
metodo_corte = gr.Radio(
["inteligente", "manual"],
label="M茅todo de corte",
value="inteligente"
)
duracion_corte = gr.Slider(1, 60, 10, label="Duraci贸n por corte (solo manual)")
procesar_btn.click(
procesar_video,
inputs=[video_input, texto_tts, voz_seleccionada, metodo_corte, duracion_corte],
outputs=video_output
)
if __name__ == "__main__":
demo.queue().launch()