Update app.py
Browse files
app.py
CHANGED
@@ -18,6 +18,9 @@ OUTRO_VIDEO = "outrovideo.mp4"
|
|
18 |
MUSIC_BG = "musicafondo.mp3"
|
19 |
EJEMPLO_VIDEO = "ejemplo.mp4"
|
20 |
|
|
|
|
|
|
|
21 |
# Configuraci贸n de chunks
|
22 |
SEGMENT_DURATION = 30 # Duraci贸n exacta entre transiciones (sin overlap)
|
23 |
TRANSITION_DURATION = 1.5 # Duraci贸n del efecto slide
|
@@ -68,13 +71,13 @@ def convertir_video(video_path):
|
|
68 |
with tempfile.NamedTemporaryFile(delete=False, suffix=".mp4") as tmp_converted:
|
69 |
output_path = tmp_converted.name
|
70 |
|
71 |
-
# Convertir a un formato m谩s eficiente
|
72 |
-
os.system(f'ffmpeg -i "{video_path}" -
|
73 |
|
74 |
# Comprobar si ahora cumple las limitaciones de tama帽o
|
75 |
if not validar_video(output_path):
|
76 |
-
# Si sigue sin cumplir, aumentar la compresi贸n
|
77 |
-
os.system(f'ffmpeg -i "{output_path}" -
|
78 |
os.remove(output_path)
|
79 |
os.rename(f"{output_path}.tmp", output_path)
|
80 |
|
@@ -115,14 +118,17 @@ def crear_musica_fondo(duracion_total):
|
|
115 |
return AudioFileClip(tmp_bg.name).volumex(0.15), tmp_bg.name
|
116 |
|
117 |
def create_slide_transition(clip1, clip2, duration=TRANSITION_DURATION):
|
|
|
|
|
|
|
118 |
part1 = clip1.subclip(clip1.duration - duration)
|
119 |
part2 = clip2.subclip(0, duration)
|
120 |
transition = CompositeVideoClip([
|
121 |
part1.fx(vfx.fadeout, duration),
|
122 |
part2.fx(vfx.fadein, duration).set_position(
|
123 |
-
lambda t: ('center',
|
124 |
)
|
125 |
-
], size=
|
126 |
return transition
|
127 |
|
128 |
def liberar_memoria(objetos_cerrar=None):
|
@@ -155,13 +161,16 @@ async def procesar_video(video_input, texto_tts, voz_seleccionada, progress=gr.P
|
|
155 |
temp_files.append(video_input)
|
156 |
|
157 |
progress(0.1, desc="Preparando video")
|
158 |
-
#
|
159 |
video_original = VideoFileClip(video_input)
|
160 |
duracion_video = video_original.duration
|
|
|
|
|
161 |
video_original.close() # Cerrar para liberar memoria
|
162 |
|
163 |
# Informaci贸n importante sobre el video original
|
164 |
logging.info(f"Duraci贸n total del video: {duracion_video} segundos")
|
|
|
165 |
|
166 |
if duracion_video <= 0:
|
167 |
raise ValueError("El video debe tener una duraci贸n mayor que cero.")
|
@@ -189,7 +198,6 @@ async def procesar_video(video_input, texto_tts, voz_seleccionada, progress=gr.P
|
|
189 |
chunk_video = VideoFileClip(video_input).subclip(chunk_start, chunk_end)
|
190 |
|
191 |
# Extraer la porci贸n de audio correspondiente a este bloque
|
192 |
-
# FIX: Correcci贸n para evitar acceder a tiempo m谩s all谩 de la duraci贸n del audio TTS
|
193 |
tts_chunk_end = min(chunk_end, tts_audio.duration)
|
194 |
chunk_tts = None
|
195 |
if chunk_start < tts_audio.duration:
|
@@ -258,9 +266,9 @@ async def procesar_video(video_input, texto_tts, voz_seleccionada, progress=gr.P
|
|
258 |
liberar_memoria([tts_audio, bg_audio])
|
259 |
tts_audio = bg_audio = None
|
260 |
|
261 |
-
# A帽adir intro y outro
|
262 |
progress(0.85, desc="Preparando intro y outro")
|
263 |
-
intro = VideoFileClip(INTRO_VIDEO
|
264 |
with tempfile.NamedTemporaryFile(delete=False, suffix="_intro.mp4") as tmp_intro:
|
265 |
intro.write_videofile(
|
266 |
tmp_intro.name,
|
@@ -274,7 +282,7 @@ async def procesar_video(video_input, texto_tts, voz_seleccionada, progress=gr.P
|
|
274 |
segmentos_temp.insert(0, tmp_intro.name) # Intro al principio
|
275 |
intro.close()
|
276 |
|
277 |
-
outro = VideoFileClip(OUTRO_VIDEO
|
278 |
with tempfile.NamedTemporaryFile(delete=False, suffix="_outro.mp4") as tmp_outro:
|
279 |
outro.write_videofile(
|
280 |
tmp_outro.name,
|
@@ -415,7 +423,7 @@ with gr.Blocks() as demo:
|
|
415 |
- **Optimizaciones para Hugging Face Spaces:**
|
416 |
- Procesamiento por bloques para videos largos
|
417 |
- M谩ximo tama帽o de archivo: 200MB
|
418 |
-
-
|
419 |
- Texto TTS limitado a 1000 caracteres
|
420 |
- Las transiciones ocurren cada 30 segundos
|
421 |
- El video contiene intro y outro predefinidos
|
|
|
18 |
MUSIC_BG = "musicafondo.mp3"
|
19 |
EJEMPLO_VIDEO = "ejemplo.mp4"
|
20 |
|
21 |
+
# CONSTANTES DE LIMITACIONES
|
22 |
+
MAX_VIDEO_SIZE = 200 * 1024 * 1024 # Tama帽o m谩ximo en bytes (200MB)
|
23 |
+
|
24 |
# Configuraci贸n de chunks
|
25 |
SEGMENT_DURATION = 30 # Duraci贸n exacta entre transiciones (sin overlap)
|
26 |
TRANSITION_DURATION = 1.5 # Duraci贸n del efecto slide
|
|
|
71 |
with tempfile.NamedTemporaryFile(delete=False, suffix=".mp4") as tmp_converted:
|
72 |
output_path = tmp_converted.name
|
73 |
|
74 |
+
# Convertir a un formato m谩s eficiente pero manteniendo la resoluci贸n original
|
75 |
+
os.system(f'ffmpeg -i "{video_path}" -c:v libx264 -crf 28 -preset ultrafast -c:a aac -b:a 96k "{output_path}" -y')
|
76 |
|
77 |
# Comprobar si ahora cumple las limitaciones de tama帽o
|
78 |
if not validar_video(output_path):
|
79 |
+
# Si sigue sin cumplir, aumentar la compresi贸n pero sin cambiar la resoluci贸n
|
80 |
+
os.system(f'ffmpeg -i "{output_path}" -c:v libx264 -crf 32 -preset ultrafast -c:a aac -b:a 64k "{output_path}.tmp" -y')
|
81 |
os.remove(output_path)
|
82 |
os.rename(f"{output_path}.tmp", output_path)
|
83 |
|
|
|
118 |
return AudioFileClip(tmp_bg.name).volumex(0.15), tmp_bg.name
|
119 |
|
120 |
def create_slide_transition(clip1, clip2, duration=TRANSITION_DURATION):
|
121 |
+
# Obtener dimensiones de los clips
|
122 |
+
width, height = clip1.size
|
123 |
+
|
124 |
part1 = clip1.subclip(clip1.duration - duration)
|
125 |
part2 = clip2.subclip(0, duration)
|
126 |
transition = CompositeVideoClip([
|
127 |
part1.fx(vfx.fadeout, duration),
|
128 |
part2.fx(vfx.fadein, duration).set_position(
|
129 |
+
lambda t: ('center', height - (height * (t/duration)))
|
130 |
)
|
131 |
+
], size=(width, height)).set_duration(duration)
|
132 |
return transition
|
133 |
|
134 |
def liberar_memoria(objetos_cerrar=None):
|
|
|
161 |
temp_files.append(video_input)
|
162 |
|
163 |
progress(0.1, desc="Preparando video")
|
164 |
+
# Cargamos el video original pero respetamos su resoluci贸n
|
165 |
video_original = VideoFileClip(video_input)
|
166 |
duracion_video = video_original.duration
|
167 |
+
# Guardamos las dimensiones originales
|
168 |
+
original_size = video_original.size
|
169 |
video_original.close() # Cerrar para liberar memoria
|
170 |
|
171 |
# Informaci贸n importante sobre el video original
|
172 |
logging.info(f"Duraci贸n total del video: {duracion_video} segundos")
|
173 |
+
logging.info(f"Resoluci贸n original: {original_size[0]}x{original_size[1]}")
|
174 |
|
175 |
if duracion_video <= 0:
|
176 |
raise ValueError("El video debe tener una duraci贸n mayor que cero.")
|
|
|
198 |
chunk_video = VideoFileClip(video_input).subclip(chunk_start, chunk_end)
|
199 |
|
200 |
# Extraer la porci贸n de audio correspondiente a este bloque
|
|
|
201 |
tts_chunk_end = min(chunk_end, tts_audio.duration)
|
202 |
chunk_tts = None
|
203 |
if chunk_start < tts_audio.duration:
|
|
|
266 |
liberar_memoria([tts_audio, bg_audio])
|
267 |
tts_audio = bg_audio = None
|
268 |
|
269 |
+
# A帽adir intro y outro - conservar resoluci贸n original para consistencia
|
270 |
progress(0.85, desc="Preparando intro y outro")
|
271 |
+
intro = VideoFileClip(INTRO_VIDEO)
|
272 |
with tempfile.NamedTemporaryFile(delete=False, suffix="_intro.mp4") as tmp_intro:
|
273 |
intro.write_videofile(
|
274 |
tmp_intro.name,
|
|
|
282 |
segmentos_temp.insert(0, tmp_intro.name) # Intro al principio
|
283 |
intro.close()
|
284 |
|
285 |
+
outro = VideoFileClip(OUTRO_VIDEO)
|
286 |
with tempfile.NamedTemporaryFile(delete=False, suffix="_outro.mp4") as tmp_outro:
|
287 |
outro.write_videofile(
|
288 |
tmp_outro.name,
|
|
|
423 |
- **Optimizaciones para Hugging Face Spaces:**
|
424 |
- Procesamiento por bloques para videos largos
|
425 |
- M谩ximo tama帽o de archivo: 200MB
|
426 |
+
- Mantiene la resoluci贸n original del video
|
427 |
- Texto TTS limitado a 1000 caracteres
|
428 |
- Las transiciones ocurren cada 30 segundos
|
429 |
- El video contiene intro y outro predefinidos
|