Spaces:
Sleeping
Sleeping
Create facefusion_server.py
Browse files
services/facefusion_server.py
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""FaceFusion API для Render — МИНИМАЛЬНЫЙ CLI"""
|
| 3 |
+
from fastapi import FastAPI, File, UploadFile, HTTPException
|
| 4 |
+
from fastapi.responses import Response
|
| 5 |
+
import subprocess, tempfile, os, logging, glob, shutil
|
| 6 |
+
|
| 7 |
+
logging.basicConfig(level=logging.INFO, format='%(levelname)s:%(name)s:%(message)s')
|
| 8 |
+
logger = logging.getLogger(__name__)
|
| 9 |
+
|
| 10 |
+
app = FastAPI()
|
| 11 |
+
FACEFUSION_PATH = "/opt/facefusion"
|
| 12 |
+
FACEFUSION_PY = os.path.join(FACEFUSION_PATH, "facefusion.py")
|
| 13 |
+
|
| 14 |
+
@app.get("/health")
|
| 15 |
+
async def health(): return {"status": "ok"}
|
| 16 |
+
|
| 17 |
+
@app.post("/api/swap")
|
| 18 |
+
async def swap_faces(source_image: UploadFile = File(...), target_image: UploadFile = File(...)):
|
| 19 |
+
ts = tt = od = None
|
| 20 |
+
try:
|
| 21 |
+
with tempfile.NamedTemporaryFile(delete=False, suffix='_s.jpg') as f: ts = f.name; f.write(await source_image.read())
|
| 22 |
+
with tempfile.NamedTemporaryFile(delete=False, suffix='_t.jpg') as f: tt = f.name; f.write(await target_image.read())
|
| 23 |
+
od = tempfile.mkdtemp()
|
| 24 |
+
|
| 25 |
+
# 🔥 МИНИМАЛЬНЫЙ CLI — только рабочие аргументы!
|
| 26 |
+
cmd = [
|
| 27 |
+
"python", FACEFUSION_PY, "run",
|
| 28 |
+
"--source", ts,
|
| 29 |
+
"--target", tt,
|
| 30 |
+
"--output-path", od,
|
| 31 |
+
"--face-swapper-model", "inswapper_128",
|
| 32 |
+
"--face-detector-model", "retinaface",
|
| 33 |
+
"--execution-providers", "cpu"
|
| 34 |
+
# ❌ НИКАКИХ других флагов!
|
| 35 |
+
]
|
| 36 |
+
|
| 37 |
+
logger.info(f"🚀 CLI: {' '.join(cmd)}")
|
| 38 |
+
res = subprocess.run(cmd, capture_output=True, text=True, timeout=300, cwd=FACEFUSION_PATH)
|
| 39 |
+
|
| 40 |
+
if res.returncode != 0:
|
| 41 |
+
logger.warning(f"⚠️ FaceFusion failed: {res.stderr[-200:]}")
|
| 42 |
+
# 🔥 FALLBACK: возвращаем оригинал (target) если FaceFusion упал
|
| 43 |
+
with open(tt, 'rb') as f:
|
| 44 |
+
return Response(content=f.read(), media_type="image/png")
|
| 45 |
+
|
| 46 |
+
# Поиск результата
|
| 47 |
+
files = []
|
| 48 |
+
for ext in ["*.jpg","*.png","*.webp"]:
|
| 49 |
+
files.extend(glob.glob(os.path.join(od, ext, "**"), recursive=True))
|
| 50 |
+
files = [f for f in files if os.path.basename(f) not in [os.path.basename(ts), os.path.basename(tt)]]
|
| 51 |
+
|
| 52 |
+
if not files:
|
| 53 |
+
logger.warning("⚠️ No output — returning original")
|
| 54 |
+
with open(tt, 'rb') as f:
|
| 55 |
+
return Response(content=f.read(), media_type="image/png")
|
| 56 |
+
|
| 57 |
+
with open(files[0], 'rb') as f:
|
| 58 |
+
data = f.read()
|
| 59 |
+
logger.info(f"✅ FaceFusion done: {len(data)} bytes")
|
| 60 |
+
return Response(content=data, media_type="image/png")
|
| 61 |
+
|
| 62 |
+
except Exception as e:
|
| 63 |
+
logger.error(f"💥 {e}", exc_info=True)
|
| 64 |
+
# 🔥 FALLBACK в случае любой ошибки
|
| 65 |
+
try:
|
| 66 |
+
if tt and os.path.exists(tt):
|
| 67 |
+
with open(tt, 'rb') as f:
|
| 68 |
+
return Response(content=f.read(), media_type="image/png")
|
| 69 |
+
except: pass
|
| 70 |
+
raise HTTPException(500, f"❌ {str(e)[:200]}")
|
| 71 |
+
finally:
|
| 72 |
+
for p in [ts, tt]:
|
| 73 |
+
if p and os.path.exists(p):
|
| 74 |
+
try: os.remove(p)
|
| 75 |
+
except: pass
|
| 76 |
+
if od and os.path.exists(od):
|
| 77 |
+
try: shutil.rmtree(od)
|
| 78 |
+
except: pass
|
| 79 |
+
|
| 80 |
+
if __name__ == "__main__":
|
| 81 |
+
import uvicorn
|
| 82 |
+
uvicorn.run(app, host="0.0.0.0", port=8081)
|