gnosticdev commited on
Commit
63e5b79
verified
1 Parent(s): 4a04dae

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +64 -86
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
@@ -37,11 +37,10 @@ def eliminar_archivo_tiempo(ruta, delay=1800):
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,29 +48,20 @@ 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
73
- bg_music = AudioSegment.from_mp3(MUSIC_BG)
74
  needed_ms = int(duracion_video * 1000)
 
75
  repeticiones = needed_ms // len(bg_music) + 1
76
  bg_music = bg_music * repeticiones
77
  bg_music = bg_music[:needed_ms].fade_out(1000)
@@ -81,17 +71,17 @@ 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
92
 
93
  except Exception as e:
94
- logging.error(f" fallo en audio: {str(e)}")
95
  raise
96
  finally:
97
  for file in temp_files:
@@ -101,16 +91,12 @@ 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."""
105
  def glitch_effect(frame):
106
  import numpy as np
 
107
  height, width, _ = frame.shape
108
- offset = np.random.randint(-10, 10)
109
- if offset > 0:
110
- offset = min(offset, height)
111
- if offset < 0:
112
- offset = max(offset, -height + 1)
113
- if offset!= 0 and height > 0:
114
  frame[offset:, :] = np.roll(frame[:-offset, :], -offset, axis=0)
115
  return frame
116
 
@@ -118,16 +104,19 @@ def aplicar_glitch(video_clip):
118
 
119
  async def procesar_video(video_input, texto_tts, voz_seleccionada):
120
  try:
121
- # Cargar componentes
122
- intro = VideoFileClip(INTRO_VIDEO)
123
- outro = VideoFileClip(OUTRO_VIDEO)
124
- video_original = VideoFileClip(video_input)
125
  audio_original = video_original.audio
126
 
127
- # Duraci贸n del video editado (sin intro/outro)
 
 
 
128
  duracion_video = video_original.duration
 
129
 
130
- # Procesar audio
131
  audio_final = await procesar_audio(
132
  texto_tts,
133
  voz_seleccionada,
@@ -135,85 +124,74 @@ async def procesar_video(video_input, texto_tts, voz_seleccionada):
135
  audio_original
136
  )
137
 
138
- # Redimensionar todos los clips a 1920x1080
139
- target_width = 1920
140
- target_height = 1080
141
-
142
- # Redimensionar intro
143
- intro_resized = intro.resize((target_width, target_height))
144
-
145
- # Redimensionar outro
146
- outro_resized = outro.resize((target_width, target_height))
147
-
148
- # Redimensionar video principal
149
- video_resized = video_original.resize((target_width, target_height))
150
-
151
- # Dividir el video en segmentos de 20 segundos y eliminar 2 segundos en cada corte
152
- segment_duration = 20
153
- overlap = 2 # Segundos a eliminar en cada corte
154
- num_segments = int(duracion_video // (segment_duration - overlap)) + 1
155
  segments = []
156
  glitch_clips = []
157
- glitch_sound = AudioFileClip(GLITCH_SOUND)
158
 
159
  start_time = 0
160
- for i in range(num_segments):
161
- end_time = min(start_time + segment_duration, duracion_video)
162
- if start_time >= duracion_video:
163
- break
164
-
165
- # Extraer el segmento de video y audio
166
- segment = video_resized.subclip(start_time, end_time)
167
- segment_audio = audio_original.subclip(start_time, end_time) # cortar el audio con el video
168
- segment = segment.set_audio(segment_audio) # asignando el audio cortado al video
169
-
170
- # Aplicar glitch al inicio del segmento (excepto el primero)
171
  if i > 0:
172
- glitch_segment = aplicar_glitch(segment.subclip(0, 0.5)) # Glitch de 0.5 segundos
173
- glitch_sound_clip = glitch_sound.set_start(start_time).volumex(0.5)
 
 
 
 
 
 
174
  glitch_clips.append(glitch_sound_clip)
175
- segment = concatenate_videoclips([glitch_segment, segment.subclip(0.5)], method="compose")
176
-
177
- segments.append(segment)
178
-
179
- # Avanzar al siguiente segmento, eliminando 2 segundos
180
- start_time += segment_duration - overlap
181
 
182
- # Combinar los segmentos procesados
183
- video_final = concatenate_videoclips(segments)
184
 
185
- # Combinar audio con efectos de glitch
186
- audio_final = CompositeAudioClip([audio_final] + glitch_clips).set_duration(video_final.duration)
187
-
188
- # Combinar video con audio
189
  video_con_audio = video_final.set_audio(audio_final)
190
 
191
- # Concatenar intro + video + outro SIN alteraciones
192
- video_final = concatenate_videoclips(
193
- [intro_resized, video_con_audio, outro_resized],
194
- method="compose", # Evitar problemas de grid
195
- padding=0 # Sin espacio entre clips
196
- )
197
 
198
- # Renderizar video final con metadatos correctos
199
  with tempfile.NamedTemporaryFile(suffix=".mp4", delete=False) as tmp:
200
  video_final.write_videofile(
201
  tmp.name,
202
  codec="libx264",
203
  audio_codec="aac",
204
- fps=video_original.fps, # Mantener FPS original
205
  threads=4,
 
206
  ffmpeg_params=[
207
- "-aspect", "16:9", # Forzar relaci贸n de aspecto
208
- "-vf", "scale=1920:1080" # Forzar escalado expl铆cito
 
 
209
  ],
210
  verbose=False
211
  )
212
- # eliminar_archivo_tiempo(tmp.name) # Comentar o eliminar esta l铆nea
 
213
  return tmp.name
 
214
  except Exception as e:
215
- logging.error(f" fallo general: {str(e)}")
216
  raise
 
 
 
 
 
 
 
217
 
218
  # Interfaz Gradio
219
  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
 
37
  async def procesar_audio(texto, voz, duracion_video, audio_original):
38
  temp_files = []
39
  try:
40
+ logging.info("Iniciando procesamiento de audio")
41
  if not texto.strip():
42
  raise ValueError("El texto para TTS no puede estar vac铆o.")
43
 
 
44
  def dividir_texto(texto, max_length=3000):
45
  return [texto[i:i + max_length] for i in range(0, len(texto), max_length)]
46
 
 
48
  audios_tts = []
49
 
50
  for fragmento in fragmentos:
 
51
  communicate = edge_tts.Communicate(fragmento, voz)
52
  with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as tmp_tts:
53
+ await communicate.save(tmp_tts.name)
 
 
 
 
 
54
  tts_audio = AudioFileClip(tmp_tts.name)
55
  temp_files.append(tmp_tts.name)
56
  audios_tts.append(tts_audio)
57
 
 
58
  tts_audio_final = concatenate_audioclips(audios_tts)
59
 
 
60
  if tts_audio_final.duration > duracion_video:
61
  tts_audio_final = tts_audio_final.subclip(0, duracion_video)
62
 
 
 
63
  needed_ms = int(duracion_video * 1000)
64
+ bg_music = AudioSegment.from_mp3(MUSIC_BG)
65
  repeticiones = needed_ms // len(bg_music) + 1
66
  bg_music = bg_music * repeticiones
67
  bg_music = bg_music[:needed_ms].fade_out(1000)
 
71
  bg_audio = AudioFileClip(tmp_bg.name).volumex(0.15)
72
  temp_files.append(tmp_bg.name)
73
 
 
74
  audios = [bg_audio.set_duration(duracion_video)]
75
  if audio_original:
76
+ audios.append(audio_original.volumex(0.7))
77
+ audios.append(tts_audio_final.volumex(0.85).set_start(0))
78
 
79
  audio_final = CompositeAudioClip(audios).set_duration(duracion_video)
80
+ logging.info("Audio procesado exitosamente")
81
  return audio_final
82
 
83
  except Exception as e:
84
+ logging.error(f"Fallo en procesamiento de audio: {str(e)}")
85
  raise
86
  finally:
87
  for file in temp_files:
 
91
  logging.warning(f"Error limpiando {file}: {e}")
92
 
93
  def aplicar_glitch(video_clip):
 
94
  def glitch_effect(frame):
95
  import numpy as np
96
+ frame = frame.copy()
97
  height, width, _ = frame.shape
98
+ offset = np.random.randint(5, 15)
99
+ if height > 0:
 
 
 
 
100
  frame[offset:, :] = np.roll(frame[:-offset, :], -offset, axis=0)
101
  return frame
102
 
 
104
 
105
  async def procesar_video(video_input, texto_tts, voz_seleccionada):
106
  try:
107
+ logging.info("Iniciando procesamiento de video")
108
+ intro = VideoFileClip(INTRO_VIDEO, target_resolution=(1080, 1920))
109
+ outro = VideoFileClip(OUTRO_VIDEO, target_resolution=(1080, 1920))
110
+ video_original = VideoFileClip(video_input, target_resolution=(1080, 1920))
111
  audio_original = video_original.audio
112
 
113
+ intro.reader.close()
114
+ outro.reader.close()
115
+ video_original.reader.close()
116
+
117
  duracion_video = video_original.duration
118
+ logging.info(f"Duraci贸n del video original: {duracion_video}s")
119
 
 
120
  audio_final = await procesar_audio(
121
  texto_tts,
122
  voz_seleccionada,
 
124
  audio_original
125
  )
126
 
127
+ segment_duration = 18
128
+ overlap = 2
129
+ total_segments = int((duracion_video) // (segment_duration)) + 1
130
+
 
 
 
 
 
 
 
 
 
 
 
 
 
131
  segments = []
132
  glitch_clips = []
133
+ glitch_sound = AudioFileClip(GLITCH_SOUND).volumex(0.5)
134
 
135
  start_time = 0
136
+ for i in range(total_segments):
137
+ end_time = start_time + segment_duration + overlap
138
+ end_time = min(end_time, duracion_video)
139
+
140
+ full_segment = video_original.subclip(start_time, end_time)
141
+
 
 
 
 
 
142
  if i > 0:
143
+ glitch_part = full_segment.subclip(0, 0.5)
144
+ glitch_part = aplicar_glitch(glitch_part)
145
+ processed_segment = concatenate_videoclips([
146
+ glitch_part,
147
+ full_segment.subclip(0.5)
148
+ ], method="compose")
149
+
150
+ glitch_sound_clip = glitch_sound.set_start(start_time)
151
  glitch_clips.append(glitch_sound_clip)
152
+ else:
153
+ processed_segment = full_segment
 
 
 
 
154
 
155
+ segments.append(processed_segment)
156
+ start_time += segment_duration
157
 
158
+ video_final = concatenate_videoclips(segments, method="compose")
 
 
 
159
  video_con_audio = video_final.set_audio(audio_final)
160
 
161
+ intro = VideoFileClip(INTRO_VIDEO, target_resolution=(1080, 1920))
162
+ outro = VideoFileClip(OUTRO_VIDEO, target_resolution=(1080, 1920))
163
+ video_final = concatenate_videoclips([intro, video_con_audio, outro], method="compose")
 
 
 
164
 
 
165
  with tempfile.NamedTemporaryFile(suffix=".mp4", delete=False) as tmp:
166
  video_final.write_videofile(
167
  tmp.name,
168
  codec="libx264",
169
  audio_codec="aac",
170
+ fps=video_original.fps,
171
  threads=4,
172
+ bitrate="5M",
173
  ffmpeg_params=[
174
+ "-preset", "ultrafast",
175
+ "-crf", "23",
176
+ "-movflags", "+faststart",
177
+ "-vf", "scale=1920:1080"
178
  ],
179
  verbose=False
180
  )
181
+ eliminar_archivo_tiempo(tmp.name, 1800)
182
+ logging.info(f"Video procesado guardado en: {tmp.name}")
183
  return tmp.name
184
+
185
  except Exception as e:
186
+ logging.error(f"Fallo general en procesamiento: {str(e)}")
187
  raise
188
+ finally:
189
+ try:
190
+ intro.close()
191
+ outro.close()
192
+ video_original.close()
193
+ except:
194
+ pass
195
 
196
  # Interfaz Gradio
197
  with gr.Blocks() as demo: