Spaces:
Running
on
T4
Running
on
T4
Update app.py
Browse files
app.py
CHANGED
|
@@ -295,8 +295,8 @@ async def health():
|
|
| 295 |
@fastapi_app.post("/face-swap")
|
| 296 |
async def face_swap_api(
|
| 297 |
source: UploadFile = File(...),
|
| 298 |
-
target_category_id: Optional[str] = Form(None),
|
| 299 |
-
category_id: Optional[str] = Form(None),
|
| 300 |
user_id: Optional[str] = Form(None),
|
| 301 |
new_subcategory_id: Optional[str] = Form(None),
|
| 302 |
user_email: str = Depends(verify_firebase_token)
|
|
@@ -306,7 +306,7 @@ async def face_swap_api(
|
|
| 306 |
|
| 307 |
try:
|
| 308 |
# ---------------------------------------------------------
|
| 309 |
-
# NORMALIZE EMPTY STRINGS
|
| 310 |
# ---------------------------------------------------------
|
| 311 |
if target_category_id == "": target_category_id = None
|
| 312 |
if new_subcategory_id == "": new_subcategory_id = None
|
|
@@ -338,29 +338,19 @@ async def face_swap_api(
|
|
| 338 |
upload_to_spaces(src_bytes, src_key, content_type=source.content_type)
|
| 339 |
|
| 340 |
# ---------------------------------------------------------
|
| 341 |
-
# TARGET IMAGE RETRIEVAL
|
| 342 |
# ---------------------------------------------------------
|
| 343 |
|
| 344 |
-
# CASE 1 — Old behavior (use DO Spaces target image)
|
| 345 |
if target_provided:
|
|
|
|
| 346 |
target_filename = f"{target_category_id}.png"
|
| 347 |
target_url = (
|
| 348 |
f"https://{DO_SPACES_BUCKET}.{DO_SPACES_REGION}."
|
| 349 |
f"digitaloceanspaces.com/bikini-theme/target/{target_filename}"
|
| 350 |
)
|
| 351 |
|
| 352 |
-
# Log click for old system (if user_id and category_id are passed)
|
| 353 |
-
if user_id and category_id:
|
| 354 |
-
await log_media_click(user_id, category_id)
|
| 355 |
-
|
| 356 |
-
resp = requests.get(target_url)
|
| 357 |
-
if resp.status_code != 200:
|
| 358 |
-
await log_faceswap_hit(user_email, "error", start_time, datetime.now(timezone.utc))
|
| 359 |
-
raise HTTPException(status_code=404, detail=f"Target image not found: {target_url}")
|
| 360 |
-
tgt_bytes = resp.content
|
| 361 |
-
|
| 362 |
-
# CASE 2 — New behavior (use subcategory asset image)
|
| 363 |
elif new_sub_provided:
|
|
|
|
| 364 |
|
| 365 |
try:
|
| 366 |
asset_oid = ObjectId(new_subcategory_id)
|
|
@@ -370,36 +360,38 @@ async def face_swap_api(
|
|
| 370 |
# 1. Find subcategory asset by asset_images._id
|
| 371 |
subcat_doc = await subcategories_col.find_one(
|
| 372 |
{"asset_images._id": asset_oid},
|
| 373 |
-
{"asset_images.$": 1
|
| 374 |
)
|
| 375 |
|
| 376 |
-
if not subcat_doc or "asset_images" not in subcat_doc
|
| 377 |
await log_faceswap_hit(user_email, "error", start_time, datetime.now(timezone.utc))
|
| 378 |
raise HTTPException(
|
| 379 |
status_code=404,
|
| 380 |
-
detail="Subcategory asset
|
| 381 |
)
|
| 382 |
|
| 383 |
asset_url = subcat_doc["asset_images"][0]["url"]
|
| 384 |
target_url = asset_url
|
| 385 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 386 |
|
| 387 |
-
# Log click for new system (uses parent category ID from the subcategory doc)
|
| 388 |
-
if user_id:
|
| 389 |
-
await log_media_click(user_id, parent_category_id)
|
| 390 |
-
|
| 391 |
-
# 2. Download target image
|
| 392 |
-
resp = requests.get(asset_url)
|
| 393 |
-
if resp.status_code != 200:
|
| 394 |
-
await log_faceswap_hit(user_email, "error", start_time, datetime.now(timezone.utc))
|
| 395 |
-
raise HTTPException(
|
| 396 |
-
status_code=404,
|
| 397 |
-
detail=f"Failed to download asset image: {asset_url}"
|
| 398 |
-
)
|
| 399 |
-
tgt_bytes = resp.content
|
| 400 |
|
| 401 |
# ---------------------------------------------------------
|
| 402 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 403 |
# ---------------------------------------------------------
|
| 404 |
src_array = np.frombuffer(src_bytes, np.uint8)
|
| 405 |
tgt_array = np.frombuffer(tgt_bytes, np.uint8)
|
|
@@ -414,9 +406,7 @@ async def face_swap_api(
|
|
| 414 |
src_rgb = cv2.cvtColor(src_bgr, cv2.COLOR_BGR2RGB)
|
| 415 |
tgt_rgb = cv2.cvtColor(tgt_bgr, cv2.COLOR_BGR2RGB)
|
| 416 |
|
| 417 |
-
# ---------------------------------------------------------
|
| 418 |
# FACE SWAP & ENHANCE
|
| 419 |
-
# ---------------------------------------------------------
|
| 420 |
final_img, final_path, err = face_swap_and_enhance(src_rgb, tgt_rgb)
|
| 421 |
if err:
|
| 422 |
await log_faceswap_hit(user_email, "error", start_time, datetime.now(timezone.utc))
|
|
@@ -451,7 +441,6 @@ async def face_swap_api(
|
|
| 451 |
logger.error("Failed to write log_faceswap_hit: %s", log_exc)
|
| 452 |
|
| 453 |
logger.error(f"Critical /face-swap error: {e}")
|
| 454 |
-
# The HTTPException detail might include sensitive info like target_url, simplified for the final response
|
| 455 |
raise HTTPException(status_code=500, detail=f"Face swap failed: Internal server error.")
|
| 456 |
|
| 457 |
|
|
|
|
| 295 |
@fastapi_app.post("/face-swap")
|
| 296 |
async def face_swap_api(
|
| 297 |
source: UploadFile = File(...),
|
| 298 |
+
target_category_id: Optional[str] = Form(None),
|
| 299 |
+
category_id: Optional[str] = Form(None), # <-- The ID to use for media click logging
|
| 300 |
user_id: Optional[str] = Form(None),
|
| 301 |
new_subcategory_id: Optional[str] = Form(None),
|
| 302 |
user_email: str = Depends(verify_firebase_token)
|
|
|
|
| 306 |
|
| 307 |
try:
|
| 308 |
# ---------------------------------------------------------
|
| 309 |
+
# NORMALIZE EMPTY STRINGS
|
| 310 |
# ---------------------------------------------------------
|
| 311 |
if target_category_id == "": target_category_id = None
|
| 312 |
if new_subcategory_id == "": new_subcategory_id = None
|
|
|
|
| 338 |
upload_to_spaces(src_bytes, src_key, content_type=source.content_type)
|
| 339 |
|
| 340 |
# ---------------------------------------------------------
|
| 341 |
+
# TARGET IMAGE RETRIEVAL
|
| 342 |
# ---------------------------------------------------------
|
| 343 |
|
|
|
|
| 344 |
if target_provided:
|
| 345 |
+
# CASE 1 — Old behavior (use DO Spaces target image)
|
| 346 |
target_filename = f"{target_category_id}.png"
|
| 347 |
target_url = (
|
| 348 |
f"https://{DO_SPACES_BUCKET}.{DO_SPACES_REGION}."
|
| 349 |
f"digitaloceanspaces.com/bikini-theme/target/{target_filename}"
|
| 350 |
)
|
| 351 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 352 |
elif new_sub_provided:
|
| 353 |
+
# CASE 2 — New behavior (use subcategory asset image)
|
| 354 |
|
| 355 |
try:
|
| 356 |
asset_oid = ObjectId(new_subcategory_id)
|
|
|
|
| 360 |
# 1. Find subcategory asset by asset_images._id
|
| 361 |
subcat_doc = await subcategories_col.find_one(
|
| 362 |
{"asset_images._id": asset_oid},
|
| 363 |
+
{"asset_images.$": 1} # Only need the asset image URL
|
| 364 |
)
|
| 365 |
|
| 366 |
+
if not subcat_doc or "asset_images" not in subcat_doc:
|
| 367 |
await log_faceswap_hit(user_email, "error", start_time, datetime.now(timezone.utc))
|
| 368 |
raise HTTPException(
|
| 369 |
status_code=404,
|
| 370 |
+
detail="Subcategory asset image not found in DB."
|
| 371 |
)
|
| 372 |
|
| 373 |
asset_url = subcat_doc["asset_images"][0]["url"]
|
| 374 |
target_url = asset_url
|
| 375 |
+
|
| 376 |
+
# ---------------------------------------------------------
|
| 377 |
+
# DOWNLOAD TARGET IMAGE
|
| 378 |
+
# ---------------------------------------------------------
|
| 379 |
+
resp = requests.get(target_url)
|
| 380 |
+
if resp.status_code != 200:
|
| 381 |
+
await log_faceswap_hit(user_email, "error", start_time, datetime.now(timezone.utc))
|
| 382 |
+
raise HTTPException(status_code=404, detail=f"Target image not found or download failed: {target_url}")
|
| 383 |
+
tgt_bytes = resp.content
|
| 384 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 385 |
|
| 386 |
# ---------------------------------------------------------
|
| 387 |
+
# MEDIA CLICK LOGGING (Unified Logic)
|
| 388 |
+
# ---------------------------------------------------------
|
| 389 |
+
if user_id and category_id:
|
| 390 |
+
# Log only if both optional logging fields are provided
|
| 391 |
+
await log_media_click(user_id, category_id)
|
| 392 |
+
|
| 393 |
+
# ---------------------------------------------------------
|
| 394 |
+
# DECODE & FACE SWAP
|
| 395 |
# ---------------------------------------------------------
|
| 396 |
src_array = np.frombuffer(src_bytes, np.uint8)
|
| 397 |
tgt_array = np.frombuffer(tgt_bytes, np.uint8)
|
|
|
|
| 406 |
src_rgb = cv2.cvtColor(src_bgr, cv2.COLOR_BGR2RGB)
|
| 407 |
tgt_rgb = cv2.cvtColor(tgt_bgr, cv2.COLOR_BGR2RGB)
|
| 408 |
|
|
|
|
| 409 |
# FACE SWAP & ENHANCE
|
|
|
|
| 410 |
final_img, final_path, err = face_swap_and_enhance(src_rgb, tgt_rgb)
|
| 411 |
if err:
|
| 412 |
await log_faceswap_hit(user_email, "error", start_time, datetime.now(timezone.utc))
|
|
|
|
| 441 |
logger.error("Failed to write log_faceswap_hit: %s", log_exc)
|
| 442 |
|
| 443 |
logger.error(f"Critical /face-swap error: {e}")
|
|
|
|
| 444 |
raise HTTPException(status_code=500, detail=f"Face swap failed: Internal server error.")
|
| 445 |
|
| 446 |
|