gnosticdev commited on
Commit
38ff849
verified
1 Parent(s): 07b3b3d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +167 -45
app.py CHANGED
@@ -1,106 +1,228 @@
1
  import os
2
  import subprocess
 
3
  import gradio as gr
4
  from moviepy.editor import *
5
- import requests
6
  from datetime import datetime
7
  import tempfile
 
 
8
 
9
  # Configuraci贸n inicial
10
- PEXELS_API_KEY = os.getenv("PEXELS_API_KEY") # Configurar en variables de entorno
 
 
 
11
 
12
- # Lista de voces v谩lidas
13
  VOICES = [
14
- "es-MX-DaliaNeural", "es-ES-ElviraNeural",
15
- "es-AR-ElenaNeural", "en-US-JennyNeural",
16
- "fr-FR-DeniseNeural", "de-DE-KatjaNeural"
17
  ]
18
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  def descargar_video(url, output_path):
20
  """Descarga un video y lo guarda localmente"""
21
  try:
22
- response = requests.get(url, stream=True, timeout=15)
 
23
  with open(output_path, 'wb') as f:
24
- for chunk in response.iter_content(chunk_size=1024*1024):
25
- f.write(chunk)
 
26
  return True
27
  except Exception as e:
28
- print(f"Error descargando video: {str(e)}")
29
  return False
30
 
31
- def generar_video(prompt, voz_seleccionada, musica=None):
32
  try:
33
- # 1. Generar voz
 
 
 
 
34
  voz_archivo = "voz.mp3"
35
  subprocess.run([
36
  'edge-tts',
37
  '--voice', voz_seleccionada,
38
- '--text', prompt,
39
  '--write-media', voz_archivo
40
  ], check=True)
41
 
42
- # 2. Buscar videos en Pexels
43
  headers = {"Authorization": PEXELS_API_KEY}
44
  response = requests.get(
45
- f"https://api.pexels.com/videos/search?query={prompt[:50]}&per_page=2",
46
  headers=headers,
47
  timeout=15
48
  )
49
- videos = response.json().get("videos", [])
50
 
51
- # 3. Descargar y procesar videos
 
 
 
52
  clips = []
53
- for v in videos[:2]:
54
- video_url = v["video_files"][0]["link"]
55
- temp_video = tempfile.NamedTemporaryFile(suffix=".mp4", delete=False)
56
- if descargar_video(video_url, temp_video.name):
57
- clip = VideoFileClip(temp_video.name).subclip(0, min(5, VideoFileClip(temp_video.name).duration))
58
- clips.append(clip)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
 
60
  if not clips:
61
  raise Exception("No se pudieron cargar videos v谩lidos")
62
 
63
- # 4. Procesar audio
64
  audio = AudioFileClip(voz_archivo)
 
 
65
  if musica:
66
  musica_clip = AudioFileClip(musica.name)
67
- if musica_clip.duration < audio.duration:
68
- musica_clip = musica_clip.loop(duration=audio.duration)
69
- audio = CompositeAudioClip([audio, musica_clip.volumex(0.3)])
 
 
 
 
70
 
71
- # 5. Crear video final
72
- final_clip = concatenate_videoclips(clips).set_audio(audio)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  output_path = f"video_{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp4"
74
- final_clip.write_videofile(output_path, codec="libx264", audio_codec="aac", threads=2)
 
 
 
 
 
 
 
75
 
76
  return output_path
 
77
  except Exception as e:
78
- print(f"ERROR: {str(e)}")
79
  return None
80
  finally:
81
- # Limpieza
82
  if os.path.exists(voz_archivo):
83
  os.remove(voz_archivo)
 
 
 
84
 
85
- # Interfaz Gradio
86
- with gr.Blocks() as app:
87
- gr.Markdown("# 馃幀 Generador de Videos Autom谩tico")
88
 
89
  with gr.Row():
90
- with gr.Column():
91
- prompt = gr.Textbox(label="Tema del video")
92
- voz = gr.Dropdown(label="Selecci贸n de voz", choices=VOICES, value="es-ES-ElviraNeural")
93
- musica = gr.File(label="M煤sica de fondo (opcional)", file_types=[".mp3"])
94
- btn = gr.Button("Generar Video")
95
-
96
- with gr.Column():
97
- output = gr.Video(label="Resultado")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
 
99
  btn.click(
100
- fn=generar_video,
101
- inputs=[prompt, voz, musica],
102
  outputs=output
103
  )
104
 
 
 
 
 
 
 
 
 
 
105
  if __name__ == "__main__":
106
  app.launch(server_name="0.0.0.0", server_port=7860)
 
1
  import os
2
  import subprocess
3
+ import requests
4
  import gradio as gr
5
  from moviepy.editor import *
 
6
  from datetime import datetime
7
  import tempfile
8
+ import logging
9
+ from transformers import pipeline
10
 
11
  # Configuraci贸n inicial
12
+ logging.basicConfig(level=logging.INFO)
13
+ logger = logging.getLogger(__name__)
14
+
15
+ PEXELS_API_KEY = os.getenv("PEXELS_API_KEY") # Configurar en Hugging Face
16
 
17
+ # Lista de voces v谩lidas (puedes a帽adir m谩s)
18
  VOICES = [
19
+ "es-MX-DaliaNeural", "es-ES-ElviraNeural", "es-AR-ElenaNeural",
20
+ "en-US-JennyNeural", "fr-FR-DeniseNeural", "de-DE-KatjaNeural",
21
+ "it-IT-ElsaNeural", "pt-BR-FranciscaNeural", "ja-JP-NanamiNeural"
22
  ]
23
 
24
+ # Inicializar el generador de texto
25
+ try:
26
+ script_generator = pipeline("text-generation", model="facebook/mbart-large-50")
27
+ except:
28
+ logger.warning("No se pudo cargar el modelo de generaci贸n de texto")
29
+ script_generator = None
30
+
31
+ def generar_guion(prompt):
32
+ """Genera un guion autom谩tico usando IA"""
33
+ if script_generator:
34
+ try:
35
+ result = script_generator(
36
+ f"Genera un guion breve para un video sobre '{prompt}' con 3 puntos principales:",
37
+ max_length=250,
38
+ num_return_sequences=1
39
+ )
40
+ return result[0]['generated_text']
41
+ except Exception as e:
42
+ logger.error(f"Error generando guion: {str(e)}")
43
+
44
+ # Fallback si falla la generaci贸n
45
+ return f"1. Primer punto sobre {prompt}\n2. Segundo punto\n3. Tercer punto"
46
+
47
  def descargar_video(url, output_path):
48
  """Descarga un video y lo guarda localmente"""
49
  try:
50
+ response = requests.get(url, stream=True, timeout=20)
51
+ response.raise_for_status()
52
  with open(output_path, 'wb') as f:
53
+ for chunk in response.iter_content(chunk_size=1024*1024): # 1MB chunks
54
+ if chunk:
55
+ f.write(chunk)
56
  return True
57
  except Exception as e:
58
+ logger.error(f"Error descargando video: {str(e)}")
59
  return False
60
 
61
+ def crear_video(prompt, custom_script, voz_seleccionada, musica=None):
62
  try:
63
+ # 1. Generar o usar guion
64
+ guion = custom_script if custom_script else generar_guion(prompt)
65
+ logger.info(f"Guion: {guion[:100]}...")
66
+
67
+ # 2. Generar voz
68
  voz_archivo = "voz.mp3"
69
  subprocess.run([
70
  'edge-tts',
71
  '--voice', voz_seleccionada,
72
+ '--text', guion,
73
  '--write-media', voz_archivo
74
  ], check=True)
75
 
76
+ # 3. Buscar videos en Pexels
77
  headers = {"Authorization": PEXELS_API_KEY}
78
  response = requests.get(
79
+ f"https://api.pexels.com/videos/search?query={prompt[:50]}&per_page=3",
80
  headers=headers,
81
  timeout=15
82
  )
83
+ videos_data = response.json().get("videos", [])
84
 
85
+ if not videos_data:
86
+ raise Exception("No se encontraron videos en Pexels")
87
+
88
+ # 4. Descargar y preparar clips de video
89
  clips = []
90
+ for i, video in enumerate(videos_data[:3]):
91
+ # Seleccionar la mejor calidad de video disponible
92
+ video_files = sorted(
93
+ [vf for vf in video['video_files'] if vf.get('width')],
94
+ key=lambda x: x['width'],
95
+ reverse=True
96
+ )
97
+
98
+ if not video_files:
99
+ continue
100
+
101
+ video_url = video_files[0]['link']
102
+ temp_video_path = f"temp_video_{i}.mp4"
103
+
104
+ if descargar_video(video_url, temp_video_path):
105
+ clip = VideoFileClip(temp_video_path)
106
+
107
+ # Calcular duraci贸n proporcional
108
+ clip_duration = min(10, clip.duration) # M谩ximo 10 segundos por clip
109
+ clips.append(clip.subclip(0, clip_duration))
110
 
111
  if not clips:
112
  raise Exception("No se pudieron cargar videos v谩lidos")
113
 
114
+ # 5. Procesar audio
115
  audio = AudioFileClip(voz_archivo)
116
+ total_duration = audio.duration
117
+
118
  if musica:
119
  musica_clip = AudioFileClip(musica.name)
120
+ if musica_clip.duration < total_duration:
121
+ # Crear loop si la m煤sica es m谩s corta
122
+ looped_music = musica_clip.loop(duration=total_duration)
123
+ else:
124
+ looped_music = musica_clip.subclip(0, total_duration)
125
+
126
+ audio = CompositeAudioClip([audio, looped_music.volumex(0.25)])
127
 
128
+ # 6. Crear video final
129
+ # Calcular duraci贸n por clip
130
+ clip_durations = [c.duration for c in clips]
131
+ total_clip_duration = sum(clip_durations)
132
+ scale_factor = total_duration / total_clip_duration if total_clip_duration > 0 else 1
133
+
134
+ # Ajustar velocidad de los clips para que coincidan con el audio
135
+ adjusted_clips = [c.fx(vfx.speedx, scale_factor) for c in clips]
136
+ final_clip = concatenate_videoclips(adjusted_clips, method="compose")
137
+
138
+ # Aplicar transici贸n suave entre clips
139
+ final_clip = final_clip.fx(vfx.fadein, 0.5).fx(vfx.fadeout, 0.5)
140
+
141
+ # Ajustar duraci贸n exacta
142
+ final_clip = final_clip.set_duration(total_duration).set_audio(audio)
143
+
144
+ # 7. Guardar video final
145
  output_path = f"video_{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp4"
146
+ final_clip.write_videofile(
147
+ output_path,
148
+ codec="libx264",
149
+ audio_codec="aac",
150
+ threads=2,
151
+ preset='fast',
152
+ fps=24
153
+ )
154
 
155
  return output_path
156
+
157
  except Exception as e:
158
+ logger.error(f"ERROR: {str(e)}")
159
  return None
160
  finally:
161
+ # Limpieza de archivos temporales
162
  if os.path.exists(voz_archivo):
163
  os.remove(voz_archivo)
164
+ for i in range(3):
165
+ if os.path.exists(f"temp_video_{i}.mp4"):
166
+ os.remove(f"temp_video_{i}.mp4")
167
 
168
+ # Interfaz Gradio mejorada
169
+ with gr.Blocks(theme=gr.themes.Soft(), title="Generador de Videos Profesional") as app:
170
+ gr.Markdown("# 馃幀 GENERADOR DE VIDEOS AUTOM脕TICO")
171
 
172
  with gr.Row():
173
+ with gr.Column(scale=1):
174
+ gr.Markdown("### Configuraci贸n del Video")
175
+ prompt = gr.Textbox(label="Tema principal", placeholder="Ej: 'Lugares misteriosos de Espa帽a'")
176
+ custom_script = gr.TextArea(
177
+ label="Guion personalizado (opcional)",
178
+ placeholder="Pega aqu铆 tu propio guion...",
179
+ lines=5
180
+ )
181
+ voz = gr.Dropdown(
182
+ label="Selecciona una voz",
183
+ choices=VOICES,
184
+ value="es-ES-ElviraNeural",
185
+ interactive=True
186
+ )
187
+ musica = gr.File(
188
+ label="M煤sica de fondo (opcional)",
189
+ file_types=[".mp3", ".wav"],
190
+ type="filepath"
191
+ )
192
+ btn = gr.Button("馃殌 GENERAR VIDEO", variant="primary", size="lg")
193
+
194
+ with gr.Column(scale=2):
195
+ output = gr.Video(
196
+ label="Video Resultante",
197
+ format="mp4",
198
+ interactive=False,
199
+ elem_id="video-player"
200
+ )
201
+
202
+ gr.Examples(
203
+ examples=[
204
+ ["Lugares hist贸ricos de Roma", "", "it-IT-ElsaNeural", None],
205
+ ["Tecnolog铆as del futuro", "", "en-US-JennyNeural", None],
206
+ ["Playas paradis铆acas del Caribe", "", "es-MX-DaliaNeural", None]
207
+ ],
208
+ inputs=[prompt, custom_script, voz, musica],
209
+ label="Ejemplos para probar"
210
+ )
211
 
212
  btn.click(
213
+ fn=crear_video,
214
+ inputs=[prompt, custom_script, voz, musica],
215
  outputs=output
216
  )
217
 
218
+ # CSS para mejorar la visualizaci贸n
219
+ app.css = """
220
+ #video-player {
221
+ max-width: 100%;
222
+ border-radius: 10px;
223
+ box-shadow: 0 4px 12px rgba(0,0,0,0.15);
224
+ }
225
+ """
226
+
227
  if __name__ == "__main__":
228
  app.launch(server_name="0.0.0.0", server_port=7860)