Spaces:
Running
on
T4
Running
on
T4
Commit
·
031a705
1
Parent(s):
3c79ccf
added /list-images and faceswap accepts categoryId
Browse files- __pycache__/app.cpython-313.pyc +0 -0
- __pycache__/list.cpython-313.pyc +0 -0
- app.py +81 -9
- list.py +45 -0
__pycache__/app.cpython-313.pyc
ADDED
|
Binary file (15.8 kB). View file
|
|
|
__pycache__/list.cpython-313.pyc
ADDED
|
Binary file (2.48 kB). View file
|
|
|
app.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
|
|
| 1 |
import os
|
| 2 |
os.environ["OMP_NUM_THREADS"] = "1"
|
| 3 |
import shutil
|
|
@@ -13,7 +14,7 @@ import insightface
|
|
| 13 |
from insightface.app import FaceAnalysis
|
| 14 |
from huggingface_hub import hf_hub_download
|
| 15 |
|
| 16 |
-
from fastapi import FastAPI, UploadFile, File, HTTPException, Response, Depends, Security
|
| 17 |
from fastapi.responses import RedirectResponse
|
| 18 |
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
| 19 |
from pydantic import BaseModel
|
|
@@ -103,7 +104,7 @@ ensure_codeformer()
|
|
| 103 |
# --------------------- MongoDB (for API logs only) ---------------------
|
| 104 |
MONGODB_URL = os.getenv("MONGODB_URL")
|
| 105 |
|
| 106 |
-
client
|
| 107 |
database = None
|
| 108 |
|
| 109 |
# --------------------- FastAPI ---------------------
|
|
@@ -235,26 +236,58 @@ def download_from_spaces(key):
|
|
| 235 |
def root():
|
| 236 |
return RedirectResponse("/gradio")
|
| 237 |
|
| 238 |
-
@fastapi_app.get("/health"
|
| 239 |
async def health():
|
| 240 |
return {"status": "healthy"}
|
| 241 |
|
|
|
|
|
|
|
|
|
|
| 242 |
@fastapi_app.post("/face-swap", dependencies=[Depends(verify_token)])
|
| 243 |
async def face_swap_api(
|
| 244 |
source: UploadFile = File(...),
|
| 245 |
-
|
| 246 |
credentials: HTTPAuthorizationCredentials = Security(security)
|
| 247 |
):
|
|
|
|
| 248 |
try:
|
| 249 |
-
# Read
|
| 250 |
src_bytes = await source.read()
|
| 251 |
-
tgt_bytes = await target.read()
|
| 252 |
|
| 253 |
-
# Save to Spaces
|
| 254 |
src_key = f"faceswap/source/{uuid.uuid4().hex}_{source.filename}"
|
| 255 |
-
tgt_key = f"faceswap/target/{uuid.uuid4().hex}_{target.filename}"
|
| 256 |
upload_to_spaces(src_bytes, src_key, content_type=source.content_type)
|
| 257 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 258 |
|
| 259 |
# Decode for processing
|
| 260 |
src_array = np.frombuffer(src_bytes, np.uint8)
|
|
@@ -301,6 +334,45 @@ async def preview_result(result_key: str):
|
|
| 301 |
headers={"Content-Disposition": "inline; filename=result.png"}
|
| 302 |
)
|
| 303 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 304 |
# --------------------- Mount Gradio ---------------------
|
| 305 |
fastapi_app = mount_gradio_app(fastapi_app, demo, path="/gradio")
|
| 306 |
|
|
|
|
| 1 |
+
# --------------------- List Images Endpoint ---------------------
|
| 2 |
import os
|
| 3 |
os.environ["OMP_NUM_THREADS"] = "1"
|
| 4 |
import shutil
|
|
|
|
| 14 |
from insightface.app import FaceAnalysis
|
| 15 |
from huggingface_hub import hf_hub_download
|
| 16 |
|
| 17 |
+
from fastapi import FastAPI, UploadFile, File, HTTPException, Response, Depends, Security, Query
|
| 18 |
from fastapi.responses import RedirectResponse
|
| 19 |
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
| 20 |
from pydantic import BaseModel
|
|
|
|
| 104 |
# --------------------- MongoDB (for API logs only) ---------------------
|
| 105 |
MONGODB_URL = os.getenv("MONGODB_URL")
|
| 106 |
|
| 107 |
+
client = None
|
| 108 |
database = None
|
| 109 |
|
| 110 |
# --------------------- FastAPI ---------------------
|
|
|
|
| 236 |
def root():
|
| 237 |
return RedirectResponse("/gradio")
|
| 238 |
|
| 239 |
+
@fastapi_app.get("/health")
|
| 240 |
async def health():
|
| 241 |
return {"status": "healthy"}
|
| 242 |
|
| 243 |
+
from fastapi import Form
|
| 244 |
+
import requests
|
| 245 |
+
|
| 246 |
@fastapi_app.post("/face-swap", dependencies=[Depends(verify_token)])
|
| 247 |
async def face_swap_api(
|
| 248 |
source: UploadFile = File(...),
|
| 249 |
+
target_category_id: str = Form(...),
|
| 250 |
credentials: HTTPAuthorizationCredentials = Security(security)
|
| 251 |
):
|
| 252 |
+
|
| 253 |
try:
|
| 254 |
+
# Read source image
|
| 255 |
src_bytes = await source.read()
|
|
|
|
| 256 |
|
| 257 |
+
# Save source to Spaces
|
| 258 |
src_key = f"faceswap/source/{uuid.uuid4().hex}_{source.filename}"
|
|
|
|
| 259 |
upload_to_spaces(src_bytes, src_key, content_type=source.content_type)
|
| 260 |
+
|
| 261 |
+
# Find target image URL from categoryId by searching all categories
|
| 262 |
+
client = get_spaces_client()
|
| 263 |
+
base_prefix = "faceswap/target/"
|
| 264 |
+
resp = client.list_objects_v2(Bucket=DO_SPACES_BUCKET, Prefix=base_prefix, Delimiter="/")
|
| 265 |
+
categories = [prefix["Prefix"].split("/")[2] for prefix in resp.get("CommonPrefixes", [])]
|
| 266 |
+
target_url = None
|
| 267 |
+
for category in categories:
|
| 268 |
+
original_prefix = f"faceswap/target/{category}/original/"
|
| 269 |
+
thumb_prefix = f"faceswap/target/{category}/thumb/"
|
| 270 |
+
original_objects = client.list_objects_v2(Bucket=DO_SPACES_BUCKET, Prefix=original_prefix)
|
| 271 |
+
thumb_objects = client.list_objects_v2(Bucket=DO_SPACES_BUCKET, Prefix=thumb_prefix)
|
| 272 |
+
original_files = [obj["Key"].split("/")[-1] for obj in original_objects.get("Contents", []) if obj["Key"].endswith(".png")]
|
| 273 |
+
thumb_files = [obj["Key"].split("/")[-1] for obj in thumb_objects.get("Contents", []) if obj["Key"].endswith(".png")]
|
| 274 |
+
for idx, filename in enumerate(sorted(original_files), start=1):
|
| 275 |
+
cid = f"{category.lower()}image_{idx}"
|
| 276 |
+
if filename in thumb_files and cid == target_category_id:
|
| 277 |
+
target_url = f"https://{DO_SPACES_BUCKET}.blr1.digitaloceanspaces.com/{original_prefix}{filename}"
|
| 278 |
+
break
|
| 279 |
+
if target_url:
|
| 280 |
+
break
|
| 281 |
+
if not target_url:
|
| 282 |
+
await log_faceswap_hit(credentials.credentials, status="error")
|
| 283 |
+
raise HTTPException(status_code=404, detail="Target categoryId not found")
|
| 284 |
+
|
| 285 |
+
# Download target image from Spaces
|
| 286 |
+
resp = requests.get(target_url)
|
| 287 |
+
if resp.status_code != 200:
|
| 288 |
+
await log_faceswap_hit(credentials.credentials, status="error")
|
| 289 |
+
raise HTTPException(status_code=404, detail="Target image not found in Spaces")
|
| 290 |
+
tgt_bytes = resp.content
|
| 291 |
|
| 292 |
# Decode for processing
|
| 293 |
src_array = np.frombuffer(src_bytes, np.uint8)
|
|
|
|
| 334 |
headers={"Content-Disposition": "inline; filename=result.png"}
|
| 335 |
)
|
| 336 |
|
| 337 |
+
@fastapi_app.get("/list-images", dependencies=[Depends(verify_token)])
|
| 338 |
+
def list_images(category: str = Query(None, description="Category name to list images for. If not provided, returns all available categories.")):
|
| 339 |
+
client = get_spaces_client()
|
| 340 |
+
base_prefix = "faceswap/target/"
|
| 341 |
+
|
| 342 |
+
# If no category is provided, list all categories (folders) under faceswap/target/
|
| 343 |
+
if not category:
|
| 344 |
+
# List all prefixes (categories)
|
| 345 |
+
resp = client.list_objects_v2(Bucket=DO_SPACES_BUCKET, Prefix=base_prefix, Delimiter="/")
|
| 346 |
+
categories = []
|
| 347 |
+
for prefix in resp.get("CommonPrefixes", []):
|
| 348 |
+
cat = prefix["Prefix"].split("/")[2] # faceswap/target/<category>/
|
| 349 |
+
categories.append(cat)
|
| 350 |
+
return {"categories": categories, "note": "These are available categories. Use /list-images?category=<category> to list images."}
|
| 351 |
+
|
| 352 |
+
# If category is provided, list images in that category
|
| 353 |
+
original_prefix = f"faceswap/target/{category}/original/"
|
| 354 |
+
thumb_prefix = f"faceswap/target/{category}/thumb/"
|
| 355 |
+
|
| 356 |
+
original_objects = client.list_objects_v2(Bucket=DO_SPACES_BUCKET, Prefix=original_prefix)
|
| 357 |
+
thumb_objects = client.list_objects_v2(Bucket=DO_SPACES_BUCKET, Prefix=thumb_prefix)
|
| 358 |
+
|
| 359 |
+
original_files = [obj["Key"].split("/")[-1] for obj in original_objects.get("Contents", []) if obj["Key"].endswith(".png")]
|
| 360 |
+
thumb_files = [obj["Key"].split("/")[-1] for obj in thumb_objects.get("Contents", []) if obj["Key"].endswith(".png")]
|
| 361 |
+
|
| 362 |
+
items = []
|
| 363 |
+
for idx, filename in enumerate(sorted(original_files), start=1):
|
| 364 |
+
if filename in thumb_files:
|
| 365 |
+
items.append({
|
| 366 |
+
"categoryId": f"{category.lower()}image_{idx}",
|
| 367 |
+
"url": f"https://{DO_SPACES_BUCKET}.blr1.digitaloceanspaces.com/{original_prefix}{filename}",
|
| 368 |
+
"thumburl": f"https://{DO_SPACES_BUCKET}.blr1.digitaloceanspaces.com/{thumb_prefix}{filename}"
|
| 369 |
+
})
|
| 370 |
+
|
| 371 |
+
return {
|
| 372 |
+
"categoryName": category,
|
| 373 |
+
"items": items,
|
| 374 |
+
"note": "The URLs returned are public and do not require a bearer token."
|
| 375 |
+
}
|
| 376 |
# --------------------- Mount Gradio ---------------------
|
| 377 |
fastapi_app = mount_gradio_app(fastapi_app, demo, path="/gradio")
|
| 378 |
|
list.py
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# from fastapi import FastAPI
|
| 2 |
+
# import boto3
|
| 3 |
+
# import os
|
| 4 |
+
|
| 5 |
+
# app = FastAPI()
|
| 6 |
+
|
| 7 |
+
# # Configure DigitalOcean Spaces (S3 Compatible)
|
| 8 |
+
# session = boto3.session.Session()
|
| 9 |
+
# client = session.client(
|
| 10 |
+
# 's3',
|
| 11 |
+
# region_name='blr1', # DigitalOcean region
|
| 12 |
+
# endpoint_url='https://blr1.digitaloceanspaces.com', # endpoint
|
| 13 |
+
# aws_access_key_id=os.getenv("DO_SPACES_KEY"),
|
| 14 |
+
# aws_secret_access_key=os.getenv("DO_SPACES_SECRET")
|
| 15 |
+
# )
|
| 16 |
+
|
| 17 |
+
# BUCKET_NAME = "milestone"
|
| 18 |
+
# BASE_URL = f"https://{BUCKET_NAME}.blr1.digitaloceanspaces.com"
|
| 19 |
+
|
| 20 |
+
# @app.get("/list-images")
|
| 21 |
+
# def list_images():
|
| 22 |
+
# category_name = "BirthdayParty"
|
| 23 |
+
# original_prefix = f"faceswap/target/{category_name}/original/"
|
| 24 |
+
# thumb_prefix = f"faceswap/target/{category_name}/thumb/"
|
| 25 |
+
|
| 26 |
+
# # List objects in original
|
| 27 |
+
# original_objects = client.list_objects_v2(Bucket=BUCKET_NAME, Prefix=original_prefix)
|
| 28 |
+
# thumb_objects = client.list_objects_v2(Bucket=BUCKET_NAME, Prefix=thumb_prefix)
|
| 29 |
+
|
| 30 |
+
# original_files = [obj["Key"].split("/")[-1] for obj in original_objects.get("Contents", []) if obj["Key"].endswith(".png")]
|
| 31 |
+
# thumb_files = [obj["Key"].split("/")[-1] for obj in thumb_objects.get("Contents", []) if obj["Key"].endswith(".png")]
|
| 32 |
+
|
| 33 |
+
# items = []
|
| 34 |
+
# for idx, filename in enumerate(sorted(original_files), start=1):
|
| 35 |
+
# if filename in thumb_files: # match only if both exist
|
| 36 |
+
# items.append({
|
| 37 |
+
# "categoryId": f"birthdaypartyimage_{idx}",
|
| 38 |
+
# "url": f"{BASE_URL}/{original_prefix}{filename}",
|
| 39 |
+
# "thumburl": f"{BASE_URL}/{thumb_prefix}{filename}"
|
| 40 |
+
# })
|
| 41 |
+
|
| 42 |
+
# return {
|
| 43 |
+
# "categoryName": category_name,
|
| 44 |
+
# "items": items
|
| 45 |
+
# }
|