Photobot / services /facefusion_server.py
Dmitry1313's picture
Create facefusion_server.py
c055c40 verified
#!/usr/bin/env python3
"""FaceFusion API для Render — МИНИМАЛЬНЫЙ CLI"""
from fastapi import FastAPI, File, UploadFile, HTTPException
from fastapi.responses import Response
import subprocess, tempfile, os, logging, glob, shutil
logging.basicConfig(level=logging.INFO, format='%(levelname)s:%(name)s:%(message)s')
logger = logging.getLogger(__name__)
app = FastAPI()
FACEFUSION_PATH = "/opt/facefusion"
FACEFUSION_PY = os.path.join(FACEFUSION_PATH, "facefusion.py")
@app.get("/health")
async def health(): return {"status": "ok"}
@app.post("/api/swap")
async def swap_faces(source_image: UploadFile = File(...), target_image: UploadFile = File(...)):
ts = tt = od = None
try:
with tempfile.NamedTemporaryFile(delete=False, suffix='_s.jpg') as f: ts = f.name; f.write(await source_image.read())
with tempfile.NamedTemporaryFile(delete=False, suffix='_t.jpg') as f: tt = f.name; f.write(await target_image.read())
od = tempfile.mkdtemp()
# 🔥 МИНИМАЛЬНЫЙ CLI — только рабочие аргументы!
cmd = [
"python", FACEFUSION_PY, "run",
"--source", ts,
"--target", tt,
"--output-path", od,
"--face-swapper-model", "inswapper_128",
"--face-detector-model", "retinaface",
"--execution-providers", "cpu"
# ❌ НИКАКИХ других флагов!
]
logger.info(f"🚀 CLI: {' '.join(cmd)}")
res = subprocess.run(cmd, capture_output=True, text=True, timeout=300, cwd=FACEFUSION_PATH)
if res.returncode != 0:
logger.warning(f"⚠️ FaceFusion failed: {res.stderr[-200:]}")
# 🔥 FALLBACK: возвращаем оригинал (target) если FaceFusion упал
with open(tt, 'rb') as f:
return Response(content=f.read(), media_type="image/png")
# Поиск результата
files = []
for ext in ["*.jpg","*.png","*.webp"]:
files.extend(glob.glob(os.path.join(od, ext, "**"), recursive=True))
files = [f for f in files if os.path.basename(f) not in [os.path.basename(ts), os.path.basename(tt)]]
if not files:
logger.warning("⚠️ No output — returning original")
with open(tt, 'rb') as f:
return Response(content=f.read(), media_type="image/png")
with open(files[0], 'rb') as f:
data = f.read()
logger.info(f"✅ FaceFusion done: {len(data)} bytes")
return Response(content=data, media_type="image/png")
except Exception as e:
logger.error(f"💥 {e}", exc_info=True)
# 🔥 FALLBACK в случае любой ошибки
try:
if tt and os.path.exists(tt):
with open(tt, 'rb') as f:
return Response(content=f.read(), media_type="image/png")
except: pass
raise HTTPException(500, f"❌ {str(e)[:200]}")
finally:
for p in [ts, tt]:
if p and os.path.exists(p):
try: os.remove(p)
except: pass
if od and os.path.exists(od):
try: shutil.rmtree(od)
except: pass
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8081)