File size: 8,086 Bytes
f93cca7 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 |
import gradio as gr
from tts_module import get_voices, text_to_speech # Usamos el tts_module.py actualizado
from pexels_api import search_pexels
from moviepy.editor import AudioFileClip, VideoFileClip, CompositeAudioClip, concatenate_audioclips, concatenate_videoclips
import asyncio
import os
import requests
import tempfile
# Forzar instalación de moviepy si no está disponible
def install(package):
import subprocess
import sys
subprocess.check_call([sys.executable, "-m", "pip", "install", package])
try:
# Intentar importar moviepy.editor
from moviepy.editor import AudioFileClip, VideoFileClip, CompositeAudioClip, concatenate_audioclips, concatenate_videoclips
print("MoviePy.editor está instalado correctamente.")
except ImportError:
print("Instalando MoviePy...")
install("moviepy==1.0.3") # Forzar instalación de la versión compatible
try:
from moviepy.editor import AudioFileClip, VideoFileClip, CompositeAudioClip, concatenate_audioclips, concatenate_videoclips
print("MoviePy.editor instalado con éxito después de la reinstalación.")
except ImportError:
raise ImportError("Error crítico: No se pudo instalar moviepy.editor. Verifica las dependencias.")
# Ajustar música de fondo (repetición automática)
def adjust_background_music(video_duration, music_file):
music = AudioFileClip(music_file)
if music.duration < video_duration:
repetitions = int(video_duration / music.duration) + 1
music_clips = [music] * repetitions
music = concatenate_audioclips(music_clips)
if music.duration > video_duration:
music = music.subclip(0, video_duration)
music = music.volumex(0.2) # Reducir volumen al 20%
return music
# Concatenar múltiples videos de Pexels con redimensión a 1920x1080
def concatenate_pexels_videos(text, num_videos=5):
sentences = [sentence.strip() for sentence in text.split(".") if sentence.strip()]
video_links = []
for sentence in sentences:
try:
links = search_pexels(sentence, num_results=num_videos)
if links:
video_links.append(links[0]) # Usamos el primer video encontrado para cada frase
except Exception as e:
print(f"Error al buscar video para la frase '{sentence}': {e}")
continue
if not video_links:
raise Exception("No se encontraron videos relevantes para el texto proporcionado.")
video_clips = []
target_width, target_height = 1920, 1080 # Resolución estándar 16:9
for link in video_links:
video_response = requests.get(link)
with tempfile.NamedTemporaryFile(delete=False, suffix=".mp4") as tmp_video:
tmp_video.write(video_response.content)
clip = VideoFileClip(tmp_video.name)
# Redimensionar el video a 1920x1080 manteniendo la relación de aspecto
if clip.size[0] / clip.size[1] > target_width / target_height:
# Si es más ancho, recortar los lados
new_width = int(clip.size[1] * (target_width / target_height))
clip = clip.crop(x1=(clip.size[0] - new_width) // 2, y1=0, x2=(clip.size[0] + new_width) // 2, y2=clip.size[1])
else:
# Si es más alto, recortar arriba y abajo
new_height = int(clip.size[0] * (target_height / target_width))
clip = clip.crop(x1=0, y1=(clip.size[1] - new_height) // 2, x2=clip.size[0], y2=(clip.size[1] + new_height) // 2)
# Redimensionar explícitamente a 1920x1080
clip = clip.resize((target_width, target_height))
video_clips.append(clip)
# Concatenar videos
final_clip = concatenate_videoclips(video_clips, method="compose")
return final_clip
# Combinar audio, video y música con fade out solo en el video y la música
def combine_audio_video(audio_file, video_clip, music_clip=None):
audio_clip = AudioFileClip(audio_file)
# Duración total: speech + 5 segundos para fade out
total_duration = audio_clip.duration + 5
# Extender la duración del video si es más corto que el audio + fade out
if video_clip.duration < total_duration:
video_clip = video_clip.loop(duration=total_duration) # Repetir el video si es necesario
# Aplicar fade out solo al video
video_clip = video_clip.set_duration(total_duration).fadeout(5)
# Combinar audio y video
final_clip = video_clip.set_audio(audio_clip)
# Añadir música de fondo si aplica
if music_clip:
# Extender la música para que coincida con la duración total
if music_clip.duration < total_duration:
repetitions = int(total_duration / music_clip.duration) + 1
music_clips = [music_clip] * repetitions
music_clip = concatenate_audioclips(music_clips)
if music_clip.duration > total_duration:
music_clip = music_clip.subclip(0, total_duration)
# Aplicar fade out a la música
music_clip = music_clip.audio_fadeout(5)
# Combinar audio principal, música y video
final_clip = final_clip.set_audio(CompositeAudioClip([audio_clip, music_clip]))
# Exportar el video final
output_path = tempfile.NamedTemporaryFile(delete=False, suffix=".mp4").name
final_clip.write_videofile(output_path, codec="libx264", audio_codec="aac", fps=24)
return output_path
# Función principal
def process_input(text, txt_file, mp3_file, selected_voice, rate, pitch):
try:
if text.strip():
final_text = text
elif txt_file is not None:
final_text = txt_file.decode("utf-8")
else:
return "No input provided", None
# Obtener voces disponibles
voices = asyncio.run(get_voices())
if selected_voice not in voices:
return f"La voz '{selected_voice}' no es válida. Por favor, seleccione una de las siguientes voces: {', '.join(voices.keys())}", None
# Generar audio con edge_tts
try:
audio_file = asyncio.run(text_to_speech(final_text, selected_voice, rate, pitch))
except Exception as e:
return f"Error con la voz seleccionada: {e}", None
# Concatenar múltiples videos de Pexels basados en el texto
try:
video_clip = concatenate_pexels_videos(final_text, num_videos=5)
except Exception as e:
return f"Error al buscar videos en Pexels: {e}", None
# Ajustar música de fondo
if mp3_file is not None:
music_clip = adjust_background_music(video_clip.duration, mp3_file.name)
else:
music_clip = None
# Combinar audio, video y música con fade out solo en el video y la música
final_video = combine_audio_video(audio_file, video_clip, music_clip)
return final_video
except Exception as e:
return f"Error durante el procesamiento: {e}", None
# Interfaz Gradio
with gr.Blocks() as demo:
gr.Markdown("# Text-to-Video Generator")
with gr.Row():
with gr.Column():
text_input = gr.Textbox(label="Write your text here", lines=5)
txt_file_input = gr.File(label="Or upload a .txt file", file_types=[".txt"])
mp3_file_input = gr.File(label="Upload background music (.mp3)", file_types=[".mp3"])
voices = asyncio.run(get_voices()) # Obtener voces disponibles
voice_dropdown = gr.Dropdown(choices=list(voices.keys()), label="Select Voice")
rate_slider = gr.Slider(minimum=-50, maximum=50, value=0, label="Speech Rate Adjustment (%)", step=1)
pitch_slider = gr.Slider(minimum=-20, maximum=20, value=0, label="Pitch Adjustment (Hz)", step=1)
with gr.Column():
output_video = gr.Video(label="Generated Video")
btn = gr.Button("Generate Video")
btn.click(
process_input,
inputs=[text_input, txt_file_input, mp3_file_input, voice_dropdown, rate_slider, pitch_slider],
outputs=output_video
)
demo.launch() |