Update app.py
Browse files
app.py
CHANGED
@@ -31,45 +31,50 @@ async def text_to_speech(text, voice, rate, pitch):
|
|
31 |
except Exception as e:
|
32 |
return None, f"Speech generation failed: {str(e)}"
|
33 |
|
34 |
-
# Agregar m煤sica de fondo
|
35 |
def add_background_music(speech_path, bg_music_path):
|
36 |
speech = AudioSegment.from_file(speech_path)
|
37 |
background = AudioSegment.from_file(bg_music_path) - 16 # 15% volume
|
38 |
|
39 |
-
# Asegurar que la m煤sica de fondo dure al menos como el speech + 3s fadeout
|
40 |
if len(background) < len(speech) + 3000:
|
41 |
background = background * math.ceil((len(speech)+3000)/len(background))
|
42 |
|
43 |
-
# Combinar audio con fadeout
|
44 |
combined = speech.overlay(background[:len(speech)])
|
45 |
fade_out = background[len(speech):len(speech)+3000].fade_out(3000)
|
46 |
final_audio = combined + fade_out
|
47 |
|
48 |
with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as tmp_file:
|
49 |
final_audio.export(tmp_file.name, format="mp3")
|
|
|
|
|
|
|
50 |
return tmp_file.name
|
51 |
|
52 |
-
# Procesar m煤ltiples videos
|
53 |
def process_videos(audio_path, video_files):
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
with tempfile.NamedTemporaryFile(delete=False, suffix=".mp4") as concat_video:
|
58 |
-
# Crear lista de videos para concatenar
|
59 |
-
with tempfile.NamedTemporaryFile(mode="w", delete=False, suffix=".txt") as list_file:
|
60 |
-
list_file.write("\n".join([f"file '{v.name}'" for v in video_files]))
|
61 |
-
list_file.close()
|
62 |
-
|
63 |
-
subprocess.run([
|
64 |
-
"ffmpeg", "-y",
|
65 |
-
"-f", "concat",
|
66 |
-
"-safe", "0",
|
67 |
-
"-i", list_file.name,
|
68 |
-
"-c", "copy",
|
69 |
-
concat_video.name
|
70 |
-
], check=True)
|
71 |
|
72 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
73 |
with tempfile.NamedTemporaryFile(delete=False, suffix=".mp4") as final_video:
|
74 |
subprocess.run([
|
75 |
"ffmpeg", "-y",
|
@@ -86,34 +91,49 @@ def process_videos(audio_path, video_files):
|
|
86 |
], check=True)
|
87 |
|
88 |
return final_video.name
|
|
|
|
|
|
|
|
|
|
|
|
|
89 |
|
90 |
-
# Funci贸n principal
|
91 |
async def tts_interface(text, voice, rate, pitch, bg_music, video_files):
|
92 |
-
|
93 |
-
audio_path, warning = await text_to_speech(text, voice, rate, pitch)
|
94 |
-
if warning:
|
95 |
-
return None, None, gr.Warning(warning)
|
96 |
-
|
97 |
try:
|
|
|
|
|
|
|
|
|
|
|
98 |
# Agregar m煤sica de fondo
|
99 |
if bg_music:
|
100 |
-
|
101 |
|
102 |
# Procesar videos
|
|
|
103 |
if video_files:
|
104 |
-
video_path = process_videos(
|
105 |
-
|
106 |
-
|
|
|
|
|
107 |
|
108 |
-
return
|
109 |
|
110 |
except Exception as e:
|
111 |
return None, None, gr.Warning(f"Processing error: {str(e)}")
|
112 |
finally:
|
113 |
-
|
114 |
-
|
|
|
|
|
|
|
|
|
|
|
115 |
|
116 |
-
# Crear interfaz
|
117 |
async def create_demo():
|
118 |
voices = await get_voices()
|
119 |
|
|
|
31 |
except Exception as e:
|
32 |
return None, f"Speech generation failed: {str(e)}"
|
33 |
|
34 |
+
# Agregar m煤sica de fondo (ahora elimina el audio original)
|
35 |
def add_background_music(speech_path, bg_music_path):
|
36 |
speech = AudioSegment.from_file(speech_path)
|
37 |
background = AudioSegment.from_file(bg_music_path) - 16 # 15% volume
|
38 |
|
|
|
39 |
if len(background) < len(speech) + 3000:
|
40 |
background = background * math.ceil((len(speech)+3000)/len(background))
|
41 |
|
|
|
42 |
combined = speech.overlay(background[:len(speech)])
|
43 |
fade_out = background[len(speech):len(speech)+3000].fade_out(3000)
|
44 |
final_audio = combined + fade_out
|
45 |
|
46 |
with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as tmp_file:
|
47 |
final_audio.export(tmp_file.name, format="mp3")
|
48 |
+
# Eliminar audio original
|
49 |
+
if os.path.exists(speech_path):
|
50 |
+
os.remove(speech_path)
|
51 |
return tmp_file.name
|
52 |
|
53 |
+
# Procesar m煤ltiples videos (ahora elimina archivos temporales)
|
54 |
def process_videos(audio_path, video_files):
|
55 |
+
temp_files = []
|
56 |
+
try:
|
57 |
+
audio_duration = AudioSegment.from_file(audio_path).duration_seconds
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
58 |
|
59 |
+
# Concatenar videos
|
60 |
+
with tempfile.NamedTemporaryFile(delete=False, suffix=".mp4") as concat_video:
|
61 |
+
temp_files.append(concat_video.name)
|
62 |
+
|
63 |
+
with tempfile.NamedTemporaryFile(mode="w", delete=False, suffix=".txt") as list_file:
|
64 |
+
temp_files.append(list_file.name)
|
65 |
+
list_file.write("\n".join([f"file '{v.name}'" for v in video_files]))
|
66 |
+
list_file.close()
|
67 |
+
|
68 |
+
subprocess.run([
|
69 |
+
"ffmpeg", "-y",
|
70 |
+
"-f", "concat",
|
71 |
+
"-safe", "0",
|
72 |
+
"-i", list_file.name,
|
73 |
+
"-c", "copy",
|
74 |
+
concat_video.name
|
75 |
+
], check=True)
|
76 |
+
|
77 |
+
# Crear video final
|
78 |
with tempfile.NamedTemporaryFile(delete=False, suffix=".mp4") as final_video:
|
79 |
subprocess.run([
|
80 |
"ffmpeg", "-y",
|
|
|
91 |
], check=True)
|
92 |
|
93 |
return final_video.name
|
94 |
+
|
95 |
+
finally:
|
96 |
+
# Eliminar archivos temporales
|
97 |
+
for f in temp_files:
|
98 |
+
if os.path.exists(f):
|
99 |
+
os.remove(f)
|
100 |
|
101 |
+
# Funci贸n principal (ahora elimina videos originales)
|
102 |
async def tts_interface(text, voice, rate, pitch, bg_music, video_files):
|
103 |
+
temp_audio = None
|
|
|
|
|
|
|
|
|
104 |
try:
|
105 |
+
# Generar audio principal
|
106 |
+
temp_audio, warning = await text_to_speech(text, voice, rate, pitch)
|
107 |
+
if warning:
|
108 |
+
return None, None, gr.Warning(warning)
|
109 |
+
|
110 |
# Agregar m煤sica de fondo
|
111 |
if bg_music:
|
112 |
+
temp_audio = add_background_music(temp_audio, bg_music)
|
113 |
|
114 |
# Procesar videos
|
115 |
+
video_path = None
|
116 |
if video_files:
|
117 |
+
video_path = process_videos(temp_audio, video_files)
|
118 |
+
# Eliminar videos originales subidos
|
119 |
+
for video in video_files:
|
120 |
+
if hasattr(video, 'name') and os.path.exists(video.name):
|
121 |
+
os.remove(video.name)
|
122 |
|
123 |
+
return temp_audio, video_path, None
|
124 |
|
125 |
except Exception as e:
|
126 |
return None, None, gr.Warning(f"Processing error: {str(e)}")
|
127 |
finally:
|
128 |
+
# Eliminar audio temporal si existe y no es la salida final
|
129 |
+
if temp_audio and os.path.exists(temp_audio):
|
130 |
+
try:
|
131 |
+
if video_path and temp_audio != video_path:
|
132 |
+
os.remove(temp_audio)
|
133 |
+
except: # Evitar errores si el archivo ya fue eliminado
|
134 |
+
pass
|
135 |
|
136 |
+
# Crear interfaz (sin cambios)
|
137 |
async def create_demo():
|
138 |
voices = await get_voices()
|
139 |
|