diff --git a/backend/app/routers/photos.py b/backend/app/routers/photos.py index 8e4b464..2784df1 100644 --- a/backend/app/routers/photos.py +++ b/backend/app/routers/photos.py @@ -16,6 +16,7 @@ logger = logging.getLogger(__name__) from app.database import get_db from app.models import Photo, Folder, Tag, PhotoTag +from app.models.folders import SourceRoot from app.models.heaps import heap_photos from app.schemas.photos import PhotoResponse, PhotoUpdate, PhotoListResponse, BulkAction from app.config import settings @@ -67,9 +68,27 @@ async def list_photos( if date_to: filters.append(Photo.taken_at <= date_to) - # Folder filter + # Folder filter — the sidebar exposes "source roots" (top-level scan + # paths) under the same UI affordance as folders, so the same param has + # to accept either a folder id or a source root id. If the value matches + # a source root, expand to every folder under that root and use IN. if folder_id: - filters.append(Photo.folder_id == folder_id) + sr_check = await db.execute( + select(SourceRoot.id).where(SourceRoot.id == folder_id) + ) + if sr_check.scalar_one_or_none() is not None: + child_folders = await db.execute( + select(Folder.id).where(Folder.source_root_id == folder_id) + ) + child_ids = [row[0] for row in child_folders.all()] + if child_ids: + filters.append(Photo.folder_id.in_(child_ids)) + else: + # Source root with no folder rows yet — match nothing rather + # than returning the entire library. + filters.append(Photo.id == '__no_match__') + else: + filters.append(Photo.folder_id == folder_id) # Media type filter if media_type: