File size: 8,063 Bytes
43fcbe8
19224f2
38ff849
8337d0b
 
c9d2e08
07b3b3d
38ff849
 
dd712f9
07b3b3d
38ff849
 
 
 
720c3d5
38ff849
1672ed1
38ff849
 
 
1672ed1
 
38ff849
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
07b3b3d
 
 
38ff849
 
07b3b3d
38ff849
 
 
07b3b3d
 
38ff849
07b3b3d
 
38ff849
c7b9a72
38ff849
 
 
 
 
19224f2
 
 
 
38ff849
19224f2
 
8337d0b
38ff849
8337d0b
19224f2
38ff849
c7b9a72
19224f2
 
38ff849
8337d0b
38ff849
 
 
 
07b3b3d
38ff849
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
07b3b3d
 
 
 
38ff849
19224f2
38ff849
 
8337d0b
 
38ff849
 
 
 
 
 
 
8337d0b
38ff849
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8337d0b
38ff849
 
 
 
 
 
 
 
fa201eb
 
38ff849
fa201eb
38ff849
720c3d5
07b3b3d
38ff849
07b3b3d
 
38ff849
 
 
c9d2e08
38ff849
 
 
d7f3a60
 
38ff849
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8337d0b
 
38ff849
 
518f669
9e5ee0a
d7f3a60
38ff849
 
 
 
 
 
 
 
 
07b3b3d
 
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
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
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)