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

Update app.py

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