gnosticdev commited on
Commit
77c11ae
verified
1 Parent(s): 7c87717

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +174 -113
app.py CHANGED
@@ -1,157 +1,218 @@
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 logging
8
- import re
 
 
 
 
9
  import torch
10
- from transformers import GPT2LMHeadModel, GPT2Tokenizer
 
11
 
12
- # Configuraci贸n b谩sica
13
- logging.basicConfig(level=logging.INFO)
 
14
  logger = logging.getLogger(__name__)
15
 
16
- # Configuraci贸n de entorno (usa tu propia API key de Pexels)
17
- PEXELS_API_KEY = os.getenv("PEXELS_API_KEY") or "TU_API_KEY_AQUI"
18
-
19
- # Voces disponibles (Edge-TTS)
20
- VOICES = ["es-MX-DaliaNeural", "es-ES-ElviraNeural", "en-US-JennyNeural"]
21
 
22
- # Carga el modelo GPT-2 en espa帽ol (ligero y r谩pido)
23
- tokenizer = GPT2Tokenizer.from_pretrained("datificate/gpt2-small-spanish")
24
- model = GPT2LMHeadModel.from_pretrained("datificate/gpt2-small-spanish")
25
 
26
- def generar_texto(tema):
27
- """Genera un texto largo y natural sobre el tema (sin estructuras forzadas)."""
28
- try:
29
- prompt = f"Habla extensamente sobre {tema} en un tono natural y detallado:"
30
- inputs = tokenizer(prompt, return_tensors="pt", truncation=True, max_length=512)
31
-
32
- outputs = model.generate(
33
- inputs.input_ids,
34
- max_length=800,
35
- do_sample=True,
36
- temperature=0.7,
37
- top_k=50,
38
- pad_token_id=tokenizer.eos_token_id
39
- )
40
-
41
- texto = tokenizer.decode(outputs[0], skip_special_tokens=True)
42
- return re.sub(r'\s+', ' ', texto).strip()
43
 
44
- except Exception as e:
45
- logger.error(f"Error generando texto: {e}")
46
- return f"Contenido generado sobre {tema}."
 
 
 
 
 
47
 
48
- def buscar_videos(tema):
49
- """Busca videos en Pexels y devuelve los 3 m谩s relevantes."""
50
- try:
51
- headers = {"Authorization": PEXELS_API_KEY}
52
- response = requests.get(
53
- f"https://api.pexels.com/videos/search?query={tema}&per_page=3",
54
- headers=headers,
55
- timeout=10
56
- )
57
- return response.json().get("videos", [])[:3]
58
- except Exception as e:
59
- logger.error(f"Error buscando videos: {e}")
60
- return []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
 
62
- def crear_video(tema, voz_seleccionada):
63
- """Genera el video final con voz y clips de video."""
64
  try:
65
- # 1. Generar texto
66
- texto = generar_texto(tema)
67
 
68
- # 2. Convertir texto a voz (Edge-TTS)
69
- voz_archivo = "narracion.mp3"
70
- subprocess.run([
71
- 'edge-tts',
72
- '--voice', voz_seleccionada,
73
- '--text', texto,
74
- '--write-media', voz_archivo
75
- ], check=True)
76
 
77
- # 3. Procesar audio
 
 
78
  audio = AudioFileClip(voz_archivo)
79
  duracion_total = audio.duration
80
 
81
- # 4. Buscar y descargar videos
82
- videos = buscar_videos(tema) or buscar_videos("nature")
 
 
83
  clips = []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
 
85
- for i, video in enumerate(videos[:3]): # M谩ximo 3 videos
86
- try:
87
- mejor_calidad = max(video['video_files'], key=lambda x: x.get('width', 0))
88
- url_video = mejor_calidad['link']
89
-
90
- # Descargar video temporal
91
- temp_file = f"temp_video_{i}.mp4"
92
- with requests.get(url_video, stream=True) as r:
93
- r.raise_for_status()
94
- with open(temp_file, 'wb') as f:
95
- for chunk in r.iter_content(chunk_size=8192):
96
- f.write(chunk)
97
-
98
- # Ajustar duraci贸n del clip
99
- clip = VideoFileClip(temp_file)
100
- duracion_clip = min(duracion_total / len(videos), clip.duration)
101
- clips.append(clip.subclip(0, duracion_clip))
102
-
103
- except Exception as e:
104
- logger.error(f"Error procesando video {i}: {e}")
105
 
106
- # 5. Combinar clips (o usar fondo negro si no hay videos)
107
- if not clips:
108
- video_final = ColorClip((1280, 720), (0, 0, 0), duration=duracion_total)
109
- else:
110
- video_final = concatenate_videoclips(clips, method="compose")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
111
 
112
  video_final = video_final.set_audio(audio)
113
 
114
- # 6. Exportar video
115
- nombre_archivo = f"video_final_{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp4"
116
  video_final.write_videofile(
117
- nombre_archivo,
118
- fps=24,
119
  codec="libx264",
120
  audio_codec="aac",
121
- threads=2,
122
- preset='fast'
 
 
123
  )
124
 
125
- return nombre_archivo
126
-
127
  except Exception as e:
128
- logger.error(f"Error cr铆tico: {e}")
129
  return None
130
-
131
  finally:
132
  # Limpieza de archivos temporales
133
  if os.path.exists(voz_archivo):
134
  os.remove(voz_archivo)
135
- for i in range(3):
136
- temp_file = f"temp_video_{i}.mp4"
137
- if os.path.exists(temp_file):
138
- os.remove(temp_file)
139
 
140
- # Interfaz de Gradio (sencilla y funcional)
141
- with gr.Blocks() as app:
142
- gr.Markdown("# 馃幀 Generador Autom谩tico de Videos")
143
-
144
- with gr.Row():
145
- tema = gr.Textbox(label="Tema del video", placeholder="Ej: 'Historia de la inteligencia artificial'")
146
- voz = gr.Dropdown(label="Voz", choices=VOICES, value=VOICES[0])
147
- btn = gr.Button("Generar Video", variant="primary")
148
 
149
- salida = gr.Video(label="Resultado")
 
 
 
 
150
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
  btn.click(
152
- fn=crear_video,
153
- inputs=[tema, voz],
154
- outputs=salida
155
  )
156
 
157
  if __name__ == "__main__":
 
1
  import os
2
+ import re
3
  import requests
4
  import gradio as gr
5
  from moviepy.editor import *
6
+ import edge_tts
7
+ import tempfile
8
  import logging
9
+ from datetime import datetime
10
+ import numpy as np
11
+ from sklearn.feature_extraction.text import TfidfVectorizer
12
+ import nltk
13
+ from transformers import pipeline
14
  import torch
15
+ import asyncio
16
+ import time
17
 
18
+ # Configuraci贸n inicial
19
+ nltk.download('punkt', quiet=True)
20
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
21
  logger = logging.getLogger(__name__)
22
 
23
+ # Configuraci贸n de modelos
24
+ PEXELS_API_KEY = os.getenv("PEXELS_API_KEY")
25
+ MODEL_NAME = "DeepESP/gpt2-spanish" # Modelo en espa帽ol
 
 
26
 
27
+ # Lista de voces disponibles
28
+ VOICES = asyncio.run(edge_tts.list_voices())
29
+ VOICE_NAMES = [f"{v['Name']} ({v['Gender']}, {v['LocaleName']})" for v in VOICES]
30
 
31
+ def generar_guion_profesional(prompt):
32
+ """Genera guiones detallados"""
33
+ generator = pipeline(
34
+ "text-generation",
35
+ model=MODEL_NAME,
36
+ device=0 if torch.cuda.is_available() else -1
37
+ )
 
 
 
 
 
 
 
 
 
 
38
 
39
+ response = generator(
40
+ f"Escribe un guion profesional para un video de YouTube sobre '{prompt}':",
41
+ max_length=600,
42
+ temperature=0.7,
43
+ num_return_sequences=1
44
+ )
45
+
46
+ return response[0]['generated_text']
47
 
48
+ def buscar_videos_avanzado(prompt, guion, num_videos=5):
49
+ """B煤squeda inteligente de videos usando an谩lisis de contenido"""
50
+ # Dividir el guion en oraciones
51
+ oraciones = nltk.sent_tokenize(guion)
52
+
53
+ # Extraer palabras clave con TF-IDF
54
+ vectorizer = TfidfVectorizer(stop_words=['el', 'la', 'los', 'las', 'de', 'en', 'y', 'que'])
55
+ tfidf = vectorizer.fit_transform(oraciones)
56
+ palabras = vectorizer.get_feature_names_out()
57
+ scores = np.asarray(tfidf.sum(axis=0)).ravel()
58
+ indices_importantes = np.argsort(scores)[-5:]
59
+ palabras_clave = [palabras[i] for i in indices_importantes]
60
+
61
+ # Mezclar palabras clave del prompt y del guion
62
+ palabras_prompt = re.findall(r'\b\w{4,}\b', prompt.lower())
63
+ todas_palabras = list(set(palabras_clave + palabras_prompt))[:5]
64
+
65
+ # Buscar en Pexels
66
+ headers = {"Authorization": PEXELS_API_KEY}
67
+ response = requests.get(
68
+ f"https://api.pexels.com/videos/search?query={'+'.join(todas_palabras)}&per_page={num_videos}",
69
+ headers=headers,
70
+ timeout=10
71
+ )
72
+
73
+ videos = response.json().get('videos', [])
74
+
75
+ # Seleccionar videos de mejor calidad
76
+ return sorted(
77
+ videos,
78
+ key=lambda x: x.get('width', 0) * x.get('height', 0),
79
+ reverse=True
80
+ )[:num_videos]
81
 
82
+ async def crear_video_profesional(prompt, custom_script, voz_index, musica=None):
 
83
  try:
84
+ # 1. Generar o usar guion
85
+ guion = custom_script if custom_script else generar_guion_profesional(prompt)
86
 
87
+ # 2. Seleccionar voz
88
+ voz_seleccionada = VOICES[voz_index]['ShortName']
 
 
 
 
 
 
89
 
90
+ # 3. Generar voz
91
+ voz_archivo = "voz.mp3"
92
+ await edge_tts.Communicate(guion, voz_seleccionada).save(voz_archivo)
93
  audio = AudioFileClip(voz_archivo)
94
  duracion_total = audio.duration
95
 
96
+ # 4. Buscar videos relevantes
97
+ videos_data = buscar_videos_avanzado(prompt, guion)
98
+
99
+ # 5. Descargar y preparar videos
100
  clips = []
101
+ for video in videos_data:
102
+ # Seleccionar la mejor calidad de video
103
+ video_files = sorted(
104
+ video['video_files'],
105
+ key=lambda x: x.get('width', 0) * x.get('height', 0),
106
+ reverse=True
107
+ )
108
+ video_url = video_files[0]['link']
109
+
110
+ # Descargar video
111
+ response = requests.get(video_url, stream=True)
112
+ temp_video = tempfile.NamedTemporaryFile(delete=False, suffix='.mp4')
113
+ for chunk in response.iter_content(chunk_size=1024*1024):
114
+ temp_video.write(chunk)
115
+ temp_video.close()
116
+
117
+ # Crear clip
118
+ clip = VideoFileClip(temp_video.name)
119
+ clips.append(clip)
120
 
121
+ # 6. Calcular duraci贸n por clip
122
+ duracion_por_clip = duracion_total / len(clips)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
 
124
+ # 7. Procesar clips de video
125
+ clips_procesados = []
126
+ for clip in clips:
127
+ # Si el clip es m谩s corto que la duraci贸n asignada, hacer loop
128
+ if clip.duration < duracion_por_clip:
129
+ clip = clip.loop(duration=duracion_por_clip)
130
+ # Si es m谩s largo, recortar
131
+ else:
132
+ clip = clip.subclip(0, duracion_por_clip)
133
+ clips_procesados.append(clip)
134
+
135
+ # 8. Combinar videos
136
+ video_final = concatenate_videoclips(clips_procesados)
137
+
138
+ # 9. Procesar m煤sica
139
+ if musica:
140
+ musica_clip = AudioFileClip(musica.name)
141
+ if musica_clip.duration < duracion_total:
142
+ musica_clip = musica_clip.loop(duration=duracion_total)
143
+ else:
144
+ musica_clip = musica_clip.subclip(0, duracion_total)
145
+ audio = CompositeAudioClip([audio, musica_clip.volumex(0.25)])
146
 
147
  video_final = video_final.set_audio(audio)
148
 
149
+ # 10. Exportar video
150
+ output_path = f"video_{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp4"
151
  video_final.write_videofile(
152
+ output_path,
 
153
  codec="libx264",
154
  audio_codec="aac",
155
+ threads=4,
156
+ preset='ultrafast',
157
+ fps=24,
158
+ logger=None
159
  )
160
 
161
+ return output_path
162
+
163
  except Exception as e:
164
+ logger.error(f"ERROR: {str(e)}")
165
  return None
 
166
  finally:
167
  # Limpieza de archivos temporales
168
  if os.path.exists(voz_archivo):
169
  os.remove(voz_archivo)
 
 
 
 
170
 
171
+ # Funci贸n para ejecutar la tarea as铆ncrona con manejo de progreso
172
+ def run_async_task(prompt, custom_script, voz_index, musica=None):
173
+ for i in range(5):
174
+ time.sleep(0.5) # Simular progreso
 
 
 
 
175
 
176
+ return asyncio.run(crear_video_profesional(prompt, custom_script, voz_index, musica))
177
+
178
+ # Interfaz profesional
179
+ with gr.Blocks(theme=gr.themes.Soft(), title="Generador de Videos") as app:
180
+ gr.Markdown("# 馃幀 GENERADOR DE VIDEOS CON IA")
181
 
182
+ with gr.Row():
183
+ with gr.Column(scale=1):
184
+ gr.Markdown("### Configuraci贸n")
185
+ prompt = gr.Textbox(label="Tema principal", placeholder="Ej: 'Los misterios de la antigua Grecia'")
186
+ custom_script = gr.TextArea(
187
+ label="Guion personalizado (opcional)",
188
+ placeholder="Pega aqu铆 tu propio guion completo...",
189
+ lines=6
190
+ )
191
+ voz = gr.Dropdown(
192
+ label="Voz",
193
+ choices=VOICE_NAMES,
194
+ value=VOICE_NAMES[0],
195
+ type="index"
196
+ )
197
+ musica = gr.File(label="M煤sica de fondo (opcional)", file_types=["audio"])
198
+ btn = gr.Button("馃殌 Generar Video", variant="primary")
199
+
200
+ with gr.Column(scale=2):
201
+ output = gr.Video(label="Video Resultante", format="mp4")
202
+
203
+ gr.Examples(
204
+ examples=[
205
+ ["Los secretos de las pir谩mides egipcias", "", 5, None],
206
+ ["La inteligencia artificial en medicina", "", 3, None]
207
+ ],
208
+ inputs=[prompt, custom_script, voz, musica],
209
+ label="Ejemplos"
210
+ )
211
+
212
  btn.click(
213
+ fn=run_async_task,
214
+ inputs=[prompt, custom_script, voz, musica],
215
+ outputs=output
216
  )
217
 
218
  if __name__ == "__main__":