gnosticdev commited on
Commit
ff29966
verified
1 Parent(s): 9555542

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +69 -84
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,9 @@ 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 +47,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,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,16 +89,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 +102,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 +122,83 @@ 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)
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
  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
65
  bg_music = bg_music * repeticiones
66
  bg_music = bg_music[:needed_ms].fade_out(1000)
 
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
  def glitch_effect(frame):
93
  import numpy as np
94
+ frame = frame.copy() # Correcci贸n clave para evitar errores
95
  height, width, _ = frame.shape
96
+ offset = np.random.randint(5, 15)
97
+ if height > 0:
 
 
 
 
98
  frame[offset:, :] = np.roll(frame[:-offset, :], -offset, axis=0)
99
  return frame
100
 
 
102
 
103
  async def procesar_video(video_input, texto_tts, voz_seleccionada):
104
  try:
105
+ # Carga optimizada
106
+ intro = VideoFileClip(INTRO_VIDEO, target_resolution=(1080, 1920))
107
+ outro = VideoFileClip(OUTRO_VIDEO, target_resolution=(1080, 1920))
108
+ video_original = VideoFileClip(video_input, target_resolution=(1080, 1920))
109
  audio_original = video_original.audio
110
 
111
+ # Liberar recursos
112
+ intro.reader.close()
113
+ outro.reader.close()
114
+ video_original.reader.close()
115
+
116
  duracion_video = video_original.duration
117
 
 
118
  audio_final = await procesar_audio(
119
  texto_tts,
120
  voz_seleccionada,
 
122
  audio_original
123
  )
124
 
125
+ # Configuraci贸n de cortes din谩micos
126
+ segment_duration = 18 # Duraci贸n visible del segmento
127
+ overlap = 2 # Segundos eliminados en cada corte
128
+ total_segments = int((duracion_video) // (segment_duration)) + 1
129
+
 
 
 
 
 
 
 
 
 
 
 
 
130
  segments = []
131
  glitch_clips = []
132
+ glitch_sound = AudioFileClip(GLITCH_SOUND).volumex(0.5)
133
 
134
  start_time = 0
135
+ for i in range(total_segments):
136
+ end_time = start_time + segment_duration + overlap
137
+ end_time = min(end_time, duracion_video)
138
+
139
+ # Extraer segmento completo
140
+ full_segment = video_original.subclip(start_time, end_time)
141
+
142
+ # Aplicar transici贸n/glitch
 
 
 
143
  if i > 0:
144
+ # Tomar 0.5 segundos para glitch
145
+ glitch_part = full_segment.subclip(0, 0.5)
146
+ glitch_part = aplicar_glitch(glitch_part)
147
+
148
+ # Combinar con el resto del segmento
149
+ processed_segment = concatenate_videoclips([
150
+ glitch_part,
151
+ full_segment.subclip(0.5)
152
+ ], method="compose")
153
+
154
+ # Agregar sonido de glitch
155
+ glitch_sound_clip = glitch_sound.set_start(start_time)
156
  glitch_clips.append(glitch_sound_clip)
157
+ else:
158
+ processed_segment = full_segment
 
 
 
 
159
 
160
+ segments.append(processed_segment)
161
+ start_time += segment_duration # Avanzar sin los 2 segundos de overlap
162
 
163
+ # Combinar todos los segmentos
164
+ video_final = concatenate_videoclips(segments, method="compose")
 
 
165
  video_con_audio = video_final.set_audio(audio_final)
166
 
167
+ # Agregar intro y outro
168
+ intro = VideoFileClip(INTRO_VIDEO, target_resolution=(1080, 1920))
169
+ outro = VideoFileClip(OUTRO_VIDEO, target_resolution=(1080, 1920))
170
+ video_final = concatenate_videoclips([intro, video_con_audio, outro], method="compose")
 
 
171
 
172
+ # Renderizado final
173
  with tempfile.NamedTemporaryFile(suffix=".mp4", delete=False) as tmp:
174
  video_final.write_videofile(
175
  tmp.name,
176
  codec="libx264",
177
  audio_codec="aac",
178
+ fps=video_original.fps,
179
  threads=4,
180
+ bitrate="5M",
181
  ffmpeg_params=[
182
+ "-preset", "ultrafast",
183
+ "-crf", "23",
184
+ "-movflags", "+faststart",
185
+ "-vf", "scale=1920:1080"
186
  ],
187
  verbose=False
188
  )
189
+ eliminar_archivo_tiempo(tmp.name, 1800)
190
  return tmp.name
191
+
192
  except Exception as e:
193
  logging.error(f" fallo general: {str(e)}")
194
  raise
195
+ finally:
196
+ try:
197
+ intro.close()
198
+ outro.close()
199
+ video_original.close()
200
+ except:
201
+ pass
202
 
203
  # Interfaz Gradio
204
  with gr.Blocks() as demo: