from fastapi import FastAPI, HTTPException, UploadFile, File from fastapi.responses import FileResponse from fastapi.middleware.cors import CORSMiddleware from fastapi.background import BackgroundTasks import os, subprocess, traceback, urllib.parse app = FastAPI() TMP_DIR = "/tmp/motor_hf" os.makedirs(TMP_DIR, exist_ok=True) app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # ── Healthcheck leve (usado pelo MyMp3 para o sinal luminoso de status) ───── @app.get("/") def home(): return {"status": "online", "servico": "AV Conversion Core"} # ── MP3 via upload (usado pela OrangePi e pelo LocalServer/Musicas) ───────── @app.post("/processar_conversao") async def processar_conversao(audio_bruto: UploadFile = File(...)): job_id = os.urandom(4).hex() caminho_bruto = f"{TMP_DIR}/bruto_{job_id}" caminho_mp3 = f"{TMP_DIR}/saida_{job_id}.mp3" try: conteudo = await audio_bruto.read() with open(caminho_bruto, "wb") as f: f.write(conteudo) cmd = ["ffmpeg", "-y", "-i", caminho_bruto, "-q:a", "0", "-map", "a", caminho_mp3] res = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, timeout=180) if res.returncode != 0 or not os.path.exists(caminho_mp3): raise Exception(f"Erro FFmpeg: {res.stderr}") bg = BackgroundTasks() bg.add_task(os.remove, caminho_bruto) bg.add_task(os.remove, caminho_mp3) return FileResponse(caminho_mp3, media_type="audio/mpeg", background=bg) except Exception as e: traceback.print_exc() if os.path.exists(caminho_bruto): try: os.remove(caminho_bruto) except Exception: pass raise HTTPException(status_code=500, detail=str(e)) # ── MP3 via URL direta (busca na CDN, sem upload) ─────────────────────────── @app.get("/converter_stream_direta") def converter_stream_direta(url_stream_audio: str, titulo_musica: str): job_id = os.urandom(4).hex() titulo_limpo = titulo_musica.replace("/", "").replace("\\", "").replace(" ", "_") caminho_mp3 = f"{TMP_DIR}/{titulo_limpo}_{job_id}.mp3" try: url_real = urllib.parse.unquote(url_stream_audio) cmd = ["ffmpeg", "-y", "-i", url_real, "-q:a", "0", caminho_mp3] res = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, timeout=120) if res.returncode != 0: raise Exception(f"Erro FFmpeg no co-processamento: {res.stderr}") if os.path.exists(caminho_mp3): bg = BackgroundTasks() bg.add_task(os.remove, caminho_mp3) headers = {"Content-Disposition": f'attachment; filename="{urllib.parse.quote(titulo_limpo)}.mp3"'} return FileResponse(caminho_mp3, media_type="audio/mpeg", headers=headers, background=bg) raise Exception("Falha ao gerar arquivo MP3") except Exception as e: traceback.print_exc() raise HTTPException(status_code=500, detail=str(e)) # ── MP4 via URL direta (usado pela OrangePi e LocalServer para Clipes) ────── @app.get("/converter_video_direto") def converter_video_direto(url_stream_video: str, titulo_video: str): job_id = os.urandom(4).hex() titulo_limpo = titulo_video.replace("/", "").replace("\\", "").replace(" ", "_") caminho_mp4 = f"{TMP_DIR}/{titulo_limpo}_{job_id}.mp4" try: url_real = urllib.parse.unquote(url_stream_video) cmd = [ "ffmpeg", "-y", "-i", url_real, "-c:v", "libx264", "-preset", "veryfast", "-crf", "23", "-c:a", "aac", "-b:a", "160k", "-movflags", "+faststart", caminho_mp4, ] res = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, timeout=600) if res.returncode != 0: raise Exception(f"Erro FFmpeg no co-processamento de video: {res.stderr}") if os.path.exists(caminho_mp4): bg = BackgroundTasks() bg.add_task(os.remove, caminho_mp4) headers = {"Content-Disposition": f'attachment; filename="{urllib.parse.quote(titulo_limpo)}.mp4"'} return FileResponse(caminho_mp4, media_type="video/mp4", headers=headers, background=bg) raise Exception("Falha ao gerar arquivo MP4") except Exception as e: traceback.print_exc() raise HTTPException(status_code=500, detail=str(e))