gnosticdev commited on
Commit
4123c9b
verified
1 Parent(s): d446056

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +16 -42
app.py CHANGED
@@ -10,10 +10,8 @@ import edge_tts
10
  import gradio as gr
11
  from pydub import AudioSegment
12
 
13
- # Configuraci贸n de Logs
14
  logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
15
 
16
- # CONSTANTES DE ARCHIVOS
17
  INTRO_VIDEO = "introvideo.mp4"
18
  OUTRO_VIDEO = "outrovideo.mp4"
19
  MUSIC_BG = "musicafondo.mp3"
@@ -21,14 +19,12 @@ FX_SOUND = "fxsound.mp3"
21
  WATERMARK = "watermark.png"
22
  EJEMPLO_VIDEO = "ejemplo.mp4"
23
 
24
- # Validar existencia de archivos
25
  for file in [INTRO_VIDEO, OUTRO_VIDEO, MUSIC_BG, FX_SOUND, WATERMARK, EJEMPLO_VIDEO]:
26
  if not os.path.exists(file):
27
  logging.error(f"Falta archivo necesario: {file}")
28
  raise FileNotFoundError(f"Falta: {file}")
29
 
30
  def eliminar_archivo_tiempo(ruta, delay=1800):
31
- """Elimina archivos despu茅s de 30 minutos"""
32
  def eliminar():
33
  try:
34
  if os.path.exists(ruta):
@@ -39,7 +35,6 @@ def eliminar_archivo_tiempo(ruta, delay=1800):
39
  Timer(delay, eliminar).start()
40
 
41
  def validar_texto(texto):
42
- """Validaci贸n de texto para TTS"""
43
  texto_limpio = texto.strip()
44
  if len(texto_limpio) < 3:
45
  raise gr.Error("鈿狅笍 El texto debe tener al menos 3 caracteres")
@@ -47,28 +42,23 @@ def validar_texto(texto):
47
  raise gr.Error("鈿狅笍 Caracteres no permitidos detectados")
48
 
49
  async def procesar_audio(texto, voz, duracion_total, duracion_intro):
50
- """Genera TTS y mezcla con m煤sica (versi贸n corregida)"""
51
  temp_files = []
52
  try:
53
- # Validar texto
54
  validar_texto(texto)
55
-
56
- # Generar TTS
57
  communicate = edge_tts.Communicate(texto, voz)
 
58
  with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as tmp:
59
  await communicate.save(tmp.name)
60
  tts_audio = AudioFileClip(tmp.name)
61
  temp_files.append(tmp.name)
62
 
63
- # Verificar audio v谩lido
64
  if tts_audio.duration < 0.5:
65
  raise RuntimeError(f"Audio TTS inv谩lido ({tts_audio.duration}s)")
66
 
67
- # Procesar m煤sica de fondo
68
  bg_music = AudioSegment.from_mp3(MUSIC_BG)
69
- needed_ms = int(duracion_total * 1000) # <-- Convertir a entero
70
  repeticiones = needed_ms // len(bg_music) + 1
71
- bg_music = bg_music * repeticiones # <-- Ahora es multiplicaci贸n entera
72
  bg_music = bg_music[:needed_ms].fade_out(5000)
73
 
74
  with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as tmp:
@@ -76,18 +66,18 @@ async def procesar_audio(texto, voz, duracion_total, duracion_intro):
76
  bg_audio = AudioFileClip(tmp.name).volumex(0.15)
77
  temp_files.append(tmp.name)
78
 
79
- # Combinar audios
80
  audio_final = CompositeAudioClip([
81
- bg_audio,
82
  tts_audio.volumex(0.85).set_start(duracion_intro)
 
83
  ])
84
 
85
  return audio_final
 
86
  except Exception as e:
87
  logging.error(f" fallo en audio: {str(e)}")
88
  raise
89
  finally:
90
- # Limpiar archivos
91
  for file in temp_files:
92
  try:
93
  os.remove(file)
@@ -95,7 +85,6 @@ async def procesar_audio(texto, voz, duracion_total, duracion_intro):
95
  logging.warning(f"Error limpiando {file}: {e}")
96
 
97
  def agregar_transiciones(clips):
98
- """Transiciones profesionales cada 40s"""
99
  try:
100
  fx_audio = AudioFileClip(FX_SOUND).subclip(0, 0.5)
101
  watermark = (ImageClip(WATERMARK)
@@ -107,7 +96,6 @@ def agregar_transiciones(clips):
107
  for i, clip in enumerate(clips):
108
  clip_watermarked = CompositeVideoClip([clip, watermark])
109
 
110
- # Agregar transici贸n cada 40s
111
  if i > 0 and i % 40 == 0:
112
  transicion = CompositeVideoClip([watermark.set_duration(0.5)]).set_audio(fx_audio)
113
  clips_finales.append(transicion)
@@ -121,45 +109,41 @@ def agregar_transiciones(clips):
121
 
122
  async def procesar_video(video_input, texto_tts, voz_seleccionada, metodo_corte, duracion_corte):
123
  try:
124
- # Cargar video con audio original
125
  video_original = VideoFileClip(video_input)
126
  audio_original = video_original.audio.volumex(0.7) if video_original.audio else None
127
 
128
- # Cortar video
129
  clips = []
130
  if metodo_corte == "manual":
131
  for i in range(math.ceil(video_original.duration / duracion_corte)):
132
  clips.append(video_original.subclip(i*duracion_corte, (i+1)*duracion_corte))
133
  else:
134
- clips = [video_original.subclip(i, i+40) for i in range(0, math.ceil(video_original.duration), 40)]
 
135
 
136
- # Procesar transiciones
137
  video_editado = agregar_transiciones(clips)
138
-
139
- # Combinar con intro/outro
140
  intro = VideoFileClip(INTRO_VIDEO)
141
  outro = VideoFileClip(OUTRO_VIDEO)
142
  video_final = concatenate_videoclips([intro, video_editado, outro])
143
 
144
- # Calcular duraciones
145
  duracion_total = video_final.duration
146
- audio_tts_bg = await procesar_audio(texto_tts, voz_seleccionada, duracion_total, intro.duration)
 
 
147
 
148
- # Combinar todos los audios
149
  audios = [audio_tts_bg]
150
  if audio_original:
151
  audios.append(audio_original.set_duration(video_final.duration))
152
 
153
- audio_final = CompositeAudioClip(audios)
154
 
155
- # Renderizar video final
156
  with tempfile.NamedTemporaryFile(suffix=".mp4", delete=False) as tmp:
157
  video_final.set_audio(audio_final).write_videofile(
158
  tmp.name,
159
  codec="libx264",
160
  audio_codec="aac",
161
  fps=24,
162
- threads=4
 
163
  )
164
  eliminar_archivo_tiempo(tmp.name)
165
  return tmp.name
@@ -167,7 +151,6 @@ async def procesar_video(video_input, texto_tts, voz_seleccionada, metodo_corte,
167
  logging.error(f" fallo general: {str(e)}")
168
  raise
169
 
170
- # Interfaz Gradio
171
  with gr.Blocks() as demo:
172
  gr.Markdown("# Editor de Video con IA")
173
 
@@ -197,25 +180,16 @@ with gr.Blocks() as demo:
197
  label="Segundos por corte (manual)"
198
  )
199
 
200
- # Ejemplos en footer
201
  with gr.Accordion("Ejemplos de Uso", open=False):
202
  gr.Examples(
203
- examples=[
204
- [EJEMPLO_VIDEO, "隆Hola! Esto es una prueba. Suscr铆bete al canal y activa la campanita."],
205
- ],
206
  inputs=[video_input, texto_tts],
207
  label="Ejemplos"
208
  )
209
 
210
  procesar_btn.click(
211
  procesar_video,
212
- inputs=[
213
- video_input,
214
- texto_tts,
215
- voz_seleccionada,
216
- metodo_corte,
217
- duracion_corte
218
- ],
219
  outputs=video_output
220
  )
221
 
 
10
  import gradio as gr
11
  from pydub import AudioSegment
12
 
 
13
  logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
14
 
 
15
  INTRO_VIDEO = "introvideo.mp4"
16
  OUTRO_VIDEO = "outrovideo.mp4"
17
  MUSIC_BG = "musicafondo.mp3"
 
19
  WATERMARK = "watermark.png"
20
  EJEMPLO_VIDEO = "ejemplo.mp4"
21
 
 
22
  for file in [INTRO_VIDEO, OUTRO_VIDEO, MUSIC_BG, FX_SOUND, WATERMARK, EJEMPLO_VIDEO]:
23
  if not os.path.exists(file):
24
  logging.error(f"Falta archivo necesario: {file}")
25
  raise FileNotFoundError(f"Falta: {file}")
26
 
27
  def eliminar_archivo_tiempo(ruta, delay=1800):
 
28
  def eliminar():
29
  try:
30
  if os.path.exists(ruta):
 
35
  Timer(delay, eliminar).start()
36
 
37
  def validar_texto(texto):
 
38
  texto_limpio = texto.strip()
39
  if len(texto_limpio) < 3:
40
  raise gr.Error("鈿狅笍 El texto debe tener al menos 3 caracteres")
 
42
  raise gr.Error("鈿狅笍 Caracteres no permitidos detectados")
43
 
44
  async def procesar_audio(texto, voz, duracion_total, duracion_intro):
 
45
  temp_files = []
46
  try:
 
47
  validar_texto(texto)
 
 
48
  communicate = edge_tts.Communicate(texto, voz)
49
+
50
  with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as tmp:
51
  await communicate.save(tmp.name)
52
  tts_audio = AudioFileClip(tmp.name)
53
  temp_files.append(tmp.name)
54
 
 
55
  if tts_audio.duration < 0.5:
56
  raise RuntimeError(f"Audio TTS inv谩lido ({tts_audio.duration}s)")
57
 
 
58
  bg_music = AudioSegment.from_mp3(MUSIC_BG)
59
+ needed_ms = int(duracion_total * 1000)
60
  repeticiones = needed_ms // len(bg_music) + 1
61
+ bg_music = bg_music * repeticiones
62
  bg_music = bg_music[:needed_ms].fade_out(5000)
63
 
64
  with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as tmp:
 
66
  bg_audio = AudioFileClip(tmp.name).volumex(0.15)
67
  temp_files.append(tmp.name)
68
 
 
69
  audio_final = CompositeAudioClip([
70
+ bg_audio.set_duration(duracion_total),
71
  tts_audio.volumex(0.85).set_start(duracion_intro)
72
+ .set_duration(duracion_total - duracion_intro)
73
  ])
74
 
75
  return audio_final
76
+
77
  except Exception as e:
78
  logging.error(f" fallo en audio: {str(e)}")
79
  raise
80
  finally:
 
81
  for file in temp_files:
82
  try:
83
  os.remove(file)
 
85
  logging.warning(f"Error limpiando {file}: {e}")
86
 
87
  def agregar_transiciones(clips):
 
88
  try:
89
  fx_audio = AudioFileClip(FX_SOUND).subclip(0, 0.5)
90
  watermark = (ImageClip(WATERMARK)
 
96
  for i, clip in enumerate(clips):
97
  clip_watermarked = CompositeVideoClip([clip, watermark])
98
 
 
99
  if i > 0 and i % 40 == 0:
100
  transicion = CompositeVideoClip([watermark.set_duration(0.5)]).set_audio(fx_audio)
101
  clips_finales.append(transicion)
 
109
 
110
  async def procesar_video(video_input, texto_tts, voz_seleccionada, metodo_corte, duracion_corte):
111
  try:
 
112
  video_original = VideoFileClip(video_input)
113
  audio_original = video_original.audio.volumex(0.7) if video_original.audio else None
114
 
 
115
  clips = []
116
  if metodo_corte == "manual":
117
  for i in range(math.ceil(video_original.duration / duracion_corte)):
118
  clips.append(video_original.subclip(i*duracion_corte, (i+1)*duracion_corte))
119
  else:
120
+ clips = [video_original.subclip(i, min(i+40, video_original.duration))
121
+ for i in range(0, math.ceil(video_original.duration), 40)]
122
 
 
123
  video_editado = agregar_transiciones(clips)
 
 
124
  intro = VideoFileClip(INTRO_VIDEO)
125
  outro = VideoFileClip(OUTRO_VIDEO)
126
  video_final = concatenate_videoclips([intro, video_editado, outro])
127
 
 
128
  duracion_total = video_final.duration
129
+ duracion_intro = intro.duration
130
+
131
+ audio_tts_bg = await procesar_audio(texto_tts, voz_seleccionada, duracion_total, duracion_intro)
132
 
 
133
  audios = [audio_tts_bg]
134
  if audio_original:
135
  audios.append(audio_original.set_duration(video_final.duration))
136
 
137
+ audio_final = CompositeAudioClip(audios).set_duration(video_final.duration)
138
 
 
139
  with tempfile.NamedTemporaryFile(suffix=".mp4", delete=False) as tmp:
140
  video_final.set_audio(audio_final).write_videofile(
141
  tmp.name,
142
  codec="libx264",
143
  audio_codec="aac",
144
  fps=24,
145
+ threads=4,
146
+ verbose=False
147
  )
148
  eliminar_archivo_tiempo(tmp.name)
149
  return tmp.name
 
151
  logging.error(f" fallo general: {str(e)}")
152
  raise
153
 
 
154
  with gr.Blocks() as demo:
155
  gr.Markdown("# Editor de Video con IA")
156
 
 
180
  label="Segundos por corte (manual)"
181
  )
182
 
 
183
  with gr.Accordion("Ejemplos de Uso", open=False):
184
  gr.Examples(
185
+ examples=[[EJEMPLO_VIDEO, "隆Hola! Esto es una prueba. Suscr铆bete al canal y activa la campanita."]],
 
 
186
  inputs=[video_input, texto_tts],
187
  label="Ejemplos"
188
  )
189
 
190
  procesar_btn.click(
191
  procesar_video,
192
+ inputs=[video_input, texto_tts, voz_seleccionada, metodo_corte, duracion_corte],
 
 
 
 
 
 
193
  outputs=video_output
194
  )
195