fix: photos folder_id filter accepts source root ids too
GET /folders returns SourceRoot rows (the top-level scan paths shown in the LeftSidebar tree), but photos.folder_id points to a Folder row (a directory under a source root), and the photos list endpoint was matching Photo.folder_id == folder_id literally. Result: clicking "MulitaTest" in the sidebar sent the source root id, which never matched any photo, so the timeline went empty even though the photo_count badge showed 5. Fix: when the folder_id param matches a SourceRoot, expand it to every child Folder.id under that root and use IN. Falls back to the literal match for actual folder ids. If a source root has no child folder rows yet, returns no photos (rather than the whole library) so a half-scanned root doesn't accidentally show everything. The longer-term cleanup is to deduplicate the source_root / folder rows the scanner is creating on each rescan, but this makes the navigation work today. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -16,6 +16,7 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
from app.database import get_db
|
from app.database import get_db
|
||||||
from app.models import Photo, Folder, Tag, PhotoTag
|
from app.models import Photo, Folder, Tag, PhotoTag
|
||||||
|
from app.models.folders import SourceRoot
|
||||||
from app.models.heaps import heap_photos
|
from app.models.heaps import heap_photos
|
||||||
from app.schemas.photos import PhotoResponse, PhotoUpdate, PhotoListResponse, BulkAction
|
from app.schemas.photos import PhotoResponse, PhotoUpdate, PhotoListResponse, BulkAction
|
||||||
from app.config import settings
|
from app.config import settings
|
||||||
@@ -67,9 +68,27 @@ async def list_photos(
|
|||||||
if date_to:
|
if date_to:
|
||||||
filters.append(Photo.taken_at <= 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:
|
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
|
# Media type filter
|
||||||
if media_type:
|
if media_type:
|
||||||
|
|||||||
Reference in New Issue
Block a user