diff --git a/src/web/services/face_service.py b/src/web/services/face_service.py index 62ddec1..ba8c4d7 100644 --- a/src/web/services/face_service.py +++ b/src/web/services/face_service.py @@ -702,6 +702,8 @@ def process_unprocessed_photos( first_photo_start = time.time() print(f"[FaceService] Starting first photo processing...") + # Process photo fully (including pose detection, DeepFace, and database commit) + # This ensures all data is complete before checking for cancellation faces_detected, faces_stored = process_photo_faces( db, photo, @@ -718,27 +720,97 @@ def process_unprocessed_photos( first_photo_time = time.time() - first_photo_start print(f"[FaceService] First photo completed in {first_photo_time:.2f}s") + # Check for cancellation AFTER finishing the current photo completely + # This allows the current photo to complete (including pose detection and DB commit), + # then stops before the next one + if check_cancelled(): + print(f"[FaceService] Job cancelled after finishing photo {idx}/{total}") + # Update progress to show cancellation status + if update_progress: + try: + update_progress( + idx, + total, + "Cancelled by user - finished current photo", + total_faces_detected, + total_faces_stored, + ) + except KeyboardInterrupt: + # If update_progress raises KeyboardInterrupt, that's expected + # The cancellation check already happened, so we're good + pass + break + + # Update progress only if NOT cancelled (to avoid unnecessary KeyboardInterrupt) if update_progress: - update_progress( - idx, - total, - f"Completed {photo.filename} ({idx}/{total})", - total_faces_detected, - total_faces_stored, - ) + try: + update_progress( + idx, + total, + f"Completed {photo.filename} ({idx}/{total})", + total_faces_detected, + total_faces_stored, + ) + except KeyboardInterrupt: + # If cancellation was detected during update_progress, check again and break + if check_cancelled(): + print(f"[FaceService] Job cancelled during progress update after photo {idx}/{total}") + break + # Re-raise if it wasn't a cancellation + raise + except KeyboardInterrupt: + # Cancellation was requested - stop processing gracefully + print(f"[FaceService] Job cancelled during processing of photo {idx}/{total}") + if check_cancelled(): + if update_progress: + try: + update_progress( + idx, + total, + "Cancelled by user", + total_faces_detected, + total_faces_stored, + ) + except KeyboardInterrupt: + pass + break + # Re-raise if it wasn't a cancellation + raise except Exception as e: # Log error but continue processing other photos print(f"[FaceService] Error processing photo {photo.filename}: {e}") import traceback traceback.print_exc() if update_progress: - update_progress( - idx, - total, - f"Error: {photo.filename}", - total_faces_detected, - total_faces_stored, - ) + try: + update_progress( + idx, + total, + f"Error: {photo.filename}", + total_faces_detected, + total_faces_stored, + ) + except KeyboardInterrupt: + # If cancellation detected during error progress update, stop + if check_cancelled(): + print(f"[FaceService] Job cancelled after error on photo {idx}/{total}") + break + + # Check for cancellation after handling error + if check_cancelled(): + print(f"[FaceService] Job cancelled after error on photo {idx}/{total}") + if update_progress: + try: + update_progress( + idx, + total, + "Cancelled by user - finished current photo", + total_faces_detected, + total_faces_stored, + ) + except KeyboardInterrupt: + pass + break return photos_processed, total_faces_detected, total_faces_stored