Spaces:
Sleeping
Sleeping
Update app.py
Browse filesadd endpoint para unir narração com fundo musical
app.py
CHANGED
|
@@ -247,7 +247,80 @@ async def get_duration_from_url(payload: Dict):
|
|
| 247 |
# Este bloco é executado sempre, mesmo se ocorrerem erros
|
| 248 |
if os.path.exists(temp_audio_path):
|
| 249 |
os.remove(temp_audio_path)
|
| 250 |
-
print(f"--- CLEANUP: Removed temporary probe file {temp_audio_path} ---")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 251 |
|
| 252 |
# ==============================================================================
|
| 253 |
# ENDPOINT "TUDO-EM-UM" OTIMIZADO (VERSÃO FINAL)
|
|
|
|
| 247 |
# Este bloco é executado sempre, mesmo se ocorrerem erros
|
| 248 |
if os.path.exists(temp_audio_path):
|
| 249 |
os.remove(temp_audio_path)
|
| 250 |
+
print(f"--- CLEANUP: Removed temporary probe file {temp_audio_path} ---")
|
| 251 |
+
|
| 252 |
+
# ==============================================================================
|
| 253 |
+
# ENDPOINT PARA MIXAR NARRAÇÃO E MÚSICA DE FUNDO
|
| 254 |
+
# ==============================================================================
|
| 255 |
+
|
| 256 |
+
@app.post("/mix-audio-tracks/")
|
| 257 |
+
async def mix_audio_tracks(
|
| 258 |
+
narration_file: UploadFile = File(...),
|
| 259 |
+
music_file: UploadFile = File(...),
|
| 260 |
+
music_volume: float = Form(0.3)
|
| 261 |
+
):
|
| 262 |
+
"""
|
| 263 |
+
Mixes a narration track with a background music track.
|
| 264 |
+
|
| 265 |
+
It lowers the volume of the music track and mixes it with the narration,
|
| 266 |
+
ensuring the final track's duration matches the narration.
|
| 267 |
+
|
| 268 |
+
Args:
|
| 269 |
+
narration_file (UploadFile): The main narration audio file.
|
| 270 |
+
music_file (UploadFile): The background music audio file.
|
| 271 |
+
music_volume (float): The desired volume for the background music (0.0 to 1.0).
|
| 272 |
+
|
| 273 |
+
Returns:
|
| 274 |
+
FileResponse: The final mixed audio file.
|
| 275 |
+
"""
|
| 276 |
+
# Validação de entrada
|
| 277 |
+
if not (0.0 <= music_volume <= 1.0):
|
| 278 |
+
raise HTTPException(status_code=400, detail="music_volume must be between 0.0 and 1.0")
|
| 279 |
+
|
| 280 |
+
# Setup do ambiente temporário
|
| 281 |
+
unique_id = str(uuid.uuid4())
|
| 282 |
+
temp_processing_dir = os.path.join(TEMP_DIR, unique_id)
|
| 283 |
+
os.makedirs(temp_processing_dir)
|
| 284 |
+
|
| 285 |
+
narration_path = os.path.join(temp_processing_dir, "narration.mp3")
|
| 286 |
+
music_path = os.path.join(temp_processing_dir, "music.mp3")
|
| 287 |
+
mixed_audio_path = os.path.join(temp_processing_dir, "mixed_audio.mp3")
|
| 288 |
+
|
| 289 |
+
try:
|
| 290 |
+
# Salva os arquivos recebidos
|
| 291 |
+
with open(narration_path, "wb") as buffer:
|
| 292 |
+
shutil.copyfileobj(narration_file.file, buffer)
|
| 293 |
+
with open(music_path, "wb") as buffer:
|
| 294 |
+
shutil.copyfileobj(music_file.file, buffer)
|
| 295 |
+
|
| 296 |
+
# Constrói o comando FFmpeg com o filtro 'amix'
|
| 297 |
+
# [0:a] -> primeiro input (narração)
|
| 298 |
+
# [1:a] -> segundo input (música)
|
| 299 |
+
# volume=... -> aplica o volume apenas no segundo input
|
| 300 |
+
# duration=first -> a mixagem termina quando o primeiro input (narração) acabar
|
| 301 |
+
command = (
|
| 302 |
+
f"ffmpeg -i {shlex.quote(narration_path)} -i {shlex.quote(music_path)} "
|
| 303 |
+
f"-filter_complex \"[1:a]volume={music_volume}[bg];[0:a][bg]amix=inputs=2:duration=first\" "
|
| 304 |
+
f"-y {shlex.quote(mixed_audio_path)}"
|
| 305 |
+
)
|
| 306 |
+
|
| 307 |
+
# Executa o comando
|
| 308 |
+
await run_subprocess(command)
|
| 309 |
+
print("SUCCESS: Narration and music mixed successfully.")
|
| 310 |
+
|
| 311 |
+
# Retorna o arquivo final e agenda a limpeza
|
| 312 |
+
cleanup_task = BackgroundTask(cleanup_directory, directory_path=temp_processing_dir)
|
| 313 |
+
return FileResponse(
|
| 314 |
+
path=mixed_audio_path,
|
| 315 |
+
filename="mixed_audio.mp3",
|
| 316 |
+
media_type="audio/mpeg",
|
| 317 |
+
background=cleanup_task
|
| 318 |
+
)
|
| 319 |
+
|
| 320 |
+
except Exception as e:
|
| 321 |
+
cleanup_directory(directory_path=temp_processing_dir)
|
| 322 |
+
raise HTTPException(status_code=500, detail=f"An internal server error occurred: {str(e)}")
|
| 323 |
+
|
| 324 |
|
| 325 |
# ==============================================================================
|
| 326 |
# ENDPOINT "TUDO-EM-UM" OTIMIZADO (VERSÃO FINAL)
|