Update app.py
Browse files
app.py
CHANGED
@@ -6,6 +6,7 @@ from moviepy.editor import *
|
|
6 |
import edge_tts
|
7 |
import gradio as gr
|
8 |
from pydub import AudioSegment
|
|
|
9 |
|
10 |
# Configuraci贸n de Logs
|
11 |
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
|
@@ -23,6 +24,10 @@ 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:
|
@@ -102,42 +107,32 @@ def aplicar_glitch(video_clip):
|
|
102 |
|
103 |
return video_clip.fl_image(glitch_effect)
|
104 |
|
105 |
-
async def
|
106 |
try:
|
107 |
-
|
108 |
-
|
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,
|
123 |
-
|
124 |
audio_original
|
125 |
)
|
126 |
|
127 |
segment_duration = 18
|
128 |
overlap = 2
|
129 |
-
total_segments = int((
|
130 |
|
131 |
segments = []
|
132 |
glitch_clips = []
|
133 |
glitch_sound = AudioFileClip(GLITCH_SOUND).volumex(0.5)
|
134 |
|
135 |
-
|
136 |
for i in range(total_segments):
|
137 |
-
end_time =
|
138 |
-
end_time = min(end_time,
|
139 |
|
140 |
-
full_segment =
|
141 |
|
142 |
if i > 0:
|
143 |
glitch_part = full_segment.subclip(0, 0.5)
|
@@ -147,49 +142,83 @@ async def procesar_video(video_input, texto_tts, voz_seleccionada):
|
|
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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
157 |
|
158 |
-
|
159 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
160 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
161 |
intro = VideoFileClip(INTRO_VIDEO, target_resolution=(1080, 1920))
|
162 |
outro = VideoFileClip(OUTRO_VIDEO, target_resolution=(1080, 1920))
|
163 |
-
|
164 |
|
|
|
165 |
with tempfile.NamedTemporaryFile(suffix=".mp4", delete=False) as tmp:
|
166 |
-
|
167 |
tmp.name,
|
168 |
-
codec="
|
|
|
169 |
audio_codec="aac",
|
170 |
fps=video_original.fps,
|
171 |
-
threads=
|
172 |
-
bitrate="
|
173 |
ffmpeg_params=[
|
174 |
-
"-
|
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
|
183 |
return tmp.name
|
184 |
|
185 |
except Exception as e:
|
186 |
-
logging.error(f"Fallo general
|
187 |
raise
|
188 |
finally:
|
189 |
try:
|
|
|
190 |
intro.close()
|
191 |
outro.close()
|
192 |
-
video_original.close()
|
193 |
except:
|
194 |
pass
|
195 |
|
@@ -226,4 +255,8 @@ with gr.Blocks() as demo:
|
|
226 |
)
|
227 |
|
228 |
if __name__ == "__main__":
|
229 |
-
demo.queue(
|
|
|
|
|
|
|
|
|
|
6 |
import edge_tts
|
7 |
import gradio as gr
|
8 |
from pydub import AudioSegment
|
9 |
+
import psutil # Para monitoreo de recursos
|
10 |
|
11 |
# Configuraci贸n de Logs
|
12 |
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
|
|
|
24 |
logging.error(f"Falta archivo necesario: {file}")
|
25 |
raise FileNotFoundError(f"Falta: {file}")
|
26 |
|
27 |
+
# Configuraci贸n de chunks
|
28 |
+
CHUNK_SIZE = 300 # 5 minutos por chunk (ajusta seg煤n tus recursos)
|
29 |
+
MAX_CHUNKS = 20 # L铆mite m谩ximo de chunks (previene loops infinitos)
|
30 |
+
|
31 |
def eliminar_archivo_tiempo(ruta, delay=1800):
|
32 |
def eliminar():
|
33 |
try:
|
|
|
107 |
|
108 |
return video_clip.fl_image(glitch_effect)
|
109 |
|
110 |
+
async def procesar_fragmento(chunk, texto_tts, voz_seleccionada, start_time):
|
111 |
try:
|
112 |
+
audio_original = chunk.audio
|
113 |
+
duracion_chunk = chunk.duration
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
114 |
|
115 |
audio_final = await procesar_audio(
|
116 |
texto_tts,
|
117 |
voz_seleccionada,
|
118 |
+
duracion_chunk,
|
119 |
audio_original
|
120 |
)
|
121 |
|
122 |
segment_duration = 18
|
123 |
overlap = 2
|
124 |
+
total_segments = int((duracion_chunk) // (segment_duration)) + 1
|
125 |
|
126 |
segments = []
|
127 |
glitch_clips = []
|
128 |
glitch_sound = AudioFileClip(GLITCH_SOUND).volumex(0.5)
|
129 |
|
130 |
+
current_time = 0
|
131 |
for i in range(total_segments):
|
132 |
+
end_time = current_time + segment_duration + overlap
|
133 |
+
end_time = min(end_time, duracion_chunk)
|
134 |
|
135 |
+
full_segment = chunk.subclip(current_time, end_time)
|
136 |
|
137 |
if i > 0:
|
138 |
glitch_part = full_segment.subclip(0, 0.5)
|
|
|
142 |
full_segment.subclip(0.5)
|
143 |
], method="compose")
|
144 |
|
145 |
+
glitch_sound_clip = glitch_sound.set_start(start_time + current_time)
|
146 |
glitch_clips.append(glitch_sound_clip)
|
147 |
else:
|
148 |
processed_segment = full_segment
|
149 |
|
150 |
segments.append(processed_segment)
|
151 |
+
current_time += segment_duration
|
152 |
+
|
153 |
+
video_chunk = concatenate_videoclips(segments, method="compose")
|
154 |
+
video_chunk = video_chunk.set_audio(audio_final)
|
155 |
+
return video_chunk
|
156 |
+
|
157 |
+
except Exception as e:
|
158 |
+
logging.error(f"Fallo procesando fragmento: {str(e)}")
|
159 |
+
raise
|
160 |
|
161 |
+
async def procesar_video(video_input, texto_tts, voz_seleccionada):
|
162 |
+
try:
|
163 |
+
logging.info("Iniciando procesamiento de video")
|
164 |
+
video_original = VideoFileClip(video_input, target_resolution=(1080, 1920))
|
165 |
+
total_duration = video_original.duration
|
166 |
+
|
167 |
+
# Monitoreo de recursos
|
168 |
+
logging.info(f"Memoria inicial: {psutil.virtual_memory().percent}%")
|
169 |
+
logging.info(f"CPU inicial: {psutil.cpu_percent()}%")
|
170 |
|
171 |
+
# Dividir en chunks
|
172 |
+
chunks = []
|
173 |
+
for start in range(0, int(total_duration), CHUNK_SIZE):
|
174 |
+
end = min(start + CHUNK_SIZE, total_duration)
|
175 |
+
chunk = video_original.subclip(start, end)
|
176 |
+
chunks.append((start, chunk))
|
177 |
+
|
178 |
+
# Procesar cada chunk
|
179 |
+
processed_clips = []
|
180 |
+
for i, (start_time, chunk) in enumerate(chunks):
|
181 |
+
logging.info(f"Procesando chunk {i+1}/{len(chunks)}")
|
182 |
+
processed_chunk = await procesar_fragmento(chunk, texto_tts, voz_seleccionada, start_time)
|
183 |
+
processed_clips.append(processed_chunk)
|
184 |
+
|
185 |
+
# Combinar todos los chunks
|
186 |
+
final_video = concatenate_videoclips(processed_clips, method="compose")
|
187 |
+
|
188 |
+
# Agregar intro y outro
|
189 |
intro = VideoFileClip(INTRO_VIDEO, target_resolution=(1080, 1920))
|
190 |
outro = VideoFileClip(OUTRO_VIDEO, target_resolution=(1080, 1920))
|
191 |
+
final_video = concatenate_videoclips([intro, final_video, outro], method="compose")
|
192 |
|
193 |
+
# Renderizado final con optimizaci贸n
|
194 |
with tempfile.NamedTemporaryFile(suffix=".mp4", delete=False) as tmp:
|
195 |
+
final_video.write_videofile(
|
196 |
tmp.name,
|
197 |
+
codec="h264_nvenc", # Usa GPU si est谩 disponible
|
198 |
+
preset="p7",
|
199 |
audio_codec="aac",
|
200 |
fps=video_original.fps,
|
201 |
+
threads=8,
|
202 |
+
bitrate="8M",
|
203 |
ffmpeg_params=[
|
204 |
+
"-crf", "28",
|
|
|
205 |
"-movflags", "+faststart",
|
206 |
"-vf", "scale=1920:1080"
|
207 |
],
|
208 |
verbose=False
|
209 |
)
|
210 |
eliminar_archivo_tiempo(tmp.name, 1800)
|
211 |
+
logging.info(f"Video final guardado: {tmp.name}")
|
212 |
return tmp.name
|
213 |
|
214 |
except Exception as e:
|
215 |
+
logging.error(f"Fallo general: {str(e)}")
|
216 |
raise
|
217 |
finally:
|
218 |
try:
|
219 |
+
video_original.close()
|
220 |
intro.close()
|
221 |
outro.close()
|
|
|
222 |
except:
|
223 |
pass
|
224 |
|
|
|
255 |
)
|
256 |
|
257 |
if __name__ == "__main__":
|
258 |
+
demo.queue(
|
259 |
+
concurrency_count=1,
|
260 |
+
max_size=20,
|
261 |
+
server_timeout=3600 # Timeout de 1 hora
|
262 |
+
).launch()
|