gnosticdev commited on
Commit
8982210
verified
1 Parent(s): 0839ffa

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +56 -63
app.py CHANGED
@@ -14,7 +14,7 @@ logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(
14
  INTRO_VIDEO = "introvideo.mp4"
15
  OUTRO_VIDEO = "outrovideo.mp4"
16
  MUSIC_BG = "musicafondo.mp3"
17
- GLITCH_SOUND = "fxsound.mp3" # Efecto de sonido para glitches
18
  EJEMPLO_VIDEO = "ejemplo.mp4"
19
 
20
  # Validar existencia de archivos
@@ -23,25 +23,23 @@ for file in [INTRO_VIDEO, OUTRO_VIDEO, MUSIC_BG, GLITCH_SOUND, EJEMPLO_VIDEO]:
23
  logging.error(f"Falta archivo necesario: {file}")
24
  raise FileNotFoundError(f"Falta: {file}")
25
 
26
- def eliminar_archivo_tiempo(ruta, delay=1800):
27
- def eliminar():
28
- try:
29
- if os.path.exists(ruta):
30
- os.remove(ruta)
31
- logging.info(f"Archivo eliminado: {ruta}")
32
- except Exception as e:
33
- logging.error(f"Error al eliminar {ruta}: {e}")
34
- from threading import Timer
35
- Timer(delay, eliminar).start()
36
 
37
  async def procesar_audio(texto, voz, duracion_video, audio_original):
38
  temp_files = []
39
  try:
40
- # Validar texto
41
  if not texto.strip():
42
  raise ValueError("El texto para TTS no puede estar vac铆o.")
43
 
44
- # Dividir el texto en fragmentos si es demasiado largo
45
  def dividir_texto(texto, max_length=3000):
46
  return [texto[i:i + max_length] for i in range(0, len(texto), max_length)]
47
 
@@ -49,27 +47,18 @@ async def procesar_audio(texto, voz, duracion_video, audio_original):
49
  audios_tts = []
50
 
51
  for fragmento in fragmentos:
52
- # Generar TTS
53
  communicate = edge_tts.Communicate(fragmento, voz)
54
  with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as tmp_tts:
55
- try:
56
- await communicate.save(tmp_tts.name)
57
- except edge_tts.exceptions.NoAudioReceived as e:
58
- logging.error(f"Error en TTS: {str(e)}")
59
- raise ValueError("No se pudo generar el audio. Verifica tu conexi贸n o los par谩metros del TTS.")
60
-
61
  tts_audio = AudioFileClip(tmp_tts.name)
62
  temp_files.append(tmp_tts.name)
63
  audios_tts.append(tts_audio)
64
 
65
- # Combinar todos los fragmentos de TTS
66
  tts_audio_final = concatenate_audioclips(audios_tts)
67
 
68
- # Limitar TTS al video
69
  if tts_audio_final.duration > duracion_video:
70
  tts_audio_final = tts_audio_final.subclip(0, duracion_video)
71
 
72
- # Preparar m煤sica de fondo en loop (OPTIMIZADO)
73
  needed_ms = int(duracion_video * 1000)
74
  bg_music = AudioSegment.from_mp3(MUSIC_BG)
75
  repeticiones = needed_ms // len(bg_music) + 1
@@ -81,11 +70,10 @@ async def procesar_audio(texto, voz, duracion_video, audio_original):
81
  bg_audio = AudioFileClip(tmp_bg.name).volumex(0.15)
82
  temp_files.append(tmp_bg.name)
83
 
84
- # Combinar audios
85
  audios = [bg_audio.set_duration(duracion_video)]
86
  if audio_original:
87
- audios.append(audio_original.volumex(0.7)) # Audio original al 70%
88
- audios.append(tts_audio_final.volumex(0.85).set_start(0)) # TTS al 85%
89
 
90
  audio_final = CompositeAudioClip(audios).set_duration(duracion_video)
91
  return audio_final
@@ -101,34 +89,42 @@ async def procesar_audio(texto, voz, duracion_video, audio_original):
101
  logging.warning(f"Error limpiando {file}: {e}")
102
 
103
  def aplicar_glitch(video_clip):
104
- """Aplica un efecto de glitch al video (OPTIMIZADO)"""
105
  def glitch_effect(frame):
106
  import numpy as np
 
107
  height, width, _ = frame.shape
108
- offset = np.random.randint(-5, 5) # Reducido el rango de glitch
109
- if offset == 0: return frame # Evitar operaciones innecesarias
 
 
 
 
 
110
  if offset > 0:
111
- offset = min(offset, height)
112
- if offset < 0:
113
- offset = max(offset, -height + 1)
114
- if height > 0:
115
  frame[offset:, :] = np.roll(frame[:-offset, :], -offset, axis=0)
 
 
 
116
  return frame
117
 
118
  return video_clip.fl_image(glitch_effect)
119
 
120
  async def procesar_video(video_input, texto_tts, voz_seleccionada):
121
  try:
122
- # Cargar componentes con OPTIMIZACI脫N DE MEMORIA
123
  intro = VideoFileClip(INTRO_VIDEO, target_resolution=(1080, 1920))
124
  outro = VideoFileClip(OUTRO_VIDEO, target_resolution=(1080, 1920))
125
  video_original = VideoFileClip(video_input, target_resolution=(1080, 1920))
126
  audio_original = video_original.audio
127
 
128
- # Duraci贸n del video editado (sin intro/outro)
 
 
 
 
129
  duracion_video = video_original.duration
130
 
131
- # Procesar audio
132
  audio_final = await procesar_audio(
133
  texto_tts,
134
  voz_seleccionada,
@@ -136,13 +132,12 @@ async def procesar_video(video_input, texto_tts, voz_seleccionada):
136
  audio_original
137
  )
138
 
139
- # Dividir el video en segmentos de 20 segundos y eliminar 2 segundos en cada corte
140
  segment_duration = 20
141
- overlap = 2 # Segundos a eliminar en cada corte
142
  num_segments = int(duracion_video // (segment_duration - overlap)) + 1
143
  segments = []
144
  glitch_clips = []
145
- glitch_sound = AudioFileClip(GLITCH_SOUND).volumex(0.5) # Pre-cargado una vez
146
 
147
  start_time = 0
148
  for i in range(num_segments):
@@ -150,60 +145,58 @@ async def procesar_video(video_input, texto_tts, voz_seleccionada):
150
  if start_time >= duracion_video:
151
  break
152
 
153
- # Extraer el segmento
154
  segment = video_original.subclip(start_time, end_time)
155
 
156
- # Aplicar glitch cada 2 segmentos (40 segundos) en lugar de cada 1
157
- if i % 2 == 0 and i > 0:
158
- glitch_segment = aplicar_glitch(segment.subclip(0, 0.3)) # Duraci贸n reducida
159
  glitch_sound_clip = glitch_sound.set_start(start_time)
160
  glitch_clips.append(glitch_sound_clip)
161
  segment = concatenate_videoclips([glitch_segment, segment.subclip(0.3)], method="compose")
162
 
163
  segments.append(segment)
164
-
165
- # Avanzar al siguiente segmento, eliminando 2 segundos
166
  start_time += segment_duration - overlap
167
 
168
- # Combinar los segmentos procesados
169
  video_final = concatenate_videoclips(segments)
170
-
171
- # Combinar audio con efectos de glitch
172
  audio_final = CompositeAudioClip([audio_final] + glitch_clips).set_duration(video_final.duration)
173
-
174
- # Combinar video con audio
175
  video_con_audio = video_final.set_audio(audio_final)
176
 
177
- # Concatenar intro + video + outro
178
- video_final = concatenate_videoclips(
179
- [intro, video_con_audio, outro],
180
- method="compose",
181
- padding=0
182
- )
183
 
184
- # Renderizar video final con OPTIMIZACIONES DE CODIFICACI脫N
185
  with tempfile.NamedTemporaryFile(suffix=".mp4", delete=False) as tmp:
186
  video_final.write_videofile(
187
  tmp.name,
188
  codec="libx264",
189
  audio_codec="aac",
190
- fps=video_original.fps,
191
  threads=4,
192
- bitrate="5M", # Control de tama帽o
193
  ffmpeg_params=[
194
- "-preset", "ultrafast", # Velocidad de codificaci贸n
195
- "-crf", "23", # Calidad est谩ndar
196
- "-movflags", "+faststart", # Para streaming
197
- "-aspect", "16:9",
198
  "-vf", "scale=1920:1080"
199
  ],
200
  verbose=False
201
  )
202
  eliminar_archivo_tiempo(tmp.name)
203
  return tmp.name
 
204
  except Exception as e:
205
  logging.error(f" fallo general: {str(e)}")
206
  raise
 
 
 
 
 
 
 
 
207
 
208
  # Interfaz Gradio
209
  with gr.Blocks() as demo:
 
14
  INTRO_VIDEO = "introvideo.mp4"
15
  OUTRO_VIDEO = "outrovideo.mp4"
16
  MUSIC_BG = "musicafondo.mp3"
17
+ GLITCH_SOUND = "fxsound.mp3"
18
  EJEMPLO_VIDEO = "ejemplo.mp4"
19
 
20
  # Validar existencia de archivos
 
23
  logging.error(f"Falta archivo necesario: {file}")
24
  raise FileNotFoundError(f"Falta: {file}")
25
 
26
+ def eliminar_archivo_tiempo(ruta):
27
+ try:
28
+ if os.path.exists(ruta):
29
+ os.remove(ruta)
30
+ logging.info(f"Archivo eliminado: {ruta}")
31
+ except Exception as e:
32
+ logging.error(f"Error al eliminar {ruta}: {e}")
33
+ finally:
34
+ import gc
35
+ gc.collect() # Forzar liberaci贸n de memoria
36
 
37
  async def procesar_audio(texto, voz, duracion_video, audio_original):
38
  temp_files = []
39
  try:
 
40
  if not texto.strip():
41
  raise ValueError("El texto para TTS no puede estar vac铆o.")
42
 
 
43
  def dividir_texto(texto, max_length=3000):
44
  return [texto[i:i + max_length] for i in range(0, len(texto), max_length)]
45
 
 
47
  audios_tts = []
48
 
49
  for fragmento in fragmentos:
 
50
  communicate = edge_tts.Communicate(fragmento, voz)
51
  with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as tmp_tts:
52
+ await communicate.save(tmp_tts.name)
 
 
 
 
 
53
  tts_audio = AudioFileClip(tmp_tts.name)
54
  temp_files.append(tmp_tts.name)
55
  audios_tts.append(tts_audio)
56
 
 
57
  tts_audio_final = concatenate_audioclips(audios_tts)
58
 
 
59
  if tts_audio_final.duration > duracion_video:
60
  tts_audio_final = tts_audio_final.subclip(0, duracion_video)
61
 
 
62
  needed_ms = int(duracion_video * 1000)
63
  bg_music = AudioSegment.from_mp3(MUSIC_BG)
64
  repeticiones = needed_ms // len(bg_music) + 1
 
70
  bg_audio = AudioFileClip(tmp_bg.name).volumex(0.15)
71
  temp_files.append(tmp_bg.name)
72
 
 
73
  audios = [bg_audio.set_duration(duracion_video)]
74
  if audio_original:
75
+ audios.append(audio_original.volumex(0.7))
76
+ audios.append(tts_audio_final.volumex(0.85).set_start(0))
77
 
78
  audio_final = CompositeAudioClip(audios).set_duration(duracion_video)
79
  return audio_final
 
89
  logging.warning(f"Error limpiando {file}: {e}")
90
 
91
  def aplicar_glitch(video_clip):
92
+ """Versi贸n corregida del efecto glitch"""
93
  def glitch_effect(frame):
94
  import numpy as np
95
+ frame = frame.copy() # 隆Clave para evitar el error de asignaci贸n!
96
  height, width, _ = frame.shape
97
+ offset = np.random.randint(-5, 5)
98
+
99
+ if offset == 0 or height <= 0:
100
+ return frame
101
+
102
+ offset = max(-height + 1, min(offset, height))
103
+
104
  if offset > 0:
 
 
 
 
105
  frame[offset:, :] = np.roll(frame[:-offset, :], -offset, axis=0)
106
+ else:
107
+ frame[:offset, :] = np.roll(frame[-offset:, :], -offset, axis=0)
108
+
109
  return frame
110
 
111
  return video_clip.fl_image(glitch_effect)
112
 
113
  async def procesar_video(video_input, texto_tts, voz_seleccionada):
114
  try:
115
+ # Carga optimizada con control de memoria
116
  intro = VideoFileClip(INTRO_VIDEO, target_resolution=(1080, 1920))
117
  outro = VideoFileClip(OUTRO_VIDEO, target_resolution=(1080, 1920))
118
  video_original = VideoFileClip(video_input, target_resolution=(1080, 1920))
119
  audio_original = video_original.audio
120
 
121
+ # Liberar recursos inmediatamente
122
+ intro.reader.close()
123
+ outro.reader.close()
124
+ video_original.reader.close()
125
+
126
  duracion_video = video_original.duration
127
 
 
128
  audio_final = await procesar_audio(
129
  texto_tts,
130
  voz_seleccionada,
 
132
  audio_original
133
  )
134
 
 
135
  segment_duration = 20
136
+ overlap = 2
137
  num_segments = int(duracion_video // (segment_duration - overlap)) + 1
138
  segments = []
139
  glitch_clips = []
140
+ glitch_sound = AudioFileClip(GLITCH_SOUND).volumex(0.5)
141
 
142
  start_time = 0
143
  for i in range(num_segments):
 
145
  if start_time >= duracion_video:
146
  break
147
 
 
148
  segment = video_original.subclip(start_time, end_time)
149
 
150
+ # Aplicar glitch solo en segmentos v谩lidos
151
+ if i % 2 == 0 and i > 0 and (end_time - start_time) > 0.5:
152
+ glitch_segment = aplicar_glitch(segment.subclip(0, 0.3))
153
  glitch_sound_clip = glitch_sound.set_start(start_time)
154
  glitch_clips.append(glitch_sound_clip)
155
  segment = concatenate_videoclips([glitch_segment, segment.subclip(0.3)], method="compose")
156
 
157
  segments.append(segment)
 
 
158
  start_time += segment_duration - overlap
159
 
 
160
  video_final = concatenate_videoclips(segments)
 
 
161
  audio_final = CompositeAudioClip([audio_final] + glitch_clips).set_duration(video_final.duration)
 
 
162
  video_con_audio = video_final.set_audio(audio_final)
163
 
164
+ # Recargar intro/outro para evitar conflictos de memoria
165
+ intro = VideoFileClip(INTRO_VIDEO, target_resolution=(1080, 1920))
166
+ outro = VideoFileClip(OUTRO_VIDEO, target_resolution=(1080, 1920))
167
+ video_final = concatenate_videoclips([intro, video_con_audio, outro], method="compose")
 
 
168
 
169
+ # Renderizado optimizado
170
  with tempfile.NamedTemporaryFile(suffix=".mp4", delete=False) as tmp:
171
  video_final.write_videofile(
172
  tmp.name,
173
  codec="libx264",
174
  audio_codec="aac",
175
+ fps=24, # FPS fijo para estabilidad
176
  threads=4,
177
+ bitrate="5M",
178
  ffmpeg_params=[
179
+ "-preset", "ultrafast",
180
+ "-crf", "23",
181
+ "-movflags", "+faststart",
 
182
  "-vf", "scale=1920:1080"
183
  ],
184
  verbose=False
185
  )
186
  eliminar_archivo_tiempo(tmp.name)
187
  return tmp.name
188
+
189
  except Exception as e:
190
  logging.error(f" fallo general: {str(e)}")
191
  raise
192
+ finally:
193
+ # Forzar cierre de recursos
194
+ try:
195
+ intro.close()
196
+ outro.close()
197
+ video_original.close()
198
+ except:
199
+ pass
200
 
201
  # Interfaz Gradio
202
  with gr.Blocks() as demo: