diff --git a/backend/app/tasks/scan.py b/backend/app/tasks/scan.py index 71c1760..97efa54 100644 --- a/backend/app/tasks/scan.py +++ b/backend/app/tasks/scan.py @@ -11,7 +11,7 @@ import json from typing import List, Dict, Optional from celery import shared_task -from sqlalchemy import select +from sqlalchemy import select, func from sqlalchemy.ext.asyncio import AsyncSession import aiofiles import redis @@ -172,10 +172,21 @@ async def _scan_folder_async(folder_path: str, source_root_id: Optional[str], ta # Calculate file hash for duplicate detection file_hash = await calculate_file_hash(filepath) - # Check for duplicate by hash - duplicate = await session.execute( - select(Photo).where(Photo.file_hash == file_hash) - ) if file_hash else None + # Check for duplicate by hash. We only care + # whether *any* other photo shares this hash, so + # use a count rather than scalar_one_or_none() + # which raises "Multiple rows were found" the + # moment the library has 2+ copies of the same + # file (i.e. exactly the case we're trying to + # flag). + is_dup = False + if file_hash: + dup_count = (await session.execute( + select(func.count(Photo.id)).where( + Photo.file_hash == file_hash + ) + )).scalar() or 0 + is_dup = dup_count > 0 # Create photo entry photo = Photo( @@ -188,7 +199,7 @@ async def _scan_folder_async(folder_path: str, source_root_id: Optional[str], ta file_size=stat.st_size, taken_at=datetime.fromtimestamp(stat.st_mtime), taken_at_source='filesystem', - is_duplicate=bool(duplicate.scalar_one_or_none() if duplicate else False), + is_duplicate=is_dup, processing_status='pending' )