LogicGoInfotechSpaces commited on
Commit
c423805
·
verified ·
1 Parent(s): 48ad1c4

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +237 -237
app.py CHANGED
@@ -249,270 +249,165 @@ async def test_admin_db():
249
  except Exception as e:
250
  return {"ok": False, "error": str(e), "url": ADMIN_MONGO_URL}
251
 
252
- @fastapi_app.post("/face-swap", dependencies=[Depends(verify_token)])
253
- async def face_swap_api(
254
- source: UploadFile = File(...),
255
- target_category_id: str = Form(None),
256
- new_category_id: str = Form(None),
257
- user_id: Optional[str] = Form(None),
258
- credentials: HTTPAuthorizationCredentials = Security(security)
259
- ):
260
- start_time = datetime.utcnow()
261
 
262
- try:
263
- # ------------------------------------------------------------------
264
- # VALIDATION
265
- # ------------------------------------------------------------------
266
- if target_category_id and new_category_id:
267
- raise HTTPException(400, "Provide only one of new_category_id or target_category_id.")
268
-
269
- if not target_category_id and not new_category_id:
270
- raise HTTPException(400, "Either new_category_id or target_category_id is required.")
271
-
272
- # ------------------------------------------------------------------
273
- # READ SOURCE IMAGE
274
- # ------------------------------------------------------------------
275
- src_bytes = await source.read()
276
- src_key = f"faceswap/source/{uuid.uuid4().hex}_{source.filename}"
277
- upload_to_spaces(src_bytes, src_key, content_type=source.content_type)
278
 
279
- # ------------------------------------------------------------------
280
- # CASE 1 : new_category_id → MongoDB lookup
281
- # ------------------------------------------------------------------
282
- if new_category_id:
283
 
284
- doc = await subcategories_col.find_one({
285
- "asset_images._id": ObjectId(new_category_id)
286
- })
287
 
288
- if not doc:
289
- raise HTTPException(404, "Asset image not found in database")
290
 
291
- # extract correct asset
292
- asset = next(
293
- (img for img in doc["asset_images"] if str(img["_id"]) == new_category_id),
294
- None
295
- )
296
 
297
- if not asset:
298
- raise HTTPException(404, "Asset image URL not found")
299
 
300
- # correct URL
301
- target_url = asset["url"]
302
 
303
- # correct categoryId (ObjectId)
304
- #category_oid = doc["categoryId"] # <-- DO NOT CONVERT TO STRING
305
- subcategory_oid = doc["_id"]
306
 
307
- # ------------------------------------------------------------------
308
- # MEDIA_CLICKS (ONLY IF user_id PRESENT)
309
- # ------------------------------------------------------------------
310
- # MEDIA_CLICKS (ONLY IF user_id PRESENT)
311
- if user_id:
312
- try:
313
- user_oid = ObjectId(user_id.strip())
314
- now = datetime.utcnow()
315
 
316
- # Try updating existing subCategory
317
- update_result = await media_clicks_col.update_one(
318
- {"userId": user_oid, "subCategories.subCategoryId": subcategory_oid},
319
- {
320
- "$set": {
321
- "updatedAt": now,
322
- "subCategories.$.lastClickedAt": now
323
- },
324
- "$inc": {"subCategories.$.click_count": 1}
325
- }
326
- )
327
 
328
- # If subCategory does NOT exist → push new one
329
- if update_result.matched_count == 0:
330
- await media_clicks_col.update_one(
331
- {"userId": user_oid},
332
- {
333
- "$setOnInsert": {"createdAt": now},
334
- "$set": {"updatedAt": now},
335
- "$push": {
336
- "subCategories": {
337
- "subCategoryId": subcategory_oid,
338
- "click_count": 1,
339
- "lastClickedAt": now
340
- }
341
- }
342
- },
343
- upsert=True
344
- )
345
 
346
- except Exception as media_err:
347
- logger.error(f"MEDIA_CLICK ERROR: {media_err}")
348
-
349
- # ------------------------------------------------------------------
350
- # CASE 2 : target_category_id → DigitalOcean path (unchanged logic)
351
- # ------------------------------------------------------------------
352
- if target_category_id:
353
- client = get_spaces_client()
354
- base_prefix = "faceswap/target/"
355
- resp = client.list_objects_v2(
356
- Bucket=DO_SPACES_BUCKET, Prefix=base_prefix, Delimiter="/"
357
- )
358
- categories = [p["Prefix"].split("/")[2] for p in resp.get("CommonPrefixes", [])]
359
-
360
- target_url = None
361
-
362
- for category in categories:
363
- original_prefix = f"faceswap/target/{category}/original/"
364
- original_objs = client.list_objects_v2(
365
- Bucket=DO_SPACES_BUCKET, Prefix=original_prefix
366
- ).get("Contents", [])
367
-
368
- original_files = sorted([obj["Key"] for obj in original_objs])
369
-
370
- for idx, file_key in enumerate(original_files, start=1):
371
- cid = f"{category.lower()}image_{idx}"
372
- if cid == target_category_id:
373
- target_url = f"{DO_SPACES_ENDPOINT}/{DO_SPACES_BUCKET}/{file_key}"
374
- break
375
-
376
- if target_url:
377
- break
378
-
379
- if not target_url:
380
- raise HTTPException(404, "Target categoryId not found")
381
-
382
- # ------------------------------------------------------------------
383
- # DOWNLOAD TARGET IMAGE
384
- # ------------------------------------------------------------------
385
- tgt_bytes = requests.get(target_url).content
386
-
387
- src_bgr = cv2.imdecode(np.frombuffer(src_bytes, np.uint8), cv2.IMREAD_COLOR)
388
- tgt_bgr = cv2.imdecode(np.frombuffer(tgt_bytes, np.uint8), cv2.IMREAD_COLOR)
389
-
390
- if src_bgr is None or tgt_bgr is None:
391
- raise HTTPException(400, "Invalid image data")
392
-
393
- src_rgb = cv2.cvtColor(src_bgr, cv2.COLOR_BGR2RGB)
394
- tgt_rgb = cv2.cvtColor(tgt_bgr, cv2.COLOR_BGR2RGB)
395
-
396
- # ------------------------------------------------------------------
397
- # FACE SWAP EXECUTION
398
- # ------------------------------------------------------------------
399
- final_img, final_path, err = face_swap_and_enhance(src_rgb, tgt_rgb)
400
- if err:
401
- raise HTTPException(500, err)
402
-
403
- with open(final_path, "rb") as f:
404
- result_bytes = f.read()
405
-
406
- result_key = f"faceswap/result/{uuid.uuid4().hex}_enhanced.png"
407
- result_url = upload_to_spaces(result_bytes, result_key)
408
- end_time = datetime.utcnow()
409
- response_time_ms = (end_time - start_time).total_seconds() * 1000
410
-
411
- if database is not None:
412
- await database.api_logs.insert_one({
413
- "endpoint": "/face-swap",
414
- "status": "success",
415
- "response_time_ms": response_time_ms,
416
- "timestamp": end_time
417
- })
418
-
419
-
420
- return {
421
- "result_key": result_key,
422
- "result_url": result_url
423
- }
424
-
425
-
426
- except Exception as e:
427
- end_time = datetime.utcnow()
428
- response_time_ms = (end_time - start_time).total_seconds() * 1000
429
-
430
- if database is not None:
431
- await database.api_logs.insert_one({
432
- "endpoint": "/face-swap",
433
- "status": "fail",
434
- "response_time_ms": response_time_ms,
435
- "timestamp": end_time,
436
- "error": str(e)
437
- })
438
-
439
- raise HTTPException(500, f"Face swap failed: {str(e)}")
440
-
441
-
442
-
443
 
 
 
444
 
445
- # @fastapi_app.post("/face-swap", dependencies=[Depends(verify_token)])
446
- # async def face_swap_api(
447
- # source: UploadFile = File(...),
448
- # target_category_id: str = Form(...),
449
- # credentials: HTTPAuthorizationCredentials = Security(security)
450
- # ):
451
- # start_time = datetime.utcnow() # start timer
452
- # try:
453
- # # Read source image
454
- # src_bytes = await source.read()
455
 
456
- # # Save source to Spaces
457
- # src_key = f"faceswap/source/{uuid.uuid4().hex}_{source.filename}"
458
- # upload_to_spaces(src_bytes, src_key, content_type=source.content_type)
459
 
460
- # # Find target image URL from categoryId
461
- # client = get_spaces_client()
462
- # base_prefix = "faceswap/target/"
463
- # resp = client.list_objects_v2(Bucket=DO_SPACES_BUCKET, Prefix=base_prefix, Delimiter="/")
464
- # categories = [prefix["Prefix"].split("/")[2] for prefix in resp.get("CommonPrefixes", [])]
465
- # target_url = None
466
- # for category in categories:
467
- # original_prefix = f"faceswap/target/{category}/original/"
468
- # thumb_prefix = f"faceswap/target/{category}/thumb/"
469
- # original_objects = client.list_objects_v2(Bucket=DO_SPACES_BUCKET, Prefix=original_prefix)
470
- # thumb_objects = client.list_objects_v2(Bucket=DO_SPACES_BUCKET, Prefix=thumb_prefix)
471
- # original_files = [obj["Key"].split("/")[-1] for obj in original_objects.get("Contents", []) if obj["Key"].endswith(".png")]
472
- # thumb_files = [obj["Key"].split("/")[-1] for obj in thumb_objects.get("Contents", []) if obj["Key"].endswith(".png")]
473
- # for idx, filename in enumerate(sorted(original_files), start=1):
474
- # cid = f"{category.lower()}image_{idx}"
475
- # if filename in thumb_files and cid == target_category_id:
476
- # target_url = f"https://{DO_SPACES_BUCKET}.blr1.digitaloceanspaces.com/{original_prefix}{filename}"
477
- # break
478
- # if target_url:
479
- # break
480
- # if not target_url:
481
- # raise HTTPException(status_code=404, detail="Target categoryId not found")
482
-
483
- # # Download target image from Spaces
484
- # resp = requests.get(target_url)
485
- # if resp.status_code != 200:
486
- # raise HTTPException(status_code=404, detail="Target image not found in Spaces")
487
- # tgt_bytes = resp.content
488
-
489
- # # Decode for processing
490
- # src_array = np.frombuffer(src_bytes, np.uint8)
491
- # tgt_array = np.frombuffer(tgt_bytes, np.uint8)
492
- # src_bgr = cv2.imdecode(src_array, cv2.IMREAD_COLOR)
493
- # tgt_bgr = cv2.imdecode(tgt_array, cv2.IMREAD_COLOR)
494
  # if src_bgr is None or tgt_bgr is None:
495
- # raise HTTPException(status_code=400, detail="Invalid image data")
496
 
497
  # src_rgb = cv2.cvtColor(src_bgr, cv2.COLOR_BGR2RGB)
498
  # tgt_rgb = cv2.cvtColor(tgt_bgr, cv2.COLOR_BGR2RGB)
499
 
500
- # # Run face swap pipeline
 
 
501
  # final_img, final_path, err = face_swap_and_enhance(src_rgb, tgt_rgb)
502
  # if err:
503
- # raise HTTPException(status_code=500, detail=err)
504
 
505
- # # Upload result to Spaces
506
  # with open(final_path, "rb") as f:
507
  # result_bytes = f.read()
508
- # result_key = f"faceswap/result/{uuid.uuid4().hex}_enhanced.png"
509
- # result_url = upload_to_spaces(result_bytes, result_key, content_type="image/png")
510
 
511
- # # Calculate response time
 
512
  # end_time = datetime.utcnow()
513
  # response_time_ms = (end_time - start_time).total_seconds() * 1000
514
 
515
- # # Log response time only
516
  # if database is not None:
517
  # await database.api_logs.insert_one({
518
  # "endpoint": "/face-swap",
@@ -521,12 +416,17 @@ async def face_swap_api(
521
  # "timestamp": end_time
522
  # })
523
 
524
- # return {"result_key": result_key, "result_url": result_url}
 
 
 
 
 
525
 
526
  # except Exception as e:
527
- # # Log response time even on error
528
  # end_time = datetime.utcnow()
529
  # response_time_ms = (end_time - start_time).total_seconds() * 1000
 
530
  # if database is not None:
531
  # await database.api_logs.insert_one({
532
  # "endpoint": "/face-swap",
@@ -535,7 +435,107 @@ async def face_swap_api(
535
  # "timestamp": end_time,
536
  # "error": str(e)
537
  # })
538
- # raise HTTPException(status_code=500, detail=f"Face swap failed: {str(e)}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
539
 
540
 
541
 
 
249
  except Exception as e:
250
  return {"ok": False, "error": str(e), "url": ADMIN_MONGO_URL}
251
 
252
+ # @fastapi_app.post("/face-swap", dependencies=[Depends(verify_token)])
253
+ # async def face_swap_api(
254
+ # source: UploadFile = File(...),
255
+ # target_category_id: str = Form(None),
256
+ # new_category_id: str = Form(None),
257
+ # user_id: Optional[str] = Form(None),
258
+ # credentials: HTTPAuthorizationCredentials = Security(security)
259
+ # ):
260
+ # start_time = datetime.utcnow()
261
 
262
+ # try:
263
+ # # ------------------------------------------------------------------
264
+ # # VALIDATION
265
+ # # ------------------------------------------------------------------
266
+ # if target_category_id and new_category_id:
267
+ # raise HTTPException(400, "Provide only one of new_category_id or target_category_id.")
268
+
269
+ # if not target_category_id and not new_category_id:
270
+ # raise HTTPException(400, "Either new_category_id or target_category_id is required.")
271
+
272
+ # # ------------------------------------------------------------------
273
+ # # READ SOURCE IMAGE
274
+ # # ------------------------------------------------------------------
275
+ # src_bytes = await source.read()
276
+ # src_key = f"faceswap/source/{uuid.uuid4().hex}_{source.filename}"
277
+ # upload_to_spaces(src_bytes, src_key, content_type=source.content_type)
278
 
279
+ # # ------------------------------------------------------------------
280
+ # # CASE 1 : new_category_id → MongoDB lookup
281
+ # # ------------------------------------------------------------------
282
+ # if new_category_id:
283
 
284
+ # doc = await subcategories_col.find_one({
285
+ # "asset_images._id": ObjectId(new_category_id)
286
+ # })
287
 
288
+ # if not doc:
289
+ # raise HTTPException(404, "Asset image not found in database")
290
 
291
+ # # extract correct asset
292
+ # asset = next(
293
+ # (img for img in doc["asset_images"] if str(img["_id"]) == new_category_id),
294
+ # None
295
+ # )
296
 
297
+ # if not asset:
298
+ # raise HTTPException(404, "Asset image URL not found")
299
 
300
+ # # correct URL
301
+ # target_url = asset["url"]
302
 
303
+ # # correct categoryId (ObjectId)
304
+ # #category_oid = doc["categoryId"] # <-- DO NOT CONVERT TO STRING
305
+ # subcategory_oid = doc["_id"]
306
 
307
+ # # ------------------------------------------------------------------
308
+ # # MEDIA_CLICKS (ONLY IF user_id PRESENT)
309
+ # # ------------------------------------------------------------------
310
+ # # MEDIA_CLICKS (ONLY IF user_id PRESENT)
311
+ # if user_id:
312
+ # try:
313
+ # user_oid = ObjectId(user_id.strip())
314
+ # now = datetime.utcnow()
315
 
316
+ # # Try updating existing subCategory
317
+ # update_result = await media_clicks_col.update_one(
318
+ # {"userId": user_oid, "subCategories.subCategoryId": subcategory_oid},
319
+ # {
320
+ # "$set": {
321
+ # "updatedAt": now,
322
+ # "subCategories.$.lastClickedAt": now
323
+ # },
324
+ # "$inc": {"subCategories.$.click_count": 1}
325
+ # }
326
+ # )
327
 
328
+ # # If subCategory does NOT exist → push new one
329
+ # if update_result.matched_count == 0:
330
+ # await media_clicks_col.update_one(
331
+ # {"userId": user_oid},
332
+ # {
333
+ # "$setOnInsert": {"createdAt": now},
334
+ # "$set": {"updatedAt": now},
335
+ # "$push": {
336
+ # "subCategories": {
337
+ # "subCategoryId": subcategory_oid,
338
+ # "click_count": 1,
339
+ # "lastClickedAt": now
340
+ # }
341
+ # }
342
+ # },
343
+ # upsert=True
344
+ # )
345
 
346
+ # except Exception as media_err:
347
+ # logger.error(f"MEDIA_CLICK ERROR: {media_err}")
348
+
349
+ # # ------------------------------------------------------------------
350
+ # # CASE 2 : target_category_id → DigitalOcean path (unchanged logic)
351
+ # # ------------------------------------------------------------------
352
+ # if target_category_id:
353
+ # client = get_spaces_client()
354
+ # base_prefix = "faceswap/target/"
355
+ # resp = client.list_objects_v2(
356
+ # Bucket=DO_SPACES_BUCKET, Prefix=base_prefix, Delimiter="/"
357
+ # )
358
+ # categories = [p["Prefix"].split("/")[2] for p in resp.get("CommonPrefixes", [])]
359
+
360
+ # target_url = None
361
+
362
+ # for category in categories:
363
+ # original_prefix = f"faceswap/target/{category}/original/"
364
+ # original_objs = client.list_objects_v2(
365
+ # Bucket=DO_SPACES_BUCKET, Prefix=original_prefix
366
+ # ).get("Contents", [])
367
+
368
+ # original_files = sorted([obj["Key"] for obj in original_objs])
369
+
370
+ # for idx, file_key in enumerate(original_files, start=1):
371
+ # cid = f"{category.lower()}image_{idx}"
372
+ # if cid == target_category_id:
373
+ # target_url = f"{DO_SPACES_ENDPOINT}/{DO_SPACES_BUCKET}/{file_key}"
374
+ # break
375
+
376
+ # if target_url:
377
+ # break
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
378
 
379
+ # if not target_url:
380
+ # raise HTTPException(404, "Target categoryId not found")
381
 
382
+ # # ------------------------------------------------------------------
383
+ # # DOWNLOAD TARGET IMAGE
384
+ # # ------------------------------------------------------------------
385
+ # tgt_bytes = requests.get(target_url).content
 
 
 
 
 
 
386
 
387
+ # src_bgr = cv2.imdecode(np.frombuffer(src_bytes, np.uint8), cv2.IMREAD_COLOR)
388
+ # tgt_bgr = cv2.imdecode(np.frombuffer(tgt_bytes, np.uint8), cv2.IMREAD_COLOR)
 
389
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
390
  # if src_bgr is None or tgt_bgr is None:
391
+ # raise HTTPException(400, "Invalid image data")
392
 
393
  # src_rgb = cv2.cvtColor(src_bgr, cv2.COLOR_BGR2RGB)
394
  # tgt_rgb = cv2.cvtColor(tgt_bgr, cv2.COLOR_BGR2RGB)
395
 
396
+ # # ------------------------------------------------------------------
397
+ # # FACE SWAP EXECUTION
398
+ # # ------------------------------------------------------------------
399
  # final_img, final_path, err = face_swap_and_enhance(src_rgb, tgt_rgb)
400
  # if err:
401
+ # raise HTTPException(500, err)
402
 
 
403
  # with open(final_path, "rb") as f:
404
  # result_bytes = f.read()
 
 
405
 
406
+ # result_key = f"faceswap/result/{uuid.uuid4().hex}_enhanced.png"
407
+ # result_url = upload_to_spaces(result_bytes, result_key)
408
  # end_time = datetime.utcnow()
409
  # response_time_ms = (end_time - start_time).total_seconds() * 1000
410
 
 
411
  # if database is not None:
412
  # await database.api_logs.insert_one({
413
  # "endpoint": "/face-swap",
 
416
  # "timestamp": end_time
417
  # })
418
 
419
+
420
+ # return {
421
+ # "result_key": result_key,
422
+ # "result_url": result_url
423
+ # }
424
+
425
 
426
  # except Exception as e:
 
427
  # end_time = datetime.utcnow()
428
  # response_time_ms = (end_time - start_time).total_seconds() * 1000
429
+
430
  # if database is not None:
431
  # await database.api_logs.insert_one({
432
  # "endpoint": "/face-swap",
 
435
  # "timestamp": end_time,
436
  # "error": str(e)
437
  # })
438
+
439
+ # raise HTTPException(500, f"Face swap failed: {str(e)}")
440
+
441
+
442
+
443
+
444
+
445
+ @fastapi_app.post("/face-swap", dependencies=[Depends(verify_token)])
446
+ async def face_swap_api(
447
+ source: UploadFile = File(...),
448
+ target_category_id: str = Form(...),
449
+ credentials: HTTPAuthorizationCredentials = Security(security)
450
+ ):
451
+ start_time = datetime.utcnow() # start timer
452
+ try:
453
+ # Read source image
454
+ src_bytes = await source.read()
455
+
456
+ # Save source to Spaces
457
+ src_key = f"faceswap/source/{uuid.uuid4().hex}_{source.filename}"
458
+ upload_to_spaces(src_bytes, src_key, content_type=source.content_type)
459
+
460
+ # Find target image URL from categoryId
461
+ client = get_spaces_client()
462
+ base_prefix = "faceswap/target/"
463
+ resp = client.list_objects_v2(Bucket=DO_SPACES_BUCKET, Prefix=base_prefix, Delimiter="/")
464
+ categories = [prefix["Prefix"].split("/")[2] for prefix in resp.get("CommonPrefixes", [])]
465
+ target_url = None
466
+ for category in categories:
467
+ original_prefix = f"faceswap/target/{category}/original/"
468
+ thumb_prefix = f"faceswap/target/{category}/thumb/"
469
+ original_objects = client.list_objects_v2(Bucket=DO_SPACES_BUCKET, Prefix=original_prefix)
470
+ thumb_objects = client.list_objects_v2(Bucket=DO_SPACES_BUCKET, Prefix=thumb_prefix)
471
+ original_files = [obj["Key"].split("/")[-1] for obj in original_objects.get("Contents", []) if obj["Key"].endswith(".png")]
472
+ thumb_files = [obj["Key"].split("/")[-1] for obj in thumb_objects.get("Contents", []) if obj["Key"].endswith(".png")]
473
+ for idx, filename in enumerate(sorted(original_files), start=1):
474
+ cid = f"{category.lower()}image_{idx}"
475
+ if filename in thumb_files and cid == target_category_id:
476
+ target_url = f"https://{DO_SPACES_BUCKET}.blr1.digitaloceanspaces.com/{original_prefix}{filename}"
477
+ break
478
+ if target_url:
479
+ break
480
+ if not target_url:
481
+ raise HTTPException(status_code=404, detail="Target categoryId not found")
482
+
483
+ # Download target image from Spaces
484
+ resp = requests.get(target_url)
485
+ if resp.status_code != 200:
486
+ raise HTTPException(status_code=404, detail="Target image not found in Spaces")
487
+ tgt_bytes = resp.content
488
+
489
+ # Decode for processing
490
+ src_array = np.frombuffer(src_bytes, np.uint8)
491
+ tgt_array = np.frombuffer(tgt_bytes, np.uint8)
492
+ src_bgr = cv2.imdecode(src_array, cv2.IMREAD_COLOR)
493
+ tgt_bgr = cv2.imdecode(tgt_array, cv2.IMREAD_COLOR)
494
+ if src_bgr is None or tgt_bgr is None:
495
+ raise HTTPException(status_code=400, detail="Invalid image data")
496
+
497
+ src_rgb = cv2.cvtColor(src_bgr, cv2.COLOR_BGR2RGB)
498
+ tgt_rgb = cv2.cvtColor(tgt_bgr, cv2.COLOR_BGR2RGB)
499
+
500
+ # Run face swap pipeline
501
+ final_img, final_path, err = face_swap_and_enhance(src_rgb, tgt_rgb)
502
+ if err:
503
+ raise HTTPException(status_code=500, detail=err)
504
+
505
+ # Upload result to Spaces
506
+ with open(final_path, "rb") as f:
507
+ result_bytes = f.read()
508
+ result_key = f"faceswap/result/{uuid.uuid4().hex}_enhanced.png"
509
+ result_url = upload_to_spaces(result_bytes, result_key, content_type="image/png")
510
+
511
+ # Calculate response time
512
+ end_time = datetime.utcnow()
513
+ response_time_ms = (end_time - start_time).total_seconds() * 1000
514
+
515
+ # Log response time only
516
+ if database is not None:
517
+ await database.api_logs.insert_one({
518
+ "endpoint": "/face-swap",
519
+ "status": "success",
520
+ "response_time_ms": response_time_ms,
521
+ "timestamp": end_time
522
+ })
523
+
524
+ return {"result_key": result_key, "result_url": result_url}
525
+
526
+ except Exception as e:
527
+ # Log response time even on error
528
+ end_time = datetime.utcnow()
529
+ response_time_ms = (end_time - start_time).total_seconds() * 1000
530
+ if database is not None:
531
+ await database.api_logs.insert_one({
532
+ "endpoint": "/face-swap",
533
+ "status": "fail",
534
+ "response_time_ms": response_time_ms,
535
+ "timestamp": end_time,
536
+ "error": str(e)
537
+ })
538
+ raise HTTPException(status_code=500, detail=f"Face swap failed: {str(e)}")
539
 
540
 
541