gnosticdev commited on
Commit
f7e8b14
verified
1 Parent(s): 19a58f2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +48 -97
app.py CHANGED
@@ -1,9 +1,7 @@
1
- import math
2
  import tempfile
3
  import logging
4
  import os
5
  import asyncio
6
- from threading import Timer # Importaci贸n faltante
7
  from moviepy.editor import *
8
  import edge_tts
9
  import gradio as gr
@@ -16,18 +14,15 @@ logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(
16
  INTRO_VIDEO = "introvideo.mp4"
17
  OUTRO_VIDEO = "outrovideo.mp4"
18
  MUSIC_BG = "musicafondo.mp3"
19
- FX_SOUND = "fxsound.mp3"
20
- WATERMARK = "watermark.png"
21
  EJEMPLO_VIDEO = "ejemplo.mp4"
22
 
23
  # Validar existencia de archivos
24
- for file in [INTRO_VIDEO, OUTRO_VIDEO, MUSIC_BG, FX_SOUND, WATERMARK, EJEMPLO_VIDEO]:
25
  if not os.path.exists(file):
26
  logging.error(f"Falta archivo necesario: {file}")
27
  raise FileNotFoundError(f"Falta: {file}")
28
 
29
  def eliminar_archivo_tiempo(ruta, delay=1800):
30
- """Elimina archivos temporales despu茅s de 30 minutos"""
31
  def eliminar():
32
  try:
33
  if os.path.exists(ruta):
@@ -35,34 +30,50 @@ def eliminar_archivo_tiempo(ruta, delay=1800):
35
  logging.info(f"Archivo eliminado: {ruta}")
36
  except Exception as e:
37
  logging.error(f"Error al eliminar {ruta}: {e}")
 
38
  Timer(delay, eliminar).start()
39
 
40
  def validar_texto(texto):
41
- """Valida el texto para evitar errores en TTS"""
42
  texto_limpio = texto.strip()
43
  if len(texto_limpio) < 3:
44
  raise gr.Error("鈿狅笍 El texto debe tener al menos 3 caracteres")
45
  if any(c in texto_limpio for c in ["|", "\n", "\r"]):
46
  raise gr.Error("鈿狅笍 Caracteres no permitidos detectados")
47
 
48
- async def procesar_audio(texto, voz, duracion_maxima):
49
- """Genera TTS y lo limita a la duraci贸n m谩xima disponible"""
50
  temp_files = []
51
  try:
52
  validar_texto(texto)
53
-
54
- # Generar TTS
55
  communicate = edge_tts.Communicate(texto, voz)
 
56
  with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as tmp_tts:
57
  await communicate.save(tmp_tts.name)
58
  tts_audio = AudioFileClip(tmp_tts.name)
59
  temp_files.append(tmp_tts.name)
60
 
61
- # Limitar TTS a la duraci贸n m谩xima
62
- if tts_audio.duration > duracion_maxima:
63
- tts_audio = tts_audio.subclip(0, duracion_maxima)
 
 
 
 
 
 
 
 
 
 
 
 
64
 
65
- return tts_audio
 
 
 
 
 
 
66
 
67
  except Exception as e:
68
  logging.error(f" fallo en audio: {str(e)}")
@@ -74,82 +85,33 @@ async def procesar_audio(texto, voz, duracion_maxima):
74
  except Exception as e:
75
  logging.warning(f"Error limpiando {file}: {e}")
76
 
77
- def agregar_transiciones(clips):
78
- """Agrega transiciones visuales cada 40 segundos"""
79
- try:
80
- fx_audio = AudioFileClip(FX_SOUND).subclip(0, 0.5)
81
- watermark = (ImageClip(WATERMARK)
82
- .set_duration(0.5)
83
- .resize(height=50)
84
- .set_pos(("right", "bottom")))
85
-
86
- clips_finales = []
87
- for i, clip in enumerate(clips):
88
- clip_watermarked = CompositeVideoClip([clip, watermark])
89
-
90
- if i > 0 and i % 40 == 0:
91
- transicion = CompositeVideoClip([watermark.set_duration(0.5)]).set_audio(fx_audio)
92
- clips_finales.append(transicion)
93
-
94
- clips_finales.append(clip_watermarked)
95
-
96
- return concatenate_videoclips(clips_finales, method="compose")
97
- except Exception as e:
98
- logging.error(f"Error en transiciones: {e}")
99
- return concatenate_videoclips(clips)
100
-
101
- async def procesar_video(video_input, texto_tts, voz_seleccionada, metodo_corte, duracion_corte):
102
  try:
103
- # Cargar video original
104
- video_original = VideoFileClip(video_input)
105
- audio_original = video_original.audio.volumex(0.7) if video_original.audio else None
106
-
107
- # Cortar video seg煤n m茅todo
108
- clips = []
109
- if metodo_corte == "manual":
110
- for i in range(math.ceil(video_original.duration / duracion_corte)):
111
- clips.append(video_original.subclip(i*duracion_corte, (i+1)*duracion_corte))
112
- else:
113
- clips = [video_original.subclip(i, min(i+40, video_original.duration))
114
- for i in range(0, math.ceil(video_original.duration), 40)]
115
-
116
- # Procesar transiciones visuales
117
- video_editado = agregar_transiciones(clips)
118
- video_editado_duration = video_editado.duration
119
-
120
- # Combinar con intro/outro
121
  intro = VideoFileClip(INTRO_VIDEO)
122
  outro = VideoFileClip(OUTRO_VIDEO)
 
123
 
124
- # M煤sica de fondo solo para el video editado
125
- bg_music = AudioSegment.from_mp3(MUSIC_BG)
126
- needed_ms = int(video_editado_duration * 1000)
127
- repeticiones = needed_ms // len(bg_music) + 1
128
- bg_music = bg_music * repeticiones
129
- bg_music = bg_music[:needed_ms].fade_out(5000)
130
-
131
- with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as tmp_bg:
132
- bg_music.export(tmp_bg.name, format="mp3")
133
- bg_audio = AudioFileClip(tmp_bg.name).volumex(0.15)
134
-
135
- # Duraci贸n m谩xima para el TTS (despu茅s de la intro)
136
- max_tts_time = video_editado_duration
137
-
138
- # Procesar TTS
139
- tts_audio = await procesar_audio(texto_tts, voz_seleccionada, max_tts_time)
140
 
141
- # Combinar audios para el video editado
142
- audios_editado = [bg_audio]
143
- if audio_original:
144
- audios_editado.append(audio_original.set_duration(video_editado_duration))
145
- audios_editado.append(tts_audio.set_start(0))
 
 
146
 
147
- audio_editado = CompositeAudioClip(audios_editado).set_duration(video_editado_duration)
148
- video_editado = video_editado.set_audio(audio_editado)
149
 
150
- # Concatenar intro, video editado y outro
151
- video_final = concatenate_videoclips([intro, video_editado, outro])
152
- duracion_total = video_final.duration
 
 
153
 
154
  # Renderizar video final
155
  with tempfile.NamedTemporaryFile(suffix=".mp4", delete=False) as tmp:
@@ -186,27 +148,16 @@ with gr.Blocks() as demo:
186
  procesar_btn = gr.Button("Generar Video")
187
  video_output = gr.Video(label="Video Procesado")
188
 
189
- with gr.Tab("Ajustes"):
190
- metodo_corte = gr.Radio(
191
- ["inteligente", "manual"],
192
- label="M茅todo de corte",
193
- value="inteligente"
194
- )
195
- duracion_corte = gr.Slider(
196
- 1, 60, 10,
197
- label="Segundos por corte (manual)"
198
- )
199
-
200
  with gr.Accordion("Ejemplos de Uso", open=False):
201
  gr.Examples(
202
- examples=[[EJEMPLO_VIDEO, "隆Hola! Esto es una prueba. Suscr铆bete al canal y activa la campanita."]],
203
  inputs=[video_input, texto_tts],
204
  label="Ejemplos"
205
  )
206
 
207
  procesar_btn.click(
208
  procesar_video,
209
- inputs=[video_input, texto_tts, voz_seleccionada, metodo_corte, duracion_corte],
210
  outputs=video_output
211
  )
212
 
 
 
1
  import tempfile
2
  import logging
3
  import os
4
  import asyncio
 
5
  from moviepy.editor import *
6
  import edge_tts
7
  import gradio as gr
 
14
  INTRO_VIDEO = "introvideo.mp4"
15
  OUTRO_VIDEO = "outrovideo.mp4"
16
  MUSIC_BG = "musicafondo.mp3"
 
 
17
  EJEMPLO_VIDEO = "ejemplo.mp4"
18
 
19
  # Validar existencia de archivos
20
+ for file in [INTRO_VIDEO, OUTRO_VIDEO, MUSIC_BG, EJEMPLO_VIDEO]:
21
  if not os.path.exists(file):
22
  logging.error(f"Falta archivo necesario: {file}")
23
  raise FileNotFoundError(f"Falta: {file}")
24
 
25
  def eliminar_archivo_tiempo(ruta, delay=1800):
 
26
  def eliminar():
27
  try:
28
  if os.path.exists(ruta):
 
30
  logging.info(f"Archivo eliminado: {ruta}")
31
  except Exception as e:
32
  logging.error(f"Error al eliminar {ruta}: {e}")
33
+ from threading import Timer
34
  Timer(delay, eliminar).start()
35
 
36
  def validar_texto(texto):
 
37
  texto_limpio = texto.strip()
38
  if len(texto_limpio) < 3:
39
  raise gr.Error("鈿狅笍 El texto debe tener al menos 3 caracteres")
40
  if any(c in texto_limpio for c in ["|", "\n", "\r"]):
41
  raise gr.Error("鈿狅笍 Caracteres no permitidos detectados")
42
 
43
+ async def procesar_audio(texto, voz, duracion_video_editado, duracion_intro):
 
44
  temp_files = []
45
  try:
46
  validar_texto(texto)
 
 
47
  communicate = edge_tts.Communicate(texto, voz)
48
+
49
  with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as tmp_tts:
50
  await communicate.save(tmp_tts.name)
51
  tts_audio = AudioFileClip(tmp_tts.name)
52
  temp_files.append(tmp_tts.name)
53
 
54
+ # Limitar TTS al tiempo disponible despu茅s de la intro
55
+ if tts_audio.duration > duracion_video_editado:
56
+ tts_audio = tts_audio.subclip(0, duracion_video_editado)
57
+
58
+ # Preparar m煤sica de fondo en loop
59
+ bg_music = AudioSegment.from_mp3(MUSIC_BG)
60
+ needed_ms = int(duracion_video_editado * 1000)
61
+ repeticiones = needed_ms // len(bg_music) + 1
62
+ bg_music = bg_music * repeticiones
63
+ bg_music = bg_music[:needed_ms].fade_out(5000)
64
+
65
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as tmp_bg:
66
+ bg_music.export(tmp_bg.name, format="mp3")
67
+ bg_audio = AudioFileClip(tmp_bg.name).volumex(0.15)
68
+ temp_files.append(tmp_bg.name)
69
 
70
+ # Combinar audios
71
+ audio_final = CompositeAudioClip([
72
+ bg_audio.set_duration(duracion_video_editado),
73
+ tts_audio.volumex(0.85).set_start(0) # Comienza al inicio del video editado
74
+ ]).set_duration(duracion_video_editado)
75
+
76
+ return audio_final
77
 
78
  except Exception as e:
79
  logging.error(f" fallo en audio: {str(e)}")
 
85
  except Exception as e:
86
  logging.warning(f"Error limpiando {file}: {e}")
87
 
88
+ async def procesar_video(video_input, texto_tts, voz_seleccionada):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  try:
90
+ # Cargar componentes
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
  intro = VideoFileClip(INTRO_VIDEO)
92
  outro = VideoFileClip(OUTRO_VIDEO)
93
+ video_original = VideoFileClip(video_input)
94
 
95
+ # Duraciones
96
+ duracion_intro = intro.duration
97
+ duracion_video_editado = video_original.duration
 
 
 
 
 
 
 
 
 
 
 
 
 
98
 
99
+ # Procesar audio
100
+ audio_final = await procesar_audio(
101
+ texto_tts,
102
+ voz_seleccionada,
103
+ duracion_video_editado,
104
+ duracion_intro
105
+ )
106
 
107
+ # Combinar video original con audio
108
+ video_con_audio = video_original.set_audio(audio_final)
109
 
110
+ # Concatenar intro + video + outro
111
+ video_final = concatenate_videoclips(
112
+ [intro, video_con_audio, outro],
113
+ method="chain" # Evita el efecto grid
114
+ )
115
 
116
  # Renderizar video final
117
  with tempfile.NamedTemporaryFile(suffix=".mp4", delete=False) as tmp:
 
148
  procesar_btn = gr.Button("Generar Video")
149
  video_output = gr.Video(label="Video Procesado")
150
 
 
 
 
 
 
 
 
 
 
 
 
151
  with gr.Accordion("Ejemplos de Uso", open=False):
152
  gr.Examples(
153
+ examples=[[EJEMPLO_VIDEO, "隆Hola! Esto es una prueba. Suscr铆bete al canal."]],
154
  inputs=[video_input, texto_tts],
155
  label="Ejemplos"
156
  )
157
 
158
  procesar_btn.click(
159
  procesar_video,
160
+ inputs=[video_input, texto_tts, voz_seleccionada],
161
  outputs=video_output
162
  )
163